Skip to content

Commit

Permalink
Redo stopping world mechanism, using lock and conditions instead of
Browse files Browse the repository at this point in the history
sched_yield.
  • Loading branch information
schveiguy committed Dec 4, 2024
1 parent 507dc96 commit 76810fe
Showing 1 changed file with 73 additions and 29 deletions.
102 changes: 73 additions & 29 deletions sdlib/d/gc/thread.d
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ void initThread() {
struct ThreadState {
private:
import d.sync.atomic;
Atomic!uint startingThreadCount;
uint startingThreadCount;
enum uint WorldIsStoppingBit = 1 << 31;

import d.sync.mutex;
Expand All @@ -129,25 +129,24 @@ private:

Mutex stopTheWorldMutex;

shared Mutex createThreadMutex;

public:
/**
* Thread management.
*/
void enterThreadCreation() shared {
auto tc = startingThreadCount.fetchAdd(1);
import d.gc.tcache;
if ((tc & WorldIsStoppingBit) && !threadCache.stoppingTheWorld) {
// Another thread is stopping the world. Suspended
// threads can hold crucial pthread locks, we cannot
// start a thread now. Sync on the mutex.
stopTheWorldMutex.lock();
stopTheWorldMutex.unlock();
}
createThreadMutex.lock();
scope(exit) createThreadMutex.unlock();

(cast(ThreadState*) &this).enterThreadCreationImpl();
}

void exitThreadCreation() shared {
auto s = startingThreadCount.fetchSub(1);
assert(s > 0, "enterThreadCreation was not called!");
createThreadMutex.lock();
scope(exit) createThreadMutex.unlock();

(cast(ThreadState*) &this).exitThreadCreationImpl();
}

void register(ThreadCache* tcache) shared {
Expand Down Expand Up @@ -192,43 +191,40 @@ public:

stopTheWorldMutex.lock();

prepareWorldStopping();

import d.gc.tcache;
threadCache.stoppingTheWorld = true;

uint count;
import sys.posix.sched;
uint tc = 0;
while (!startingThreadCount.casWeak(tc, WorldIsStoppingBit)) {
sched_yield();
assert(!(tc & WorldIsStoppingBit));
tc = 0; // Existing value must be 0
}

while (suspendRunningThreads(count++)) {
import sys.posix.sched;
sched_yield();
}
}

void restartTheWorld() shared {
import d.gc.tcache;
assert(threadCache.stoppingTheWorld);
threadCache.stoppingTheWorld = false;

// remove the WorldIsStopping bit from the starting thread count
auto tc = startingThreadCount.load();
while (true) {
assert(tc & WorldIsStoppingBit);
auto ntc = tc & ~WorldIsStoppingBit;
{
createThreadMutex.lock();
scope(exit) createThreadMutex.unlock();

if (startingThreadCount.casWeak(tc, ntc)) {
break;
}
auto ts = cast(ThreadState*) &this;

assert(ts.startingThreadCount & WorldIsStoppingBit);
ts.startingThreadCount -= WorldIsStoppingBit;
}

while (resumeSuspendedThreads()) {
import sys.posix.sched;
sched_yield();
}

import d.gc.tcache;
threadCache.stoppingTheWorld = false;

stopTheWorldMutex.unlock();

import d.gc.hooks;
Expand Down Expand Up @@ -257,6 +253,54 @@ private:
registeredThreads.insert(tcache);
}

void enterThreadCreationImpl() {
assert(createThreadMutex.isHeld(), "Mutex not held!");

// Wait for the world to not be stopping by another thread.
import d.gc.tcache;
if (!threadCache.stoppingTheWorld) {
createThreadMutex.waitFor(canCreateThreads);
assert(!(startingThreadCount & WorldIsStoppingBit));
}

++startingThreadCount;
}

bool canCreateThreads() {
return !(startingThreadCount & WorldIsStoppingBit);
}

void exitThreadCreationImpl() {
assert(createThreadMutex.isHeld(), "Mutex not held!");
assert((startingThreadCount & ~WorldIsStoppingBit) > 0,
"enterThreadCreation was not called!");

startingThreadCount--;
}

void prepareWorldStopping() shared {
createThreadMutex.lock();
scope(exit) createThreadMutex.unlock();

(cast(ThreadState*) &this).prepareWorldStoppingImpl();
}

void prepareWorldStoppingImpl() {
assert(createThreadMutex.isHeld(), "Mutex not held!");
assert((startingThreadCount & WorldIsStoppingBit) == 0);

/**
* Wait for threads in the process of starting to finish
* starting, prevent any new threads from starting.
*/
startingThreadCount += WorldIsStoppingBit;
createThreadMutex.waitFor(canStopTheWorld);
}

bool canStopTheWorld() {
return startingThreadCount == WorldIsStoppingBit;
}

void removeImpl(ThreadCache* tcache) {
assert(mThreadList.isHeld(), "Mutex not held!");

Expand Down

0 comments on commit 76810fe

Please sign in to comment.