ZoneCollectionChecking.cpp [plain text]
#include "auto_zone.h"
#include "Zone.h"
#include "BlockIterator.h"
#ifdef __BLOCKS__
#include <Block.h>
#endif
using namespace Auto;
void Zone::enable_collection_checking() {
OSAtomicIncrement32(&_collection_checking_enabled);
}
void Zone::disable_collection_checking() {
uint32_t old_count;
do {
old_count = _collection_checking_enabled;
} while (old_count > 0 && !OSAtomicCompareAndSwap32(old_count, old_count-1, &_collection_checking_enabled));
if (old_count == 1) {
for (Region *region = region_list(); region != NULL; region = region->next()) {
SubzoneRangeIterator iterator(region->subzone_range());
Subzone *sz;
for (sz = iterator.next(); sz != NULL; sz = iterator.next()) {
sz->reset_collection_checking();
}
}
SpinLock lock(&_large_lock);
Large *large = _large_list;
while (large) {
large->set_collection_checking_count(0);
large = large->next();
}
}
}
void Zone::track_pointer(void *pointer) {
assert(collection_checking_enabled());
if (in_subzone_memory(pointer)) {
Subzone *sz = Subzone::subzone(pointer);
usword_t q;
if (sz->block_is_start(pointer, &q)) {
if (sz->collection_checking_count(q) == 0) {
sz->set_collection_checking_count(q, 1);
}
}
} else {
Large *large = block_start_large(pointer);
if (large && large->collection_checking_count() == 0) {
large->set_collection_checking_count(1);
}
}
}
void Zone::clear_garbage_checking_count(void **garbage, size_t count) {
for (size_t i=0; i<count; i++) {
void *block = garbage[i];
if (in_subzone_memory(block)) {
Subzone *subzone = Subzone::subzone(block);
usword_t q = subzone->quantum_index_unchecked(block);
if (subzone->collection_checking_count(q) > 0) {
subzone->set_collection_checking_count(q, 0);
}
} else {
Large *l = Large::large(block);
l->set_collection_checking_count(0);
}
}
}
class update_checking_count_visitor {
public:
inline bool visit(Zone *zone, Subzone *subzone, usword_t q) {
uint32_t count = subzone->collection_checking_count(q);
if (count > 0) {
subzone->set_collection_checking_count(q, count+1);
}
return true;
}
inline bool visit(Zone *zone, Large *large) {
uint32_t count = large->collection_checking_count();
if (count > 0) {
large->set_collection_checking_count(count+1);
}
return true;
}
};
void Zone::increment_check_counts() {
update_checking_count_visitor visitor;
visitAllocatedBlocks(this, visitor);
}
class report_uncollected_blocks_visitor {
auto_zone_collection_checking_callback_t _callback;
Thread &_thread;
public:
report_uncollected_blocks_visitor(Zone *zone, auto_zone_collection_checking_callback_t callback) : _callback(callback), _thread(zone->registered_thread()) {
}
~report_uncollected_blocks_visitor() {
}
void report_uncollected_block(Zone *zone, void *block, int32_t count) {
auto_memory_type_t layout = zone->block_layout(block);
if (!_callback) {
char *name;
bool free_name = false;
if ((layout & AUTO_OBJECT) == AUTO_OBJECT) {
if (zone->control.name_for_address) {
name = zone->control.name_for_address((auto_zone_t *)this, (vm_address_t)block, 0);
free_name = true;
} else {
name = (char *)"object";
}
} else {
name = (char *)"non-object block";
}
malloc_printf("%s %p was not collected after %d full collections\n", name, block, count-1);
if (free_name) free(name);
} else {
auto_zone_collection_checking_info info;
info.is_object = (layout & AUTO_OBJECT) == AUTO_OBJECT;
info.survived_count = count-1;
_callback(block, &info);
}
}
inline bool visit(Zone *zone, Subzone *subzone, usword_t q) {
uint32_t count = subzone->collection_checking_count(q);
if (count > 0) {
report_uncollected_block(zone, subzone->quantum_address(q), count);
}
return true;
}
inline bool visit(Zone *zone, Large *large) {
uint32_t count = large->collection_checking_count();
if (count > 0) {
report_uncollected_block(zone, large->address(), count);
}
return true;
}
};
void Zone::enumerate_uncollected(auto_zone_collection_checking_callback_t callback) {
dispatch_sync(_collection_queue, ^{
report_uncollected_blocks_visitor visitor(this, callback);
visitAllocatedBlocks(this, visitor);
});
}