Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit fbc76f7

Browse files
authored
Merge pull request #2519 from rainers/ll_threads
add lowlevel thread support merged-on-behalf-of: Nicholas Wilson <[email protected]>
2 parents 30a2b2a + f94df07 commit fbc76f7

File tree

2 files changed

+193
-5
lines changed

2 files changed

+193
-5
lines changed

src/core/sys/windows/dll.d

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ bool dll_process_attach( HINSTANCE hInstance, bool attach_threads,
378378
// attach to all other threads
379379
return enumProcessThreads(
380380
function (uint id, void* context) {
381-
if ( !thread_findByAddr( id ) )
381+
if ( !thread_findByAddr( id ) && !findLowLevelThread( id ) )
382382
{
383383
// if the OS has not prepared TLS for us, don't attach to the thread
384384
if ( GetTlsDataAddress( id ) )
@@ -435,9 +435,10 @@ bool dll_thread_attach( bool attach_thread = true, bool initTls = true )
435435
{
436436
// if the OS has not prepared TLS for us, don't attach to the thread
437437
// (happened when running under x64 OS)
438-
if ( !GetTlsDataAddress( GetCurrentThreadId() ) )
438+
auto tid = GetCurrentThreadId();
439+
if ( !GetTlsDataAddress( tid ) )
439440
return false;
440-
if ( !thread_findByAddr( GetCurrentThreadId() ) )
441+
if ( !thread_findByAddr( tid ) && !findLowLevelThread( tid ) )
441442
{
442443
// only attach to thread and initalize it if it is not in the thread list (so it's not created by "new Thread")
443444
if ( attach_thread )

src/core/thread.d

Lines changed: 189 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ version (Windows)
196196
import core.sys.windows.winnt /+: CONTEXT, CONTEXT_CONTROL, CONTEXT_INTEGER+/;
197197

198198
extern (Windows) alias btex_fptr = uint function(void*);
199-
extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*) nothrow;
199+
extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*) nothrow @nogc;
200200

201201
//
202202
// Entry point for Windows threads
@@ -2020,7 +2020,9 @@ extern (C) void thread_init() @nogc
20202020
// exist to be scanned at this point, it is sufficient for these
20212021
// functions to detect the condition and return immediately.
20222022

2023+
initLowlevelThreads();
20232024
Thread.initLocks();
2025+
20242026
// The Android VM runtime intercepts SIGUSR1 and apparently doesn't allow
20252027
// its signal handler to run, so swap the two signals on Android, since
20262028
// thread_resumeHandler does nothing.
@@ -2116,6 +2118,7 @@ extern (C) void thread_term() @nogc
21162118
Thread.pAboutToStart = null;
21172119
}
21182120
Thread.termLocks();
2121+
termLowlevelThreads();
21192122
}
21202123

21212124

@@ -3070,7 +3073,7 @@ do
30703073
* Throws:
30713074
* ThreadError.
30723075
*/
3073-
private void onThreadError(string msg) nothrow
3076+
private void onThreadError(string msg) nothrow @nogc
30743077
{
30753078
__gshared ThreadError error = new ThreadError(null);
30763079
error.msg = msg;
@@ -5628,3 +5631,187 @@ version (Windows)
56285631
else
56295632
version (Posix)
56305633
alias ThreadID = pthread_t;
5634+
5635+
///////////////////////////////////////////////////////////////////////////////
5636+
// lowlovel threading support
5637+
private
5638+
{
5639+
__gshared size_t ll_nThreads;
5640+
__gshared ThreadID* ll_pThreads;
5641+
5642+
__gshared align(Mutex.alignof) void[__traits(classInstanceSize, Mutex)] ll_lock;
5643+
5644+
@property Mutex lowlevelLock() nothrow @nogc
5645+
{
5646+
return cast(Mutex)ll_lock.ptr;
5647+
}
5648+
5649+
void initLowlevelThreads() @nogc
5650+
{
5651+
ll_lock[] = typeid(Mutex).initializer[];
5652+
lowlevelLock.__ctor();
5653+
}
5654+
5655+
void termLowlevelThreads() @nogc
5656+
{
5657+
lowlevelLock.__dtor();
5658+
}
5659+
5660+
void ll_removeThread(ThreadID tid) nothrow @nogc
5661+
{
5662+
lowlevelLock.lock_nothrow();
5663+
scope(exit) lowlevelLock.unlock_nothrow();
5664+
5665+
foreach (i; 0 .. ll_nThreads)
5666+
{
5667+
if (tid is ll_pThreads[i])
5668+
{
5669+
import core.stdc.string : memmove;
5670+
memmove(ll_pThreads + i, ll_pThreads + i + 1, ThreadID.sizeof * (ll_nThreads - i - 1));
5671+
--ll_nThreads;
5672+
// no need to minimize, next add will do
5673+
break;
5674+
}
5675+
}
5676+
}
5677+
}
5678+
5679+
/**
5680+
* Create a thread not under control of the runtime, i.e. TLS module constructors are
5681+
* not run and the GC does not suspend it during a collection
5682+
*
5683+
* Params:
5684+
* dg = delegate to execute in the created thread
5685+
* stacksize = size of the stack of the created thread. The default of 0 will select the
5686+
* platform-specific default size
5687+
*
5688+
* Returns: the platform specific thread ID of the new thread. If an error occurs, a preallocated
5689+
* ThreadError is thrown.
5690+
*/
5691+
ThreadID createLowLevelThread(void delegate() nothrow dg, uint stacksize = 0) nothrow @nogc
5692+
{
5693+
void delegate() nothrow* context = cast(void delegate() nothrow*)malloc(dg.sizeof);
5694+
*context = dg;
5695+
5696+
ThreadID tid;
5697+
version (Windows)
5698+
{
5699+
static extern (Windows) uint thread_lowlevelEntry(void* ctx) nothrow
5700+
{
5701+
auto dg = *cast(void delegate() nothrow*)ctx;
5702+
free(ctx);
5703+
5704+
dg();
5705+
ll_removeThread(GetCurrentThreadId());
5706+
return 0;
5707+
}
5708+
5709+
// see Thread.start() for why thread is created in suspended state
5710+
HANDLE hThread = cast(HANDLE) _beginthreadex(null, stacksize, &thread_lowlevelEntry,
5711+
context, CREATE_SUSPENDED, &tid);
5712+
if (!hThread)
5713+
onThreadError("Error creating thread");
5714+
}
5715+
5716+
lowlevelLock.lock_nothrow();
5717+
scope(exit) lowlevelLock.unlock_nothrow();
5718+
5719+
ll_nThreads++;
5720+
ll_pThreads = cast(ThreadID*)realloc(ll_pThreads, Thread.sizeof * ll_nThreads);
5721+
5722+
version (Windows)
5723+
{
5724+
ll_pThreads[ll_nThreads - 1] = tid;
5725+
if (ResumeThread(hThread) == -1)
5726+
onThreadError("Error resuming thread");
5727+
CloseHandle(hThread);
5728+
}
5729+
else version (Posix)
5730+
{
5731+
static extern (C) void* thread_lowlevelEntry(void* ctx) nothrow
5732+
{
5733+
auto dg = *cast(void delegate() nothrow*)ctx;
5734+
free(ctx);
5735+
5736+
dg();
5737+
ll_removeThread(pthread_self());
5738+
return null;
5739+
}
5740+
5741+
pthread_attr_t attr;
5742+
5743+
if (pthread_attr_init(&attr))
5744+
onThreadError("Error initializing thread attributes");
5745+
if (stacksize && pthread_attr_setstacksize(&attr, stacksize))
5746+
onThreadError("Error initializing thread stack size");
5747+
if (pthread_create(&tid, &attr, &thread_lowlevelEntry, context) != 0)
5748+
onThreadError("Error creating thread");
5749+
5750+
ll_pThreads[ll_nThreads - 1] = tid;
5751+
}
5752+
return tid;
5753+
}
5754+
5755+
/**
5756+
* Wait for a thread created with `createLowLevelThread` to terminate
5757+
*
5758+
* Params:
5759+
* tid = the thread ID returned by `createLowLevelThread`
5760+
*/
5761+
void joinLowLevelThread(ThreadID tid) nothrow @nogc
5762+
{
5763+
version (Windows)
5764+
{
5765+
HANDLE handle = OpenThreadHandle(tid);
5766+
if (!handle)
5767+
return;
5768+
WaitForSingleObject(handle, INFINITE);
5769+
CloseHandle(handle);
5770+
}
5771+
else version (Posix)
5772+
{
5773+
if (pthread_join(tid, null) != 0)
5774+
onThreadError("Unable to join thread");
5775+
}
5776+
}
5777+
5778+
/**
5779+
* Check whether a thread was created by `createLowLevelThread`
5780+
*
5781+
* Params:
5782+
* tid = the platform specific thread ID
5783+
*
5784+
* Returns: `true` if the thread was created by `createLowLevelThread` and is still running
5785+
*/
5786+
bool findLowLevelThread(ThreadID tid) nothrow @nogc
5787+
{
5788+
lowlevelLock.lock_nothrow();
5789+
scope(exit) lowlevelLock.unlock_nothrow();
5790+
5791+
foreach (i; 0 .. ll_nThreads)
5792+
if (tid is ll_pThreads[i])
5793+
return true;
5794+
return false;
5795+
}
5796+
5797+
nothrow @nogc unittest
5798+
{
5799+
struct TaskWithContect
5800+
{
5801+
shared int n = 0;
5802+
void run() nothrow
5803+
{
5804+
n.atomicOp!"+="(1);
5805+
}
5806+
}
5807+
TaskWithContect task;
5808+
5809+
ThreadID[8] tids;
5810+
for (int i = 0; i < tids.length; i++)
5811+
tids[i] = createLowLevelThread(&task.run);
5812+
5813+
for (int i = 0; i < tids.length; i++)
5814+
joinLowLevelThread(tids[i]);
5815+
5816+
assert(task.n == tids.length);
5817+
}

0 commit comments

Comments
 (0)