Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 68 additions & 54 deletions src/core/thread.d
Original file line number Diff line number Diff line change
Expand Up @@ -385,15 +385,10 @@ else version( Posix )
// NOTE: Since registers are being pushed and popped from the
// stack, any other stack data used by this function should
// be gone before the stack cleanup code is called below.
Thread obj = Thread.getThis();

// NOTE: The thread reference returned by getThis is set within
// the thread startup code, so it is possible that this
// handler may be called before the reference is set. In
// this case it is safe to simply suspend and not worry
// about the stack pointers as the thread will not have
// any references to GC-managed data.
if( obj && !obj.m_lock )
Thread obj = Thread.getThis();
assert(obj !is null);

if( !obj.m_lock )
{
obj.m_curr.tstack = getStackTop();
}
Expand All @@ -407,13 +402,13 @@ else version( Posix )
status = sigdelset( &sigres, resumeSignalNumber );
assert( status == 0 );

version (FreeBSD) Thread.sm_suspendagain = false;
version (FreeBSD) obj.m_suspendagain = false;
status = sem_post( &suspendCount );
assert( status == 0 );

sigsuspend( &sigres );

if( obj && !obj.m_lock )
if( !obj.m_lock )
{
obj.m_curr.tstack = obj.m_curr.bstack;
}
Expand All @@ -422,9 +417,10 @@ else version( Posix )
// avoid deadlocks on FreeBSD, see Issue 13416
version (FreeBSD)
{
if (THR_IN_CRITICAL(pthread_self()))
auto obj = Thread.getThis();
if (THR_IN_CRITICAL(obj.m_addr))
{
Thread.sm_suspendagain = true;
obj.m_suspendagain = true;
if (sem_post(&suspendCount)) assert(0);
return;
}
Expand Down Expand Up @@ -1400,7 +1396,7 @@ private:
version (FreeBSD)
{
// set when suspend failed and should be retried, see Issue 13416
static shared bool sm_suspendagain;
shared bool m_suspendagain;
}


Expand Down Expand Up @@ -2381,17 +2377,35 @@ private __gshared uint suspendDepth = 0;
*
* Throws:
* ThreadError if the suspend operation fails for a running thread.
* Returns:
* Whether the thread is now suspended (true) or terminated (false).
*/
private void suspend( Thread t ) nothrow
private bool suspend( Thread t ) nothrow
{
Duration waittime = dur!"usecs"(10);
Lagain:
if (!t.isRunning)
{
Thread.remove(t);
return false;
}
else if (t.m_isInCriticalRegion)
{
Thread.criticalRegionLock.unlock_nothrow();
Thread.sleep(waittime);
if (waittime < dur!"msecs"(10)) waittime *= 2;
Thread.criticalRegionLock.lock_nothrow();
goto Lagain;
}

version( Windows )
{
if( t.m_addr != GetCurrentThreadId() && SuspendThread( t.m_hndl ) == 0xFFFFFFFF )
{
if( !t.isRunning )
{
Thread.remove( t );
return;
return false;
}
onThreadError( "Unable to suspend thread" );
}
Expand Down Expand Up @@ -2450,7 +2464,7 @@ private void suspend( Thread t ) nothrow
if( !t.isRunning )
{
Thread.remove( t );
return;
return false;
}
onThreadError( "Unable to suspend thread" );
}
Expand Down Expand Up @@ -2511,33 +2525,22 @@ private void suspend( Thread t ) nothrow
{
if( t.m_addr != pthread_self() )
{
Lagain:
if( pthread_kill( t.m_addr, suspendSignalNumber ) != 0 )
{
if( !t.isRunning )
{
Thread.remove( t );
return;
return false;
}
onThreadError( "Unable to suspend thread" );
}
while (sem_wait(&suspendCount) != 0)
{
if (errno != EINTR)
onThreadError( "Unable to wait for semaphore" );
errno = 0;
}
version (FreeBSD)
{
// avoid deadlocks, see Issue 13416
if (Thread.sm_suspendagain) goto Lagain;
}
}
else if( !t.m_lock )
{
t.m_curr.tstack = getStackTop();
}
}
return true;
}

/**
Expand Down Expand Up @@ -2577,40 +2580,51 @@ extern (C) void thread_suspendAll() nothrow
if( ++suspendDepth > 1 )
return;

// NOTE: I'd really prefer not to check isRunning within this loop but
// not doing so could be problematic if threads are terminated
// abnormally and a new thread is created with the same thread
// address before the next GC run. This situation might cause
// the same thread to be suspended twice, which would likely
// cause the second suspend to fail, the garbage collection to
// abort, and Bad Things to occur.

Thread.criticalRegionLock.lock_nothrow();
scope (exit) Thread.criticalRegionLock.unlock_nothrow();
size_t cnt;
auto t = Thread.sm_tbeg;
while (t)
{
auto tn = t.next;
Duration waittime = dur!"usecs"(10);
if (suspend(t))
++cnt;
t = tn;
}

version (OSX)
{}
else version (Posix)
{
// subtract own thread
assert(cnt >= 1);
--cnt;
Lagain:
if (!t.isRunning)
// wait for semaphore notifications
for (; cnt; --cnt)
{
Thread.remove(t);
}
else if (t.m_isInCriticalRegion)
{
Thread.criticalRegionLock.unlock_nothrow();
Thread.sleep(waittime);
if (waittime < dur!"msecs"(10)) waittime *= 2;
Thread.criticalRegionLock.lock_nothrow();
goto Lagain;
while (sem_wait(&suspendCount) != 0)
{
if (errno != EINTR)
onThreadError("Unable to wait for semaphore");
errno = 0;
}
}
else
version (FreeBSD)
{
suspend(t);
}
t = tn;
// avoid deadlocks, see Issue 13416
t = Thread.sm_tbeg;
while (t)
{
auto tn = t.next;
if (t.m_suspendagain && suspend(t))
++cnt;
t = tn;
}
if (cnt)
goto Lagain;
}
}
Thread.criticalRegionLock.unlock_nothrow();
}
}

Expand Down