//===-------------- gcthread.cc - Mvm Garbage Collector -------------------===//
//
//                              Mvm
//
// 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 GCThread::waitStacks() {
	_stackLock.lock();
	while(_nb_collected < _nb_threads)
		_stackCond.wait(&_stackLock);
	_stackLock.unlock();
}

void GCThread::synchronize() {
	
  if (cooperative) {
    mvm::Thread* self = mvm::Thread::get();
    assert(self && "No thread local data for this thread");
	  current_collector = self;
	  _nb_collected = 0;
 	 
    // Lock stacks. Changes on the doYield flag of threads must be
    // protected, as threads may wake up after being blocked in native code
    // and join the collection.
    stackLock();

    mvm::Thread* cur = self;
    do {
      cur->stackScanned = false;
      cur->doYield = true;
      cur = (mvm::Thread*)cur->next();
    } while (cur != self);
    
    // Unlock now. Each running thread will scan its stack.
    stackUnlock();

    // Scan the stacks of currently blocked threads.
 	  for (mvm::Thread* cur = (mvm::Thread*)self->next(); cur != self; 
         cur = (mvm::Thread*)cur->next()) {
      void* val = cur->getLastSP();
      // If val is null, this means that the thread woke up, and is
      // joining the collection. We are sure the thread will scan its stack.
      if (val) Collector::traceForeignThreadStack(cur);
	  }

    // Finally, scan my stack too!
	  Collector::siggc_handler(0);

    // And wait for other threads to finish.
    waitStacks();
  } else {
    int signo = Collector::siggc();
    mvm::Thread* self = mvm::Thread::get();
    self->stackScanned = false;
    assert(self && "No thread local data for this thread");
	  current_collector = self;
	  _nb_collected = 0;

 	  for (mvm::Thread* cur = (mvm::Thread*)self->next(); cur != self; 
         cur = (mvm::Thread*)cur->next()) {
      cur->stackScanned = false;
      cur->kill(signo);
	  }

	  Collector::siggc_handler(signo);
	
    waitStacks();
  }
}
