Skip to content
Permalink
Browse files
Introduce SleepPhase.
Thread semantics are ad hoc in Ruby so we make a best effort to mimic them
where possible. The behavior of process exit when one or more threads are
suspended in a sleep state is not well-defined. We assume that no sleeping
thread should block the process from exiting. We introduce a specific "sleep
phase" as one state a thread may be in. We then ignore sleeping threads when
executing a process exit.

Unfortunately, this is more complex than it appears on its face. While
executing a process exit, we are essentially racing any sleeping threads that
may wake up and attempt to access resources that are being destroyed (something
like one of those scenes from Inception with the buildings crumbling around the
participants). We cannot waking thread proceed once process exit has started,
so we permanently lock a mutex that every waking thread must acquire before
progressing.

Ultimately, these lock resources will need to be in the program's data segment,
so they are static memory, not dynamically allocated, as they are now.
  • Loading branch information
brixen committed Jan 24, 2016
1 parent 03f8943 commit 903aae172c8a399172b0e6c62246a90b90f5c31e
Showing with 56 additions and 7 deletions.
  1. +2 −2 vm/park.cpp
  2. +34 −5 vm/thread_nexus.cpp
  3. +5 −0 vm/thread_nexus.hpp
  4. +15 −0 vm/thread_phase.hpp
@@ -20,7 +20,7 @@ namespace rubinius {
Object* result = cNil;
while(!wake_) {
{
UnmanagedPhase unmanaged(state);
SleepPhase sleeping(state);

cond_.wait(mutex_);
}
@@ -50,7 +50,7 @@ namespace rubinius {

while(!wake_) {
{
UnmanagedPhase unmanaged(state);
SleepPhase sleeping(state);

utilities::thread::Code status = cond_.wait_until(mutex_, ts);
if(status == utilities::thread::cTimedOut) {
@@ -21,6 +21,10 @@ namespace rubinius {
return (vm->thread_phase() & cBlocking) == cBlocking;
}

bool ThreadNexus::sleeping_p(VM* vm) {
return (vm->thread_phase() & cSleeping) == cSleeping;
}

bool ThreadNexus::yielding_p(VM* vm) {
return (vm->thread_phase() & cYielding) == cYielding;
}
@@ -65,6 +69,7 @@ namespace rubinius {
stop_ = false;
threads_lock_.init();
lock_.init(true);
sleep_lock_.init();
wait_mutex_.init();
wait_condition_.init();

@@ -103,6 +108,8 @@ namespace rubinius {
return "cUnmanaged";
case ThreadNexus::cWaiting:
return "cWaiting";
case ThreadNexus::cSleeping:
return "cSleeping";
case ThreadNexus::cYielding:
return "cYielding";
case ThreadNexus::cBlocking:
@@ -221,9 +228,14 @@ namespace rubinius {
}

void ThreadNexus::waiting_lock(VM* vm) {
vm->set_thread_phase(ThreadNexus::cWaiting);
vm->set_thread_phase(cWaiting);
lock_.lock();
vm->set_thread_phase(ThreadNexus::cManaged);
vm->set_thread_phase(cManaged);
}

void ThreadNexus::sleep_lock(VM* vm) {
utilities::thread::Mutex::LockGuard guard(sleep_lock_);
vm->become_managed();
}

void ThreadNexus::lock(VM* vm) {
@@ -254,22 +266,39 @@ namespace rubinius {
}

void ThreadNexus::wait_till_alone(VM* vm) {
/* We block acquiring the sleep lock so that any waking thread that raced
* us to it will finish waking up. Any sleeping thread that wakes up after
* we get the lock will block until the process exits because once we
* acquire the sleep lock, we never unlock it.
*/
sleep_lock_.lock();

vm->set_thread_phase(cWaiting);

uint64_t ns = 0;

// TODO: add thread_stop signalling
while(true) {
bool blocking = false;

{
utilities::thread::SpinLock::LockGuard guard(threads_lock_);

if(threads_.size() == 1) {
if(threads_.front()->as_vm() == vm) {
return;
for(ThreadList::iterator i = threads_.begin();
i != threads_.end();
++i)
{
if(VM* other_vm = (*i)->as_vm()) {
if(vm == other_vm || sleeping_p(other_vm)) continue;

blocking = true;
break;
}
}
}

if(!blocking) return;

ns += delay();

detect_halt_deadlock(ns, vm);
@@ -23,6 +23,7 @@ namespace rubinius {
bool stop_;
utilities::thread::SpinLock threads_lock_;
utilities::thread::Mutex lock_;
utilities::thread::Mutex sleep_lock_;
utilities::thread::Mutex wait_mutex_;
utilities::thread::Condition wait_condition_;
ThreadList threads_;
@@ -35,13 +36,15 @@ namespace rubinius {
cBlocking = 0x04,
cUnmanaged = 0x81,
cWaiting = 0x82,
cSleeping = 0x84,
cYielding = 0x80
};

ThreadNexus()
: stop_(false)
, threads_lock_()
, lock_(true)
, sleep_lock_()
, wait_mutex_()
, wait_condition_()
, threads_()
@@ -77,6 +80,7 @@ namespace rubinius {

bool blocking_p(VM* vm);
void blocking(VM* vm);
bool sleeping_p(VM* vm);
bool yielding_p(VM* vm);
void yielding(VM* vm);
bool serialized_p(VM* vm);
@@ -96,6 +100,7 @@ namespace rubinius {
void lock(VM* vm);
void waiting_lock(VM* vm);
void managed_lock(VM* vm);
void sleep_lock(VM* vm);
bool stop_lock(VM* vm);

void wait_till_alone(VM* vm);
@@ -27,6 +27,21 @@ namespace rubinius {
}
};

class SleepPhase {
State* state_;

public:
SleepPhase(STATE)
: state_(state)
{
state->vm()->set_thread_phase(ThreadNexus::cSleeping);
}

~SleepPhase() {
state_->shared().thread_nexus()->sleep_lock(state_->vm());
}
};

class ManagedPhase {
State* state_;

0 comments on commit 903aae1

Please sign in to comment.