blob: 8fe9a60c8652d1864d06d7f48cdf58b91cca8ec1 [file] [log] [blame]
#include "vmkit/allocator.h"
#include "vmkit/thread.h"
#include "vmkit/vmkit.h"
#include "vmkit/config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/mman.h>
using namespace vmkit;
pthread_mutex_t ThreadAllocator::mutex;
uintptr_t ThreadAllocator::baseStack = 0;
std::vector<void*>* ThreadAllocator::spaces = 0;
std::vector<void*>* ThreadAllocator::freeThreads = 0;
void* BumpAllocator::operator new(size_t n) {
return (void*)((uintptr_t)map(bucketSize) + sizeof(BumpAllocatorNode));
}
BumpAllocator::BumpAllocator() {
pthread_mutex_init(&mutex, 0);
current = (BumpAllocatorNode*)((uintptr_t)this - sizeof(BumpAllocatorNode));
current->next = 0;
current->top = (uint8_t*)round((uintptr_t)(this + 1), 64);
}
BumpAllocator* BumpAllocator::create() {
return new BumpAllocator();
}
BumpAllocator::~BumpAllocator() {
while(current->next) {
BumpAllocatorNode* tmp = current->next;
unmap(current, (uintptr_t)current->top - (uintptr_t)current);
current = tmp;
}
}
void BumpAllocator::operator delete(void* p) {
unmap(p, bucketSize);
}
void BumpAllocator::destroy(BumpAllocator* allocator) {
delete allocator;
}
void* BumpAllocator::allocate(size_t size) {
if(size > (bucketSize - sizeof(BumpAllocatorNode))) {
pthread_mutex_lock(&mutex);
size_t total = round((uintptr_t)size + sizeof(BumpAllocatorNode), 4096);
BumpAllocatorNode* newBucket = (BumpAllocatorNode*)map(total);
newBucket->next = current->next;
current->next = newBucket;
newBucket->top = (uint8_t*)((uintptr_t)newBucket + bucketSize);
pthread_mutex_unlock(&mutex);
return newBucket + 1;
}
while(1) {
BumpAllocatorNode* node = current;
uint8_t* res = __sync_fetch_and_add(&node->top, (uint8_t*)round(size, 8));
uint8_t* end = res + size;
if(res >= (uint8_t*)node && (res + size) < ((uint8_t*)node) + bucketSize)
return res;
pthread_mutex_lock(&mutex);
BumpAllocatorNode* newBucket = (BumpAllocatorNode*)map(bucketSize);
newBucket->next = current;
newBucket->top = (uint8_t*)(newBucket + 1);
current = newBucket;
pthread_mutex_unlock(&mutex);
}
}
void* BumpAllocator::map(size_t n) {
void* res = mmap(0, n, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, 0, 0);
if(res == MAP_FAILED)
Thread::get()->vm()->internalError("unable to map %ld bytes", n);
return res;
}
void BumpAllocator::unmap(void* p, size_t n) {
munmap(0, n);
}
void PermanentObject::operator delete(void* ptr) {
//Thread::get()->vm()->internalError("should not happen");
}
void PermanentObject::operator delete[](void* ptr) {
//Thread::get()->vm()->internalError("should not happen");
}
void ThreadAllocator::initialize(uintptr_t minThreadStruct) {
if(spaces)
VMKit::internalError("thread allocation system is already initialized");
spaces = new std::vector<void*>();
freeThreads = new std::vector<void*>();
pthread_mutex_init(&mutex, 0);
spaces->reserve(1);
freeThreads->reserve(refill);
minThreadStruct = ((minThreadStruct - 1) & -PAGE_SIZE) + PAGE_SIZE;
baseStack = minThreadStruct + PAGE_SIZE;
}
void* ThreadAllocator::allocate() {
pthread_mutex_lock(&mutex);
if(!freeThreads->size()) {
void* space = mmap(0, VMKIT_STACK_SIZE*refill, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if(space == MAP_FAILED) {
fprintf(stderr, "unable to allocate a thread\n");
abort();
}
spaces->push_back(space);
uintptr_t base = (((uintptr_t)space - 1) & -VMKIT_STACK_SIZE) + VMKIT_STACK_SIZE;
uint32_t n = (base == (uintptr_t)space) ? refill : (refill - 1);
for(uint32_t i=0; i<n; i++) {
mprotect((void*)(base + baseStack), PROT_NONE, PAGE_SIZE);
freeThreads->push_back((void*)base);
base += VMKIT_STACK_SIZE;
}
}
void* res = freeThreads->back();
freeThreads->pop_back();
pthread_mutex_unlock(&mutex);
return res;
}
void ThreadAllocator::release(void* thread) {
pthread_mutex_lock(&mutex);
freeThreads->push_back(thread);
pthread_mutex_unlock(&mutex);
}
void* ThreadAllocator::stackAddr(void* thread) {
return (void*)((uintptr_t)thread + baseStack);
}
size_t ThreadAllocator::stackSize(void* thread) {
return VMKIT_STACK_SIZE - baseStack;
}
void* ThreadAllocator::alternateStackAddr(void* thread) {
return (void*)((uintptr_t)thread + baseStack - PAGE_SIZE);
}
size_t ThreadAllocator::alternateStackSize(void* thread) {
return PAGE_SIZE;
}