@@ -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
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)
56285631else
56295632version (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