blob: 407ed204f8f786fbcbf5915fdd02078130b45f19 [file] [log] [blame]
//===-------- CollectionRV.cpp - Rendez-vous for garbage collection -------===//
//
// The VMKit project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <cassert>
#include "MvmGC.h"
using namespace mvm;
void CollectionRV::waitEndOfRV() {
mvm::Thread* th = mvm::Thread::get();
unsigned cm = rendezvousNb;
if (nbJoined == th->MyVM->NumberOfThreads) collectorGo();
while (rendezvousNb == cm) {
condEndRV.wait(&_lockRV);
}
}
void CollectionRV::waitRV() {
mvm::Thread* self = mvm::Thread::get();
// Add myself.
another_mark();
while (nbJoined < self->MyVM->NumberOfThreads)
condInitiator.wait(&_lockRV);
}
void CollectionRV::synchronize() {
mvm::Thread* self = mvm::Thread::get();
assert(self && "No thread local data for this thread");
self->inRV = true;
// Lock thread lock, so that we can traverse the thread list safely. This will
// be released on finishRV.
self->MyVM->ThreadLock.lock();
if (cooperative) {
mvm::Thread* cur = self;
do {
cur->joinedRV = false;
cur->doYield = true;
cur = (mvm::Thread*)cur->next();
} while (cur != self);
// Lookup currently blocked threads.
for (cur = (mvm::Thread*)self->next(); cur != self;
cur = (mvm::Thread*)cur->next()) {
if (cur->getLastSP()) {
another_mark();
cur->joinedRV = true;
}
}
} else {
mvm::Thread* self = mvm::Thread::get();
self->joinedRV = false;
assert(self && "No thread local data for this thread");
for (mvm::Thread* cur = (mvm::Thread*)self->next(); cur != self;
cur = (mvm::Thread*)cur->next()) {
cur->joinedRV = false;
cur->killForRendezvous();
}
}
// And wait for other threads to finish.
waitRV();
}
void CollectionRV::join() {
mvm::Thread* th = mvm::Thread::get();
th->inRV = true;
bool changed = false;
lockRV();
if (isCooperative() && !th->doYield) {
// I was previously blocked, and I'm running late: someone else collected
// my stack, and the GC has finished already. Just unlock and return.
unlockRV();
th->inRV = false;
return;
}
// I woke up while a GC was happening, and no-one has listed me yet.
if (!th->joinedRV) {
another_mark();
th->joinedRV = true;
}
// lastSP may not be set in two cases:
// (1) The thread was interrupted while executing regular code (ie cooperative
// code).
// (2) The thread left uncooperative code and has just cleared lastSP.
if (!th->getLastSP()) {
changed = true;
th->setLastSP(FRAME_PTR());
}
assert(th->getLastSP() && "Joined without giving a SP");
do {
// Wait for the rendezvous to finish.
waitEndOfRV();
// If we wake up here and doYield is set, this means that a new GC is
// happening, so join it.
} while (th->doYield);
if (changed) th->setLastSP(0);
// Unlock after modifying lastSP, because lastSP is also read by the
// rendezvous initiator.
unlockRV();
// The rendezvous is finished. Set inRV to false.
th->inRV = false;
}
extern "C" void conditionalSafePoint() {
mvm::Thread* th = mvm::Thread::get();
th->MyVM->rendezvous.join();
}
void CollectionRV::finishRV() {
mvm::Thread* self = mvm::Thread::get();
if (cooperative) {
mvm::Thread* initiator = mvm::Thread::get();
mvm::Thread* cur = initiator;
do {
cur->doYield = false;
cur = (mvm::Thread*)cur->next();
} while (cur != initiator);
}
nbJoined = 0;
rendezvousNb++;
condEndRV.broadcast();
self->inRV = false;
self->MyVM->ThreadLock.unlock();
unlockRV();
}