#include "AutoConfiguration.h"
#include "AutoDefs.h"
#include "AutoEnvironment.h"
#include "AutoRegion.h"
#include "AutoZone.h"
namespace Auto {
Region *Region::new_region(Zone *zone) {
usword_t allocation_size; void *allocation_address = NULL; unsigned nzones;
#if UseArena
nzones = 1 << (arena_size_log2 - subzone_quantum_log2 - 1);
allocation_size = managed_size(nzones);
allocation_address = zone->arena_allocate_region(allocation_size); #else
for (unsigned n = initial_subzone_count; n >= initial_subzone_min_count && !allocation_address; n--) {
allocation_size = managed_size(n);
allocation_address = allocate_memory(allocation_size, subzone_quantum, VM_MEMORY_MALLOC_SMALL);
nzones = n;
}
#endif
if (!allocation_address) {
error("Can not allocate new region");
return NULL;
}
Region *region = new Region(zone, allocation_address, allocation_size, nzones);
if (!region) {
error("Can not allocate new region");
zone->arena_deallocate(allocation_address, allocation_size);
}
return region;
}
Region::Region(Zone *zone, void *address, usword_t size, usword_t nsubzones) {
if (!this) return;
set_range(address, size);
_next = NULL;
_zone = zone;
_subzone_lock = 0;
unsigned long bytes_per_bitmap = nsubzones << subzone_bitmap_bytes_log2;
size -= bytes_per_bitmap;
zone->statistics().add_admin(bytes_per_bitmap);
_scan_space.set_range(displace(address, size), bytes_per_bitmap);
_pending.set_address(_scan_space.address());
_pending.set_size(0);
size -= bytes_per_bitmap;
zone->statistics().add_admin(bytes_per_bitmap);
_marks.set_address(displace(address, size));
_marks.set_size(0);
_i_subzones = 0;
_n_subzones = size >> subzone_quantum_log2;
if (_n_subzones != nsubzones) {
error("region: size inconsistent with number of subzones");
}
_n_quantum = 0;
_small_admin.initialize(zone, this, allocate_quantum_small_log2);
_medium_admin.initialize(zone, this, allocate_quantum_medium_log2);
add_subzone(&_small_admin);
add_subzone(&_medium_admin);
zone->statistics().add_admin(Region::bytes_needed()); zone->statistics().add_allocated(size);
zone->statistics().increment_regions_in_use();
}
Region::~Region() {
_zone->statistics().add_admin(-Region::bytes_needed());
}
void *Region::allocate(const size_t size, const unsigned layout, bool clear, bool refcount_is_one) {
Admin *admin = size < allocate_quantum_medium ? &_small_admin : &_medium_admin;
bool did_grow = false;
void *block = NULL;
while (!(block = admin->find_allocation(size, layout, refcount_is_one, did_grow))) {
if (next()) return NULL;
_zone->control.will_grow((auto_zone_t *)_zone, AUTO_HEAP_SUBZONE_EXHAUSTED);
if (!add_subzone(admin)) {
_zone->control.will_grow((auto_zone_t *)_zone, AUTO_HEAP_REGION_EXHAUSTED);
return NULL;
}
}
if (did_grow) _zone->control.will_grow((auto_zone_t *)_zone, AUTO_HEAP_HOLES_EXHAUSTED);
Subzone *subzone = Subzone::subzone(block);
usword_t allocated_size = subzone->size(block);
if (clear) {
void **end = (void **)displace(block, allocated_size);
switch (allocated_size/sizeof(void *)) {
case 12: end[-12] = NULL;
case 11: end[-11] = NULL;
case 10: end[-10] = NULL;
case 9: end[-9] = NULL;
case 8: end[-8] = NULL;
case 7: end[-7] = NULL;
case 6: end[-6] = NULL;
case 5: end[-5] = NULL;
case 4: end[-4] = NULL;
case 3: end[-3] = NULL;
case 2: end[-2] = NULL;
case 1: end[-1] = NULL;
case 0: break;
default:
bzero(block, allocated_size);
break;
}
}
else if (layout & AUTO_UNSCANNED) {
usword_t remainder_size = (allocated_size - size);
void **end = (void **)displace(block, allocated_size);
switch (remainder_size/sizeof(void *)) { case 3: end[-3] = NULL;
case 2: end[-2] = NULL;
case 1: end[-1] = NULL;
case 0: break;
default:
bzero(displace(block, size), remainder_size);
break;
}
}
_zone->statistics().add_count(1);
_zone->statistics().add_size(allocated_size);
_zone->add_allocated_bytes(allocated_size);
return block;
}
void Region::deallocate(Subzone *subzone, void *block) {
usword_t size = subzone->size(block);
_zone->statistics().add_count(-1);
_zone->statistics().add_size(-size);
Admin *admin = subzone->admin();
admin->deallocate(block);
}
bool Region::add_subzone(Admin *admin) {
SpinLock admin_lock(admin->lock());
if (admin->active_subzone()) return true;
Subzone *subzone = NULL;
{
SpinLock subzone_lock(&_subzone_lock);
if (_i_subzones == _n_subzones) return false;
subzone = new(subzone_address(_i_subzones++)) Subzone(admin, admin->quantum_log2(), _n_quantum);
_n_quantum += subzone->allocation_limit();
_pending.set_size(Bitmap::bytes_needed(_n_quantum));
_marks.set_size(Bitmap::bytes_needed(_n_quantum));
}
admin->set_active_subzone(subzone);
_zone->statistics().add_admin(subzone_write_barrier_max);
_zone->statistics().increment_subzones_in_use();
_zone->activate_subzone(subzone);
return true;
}
};