blob: 4bd8fd1c45aaba958fd8e64833f865a3da52eaa1 [file] [log] [blame]
#include "mvm/VMKit.h"
#include "mvm/VirtualMachine.h"
#include "mvm/SystemThreads.h"
#include "mvm/GC.h"
#include "mvm/JIT.h"
using namespace mvm;
#if 0
#define dprintf(...) do { printf("[%p] ", (void*)mvm::Thread::get()); printf(__VA_ARGS__); } while(0)
#else
#define dprintf(...)
#endif
static SpinLock initedLock;
static bool inited = false;
void VMKit::initialise(llvm::CodeGenOpt::Level level, llvm::Module* TheModule, llvm::TargetMachine* TheTarget) {
initedLock.lock();
if(!inited) {
inited = true;
mvm::MvmModule::initialise(level, TheModule, TheTarget);
mvm::Collector::initialise();
}
initedLock.unlock();
}
VMKit::VMKit(mvm::BumpPtrAllocator &Alloc) : allocator(Alloc) {
initialise();
vms = 0;
vmsArraySize = 0;
}
void VMKit::scanWeakReferencesQueue(uintptr_t closure) {
if(referenceThread)
referenceThread->WeakReferencesQueue.scan(referenceThread, closure);
}
void VMKit::scanSoftReferencesQueue(uintptr_t closure) {
if(referenceThread)
referenceThread->SoftReferencesQueue.scan(referenceThread, closure);
}
void VMKit::scanPhantomReferencesQueue(uintptr_t closure) {
if(referenceThread)
referenceThread->PhantomReferencesQueue.scan(referenceThread, closure);
}
void VMKit::scanFinalizationQueue(uintptr_t closure) {
if(finalizerThread)
finalizerThread->scanFinalizationQueue(closure);
}
FinalizerThread* VMKit::getAndAllocateFinalizerThread() {
if(!finalizerThread) {
vmkitLock();
if(!finalizerThread) {
finalizerThread = new FinalizerThread(this);
finalizerThread->start((void (*)(mvm::Thread*))FinalizerThread::finalizerStart);
}
vmkitUnlock();
}
return finalizerThread;
}
ReferenceThread* VMKit::getAndAllocateReferenceThread() {
if(!referenceThread) {
vmkitLock();
if(!referenceThread) {
referenceThread = new ReferenceThread(this);
referenceThread->start((void (*)(mvm::Thread*))ReferenceThread::enqueueStart);
}
vmkitUnlock();
}
return referenceThread;
}
void VMKit::addFinalizationCandidate(mvm::gc* object) {
llvm_gcroot(object, 0);
getAndAllocateFinalizerThread()->addFinalizationCandidate(object);
}
void VMKit::addWeakReference(mvm::gc* ref) {
llvm_gcroot(ref, 0);
getAndAllocateReferenceThread()->addWeakReference(ref);
}
void VMKit::addSoftReference(mvm::gc* ref) {
llvm_gcroot(ref, 0);
getAndAllocateReferenceThread()->addSoftReference(ref);
}
void VMKit::addPhantomReference(mvm::gc* ref) {
llvm_gcroot(ref, 0);
getAndAllocateReferenceThread()->addPhantomReference(ref);
}
void VMKit::tracer(uintptr_t closure) {
// don't have to take the vmkitLock, already taken by the rendezvous.
for(size_t i=0; i<vmsArraySize; i++)
if(vms[i])
vms[i]->tracer(closure);
}
bool VMKit::startCollection() {
// do not take the lock here because if a gc is currently running, it could call enterUncooperativeCode
// which will execute the gc and we will therefore recall the gc just behind. Stupid because the previous one
// should have freed some memory
rendezvous.startRV();
if (mvm::Thread::get()->doYield) {
rendezvous.cancelRV();
rendezvous.join();
return 0;
} else {
dprintf("Start collection\n");
// Lock thread lock, so that we can traverse the vm and thread lists safely. This will be released on finishRV.
vmkitLock();
if(finalizerThread)
finalizerThread->FinalizationQueueLock.acquire();
if(referenceThread) {
referenceThread->ToEnqueueLock.acquire();
referenceThread->SoftReferencesQueue.acquire();
referenceThread->WeakReferencesQueue.acquire();
referenceThread->PhantomReferencesQueue.acquire();
}
// call first startCollection on each vm to avoid deadlock.
// indeed, a vm could want to execute applicative code
for(size_t i=0; i<vmsArraySize; i++)
if(vms[i])
vms[i]->startCollection();
rendezvous.synchronize();
return 1;
}
}
void VMKit::endCollection() {
dprintf("End collection\n");
rendezvous.finishRV();
for(size_t i=0; i<vmsArraySize; i++)
if(vms[i])
vms[i]->endCollection();
if(finalizerThread) {
finalizerThread->FinalizationQueueLock.release();
finalizerThread->FinalizationCond.broadcast();
}
if(referenceThread) {
referenceThread->ToEnqueueLock.release();
referenceThread->SoftReferencesQueue.release();
referenceThread->WeakReferencesQueue.release();
referenceThread->PhantomReferencesQueue.release();
referenceThread->EnqueueCond.broadcast();
}
vmkitUnlock();
}
size_t VMKit::addVM(VirtualMachine* vm) {
dprintf("add vm: %p\n", vm);
vmkitLock();
for(size_t i=0; i<vmsArraySize; i++)
if(!vms[i]) {
vms[i] = vm;
vmkitUnlock();
return i;
}
int res = vmsArraySize;
vmsArraySize = vmsArraySize ? (vmsArraySize<<1) : 1;
// reallocate the vms
VirtualMachine **newVms = new VirtualMachine*[vmsArraySize];
memcpy(newVms, vms, res*sizeof(VirtualMachine*));
memset(newVms + res*sizeof(VirtualMachine*), 0, (vmsArraySize-res)*sizeof(VirtualMachine*));
newVms[res] = vm;
VirtualMachine **oldVms = vms;
vms = newVms; // vms must always contain valid data
delete[] oldVms;
// reallocate the allVMDatas
for(Thread* cur=preparedThreads.next(); cur!=&preparedThreads; cur=cur->next()) {
cur->reallocAllVmsData(res, vmsArraySize);
}
for(Thread* cur=runningThreads.next(); cur!=&runningThreads; cur=cur->next()) {
cur->reallocAllVmsData(res, vmsArraySize);
}
vmkitUnlock();
return res;
}
void VMKit::removeVM(size_t id) {
dprintf("remove vm: %p\n", vm);
// I think that we only should call this function when all the thread data are released
vms[id] = 0;
}
void VMKit::registerPreparedThread(mvm::Thread* th) {
dprintf("Create thread: %p\n", th);
vmkitLock();
th->appendTo(&preparedThreads);
th->reallocAllVmsData(0, vmsArraySize);
vmkitUnlock();
}
void VMKit::unregisterPreparedThread(mvm::Thread* th) {
dprintf("Delete thread: %p\n", th);
vmkitLock();
th->remove();
for(size_t i=0; i<vmsArraySize; i++)
if(th->allVmsData[i])
delete th->allVmsData[i];
delete th->allVmsData;
vmkitUnlock();
}
void VMKit::registerRunningThread(mvm::Thread* th) {
dprintf("Register thread: %p\n", th);
vmkitLock();
numberOfRunningThreads++;
th->remove();
th->appendTo(&runningThreads);
vmkitUnlock();
}
void VMKit::unregisterRunningThread(mvm::Thread* th) {
dprintf("Unregister thread: %p\n", th);
vmkitLock();
numberOfRunningThreads--;
th->remove();
th->appendTo(&preparedThreads);
vmkitUnlock();
}
void VMKit::waitNonDaemonThreads() {
nonDaemonThreadsManager.waitNonDaemonThreads();
}
void NonDaemonThreadManager::leaveNonDaemonMode() {
nonDaemonLock.lock();
--nonDaemonThreads;
if (nonDaemonThreads == 0) nonDaemonVar.signal();
nonDaemonLock.unlock();
}
void NonDaemonThreadManager::enterNonDaemonMode() {
nonDaemonLock.lock();
++nonDaemonThreads;
nonDaemonLock.unlock();
}
void NonDaemonThreadManager::waitNonDaemonThreads() {
nonDaemonLock.lock();
while (nonDaemonThreads) {
nonDaemonVar.wait(&nonDaemonLock);
}
nonDaemonLock.unlock();
}