Index: std/thread.d =================================================================== --- std/thread.d (revision 1439) +++ std/thread.d (working copy) @@ -139,28 +139,14 @@ if (state != TS.INITIAL) error("already started"); - for (int i = 0; 1; i++) - { - if (i == allThreads.length) - error("too many threads"); - if (!allThreads[i]) - { allThreads[i] = this; - idx = i; - if (i>= allThreadsDim) - allThreadsDim = i + 1; - break; - } - } - nthreads++; + add_thread(); state = TS.RUNNING; hdl = _beginthreadex(null, cast(uint)stacksize, &threadstart, cast(void*)this, 0, &id); if (hdl == cast(thread_hdl)0) { - allThreads[idx] = null; - nthreads--; + del_thread(); state = TS.FINISHED; - idx = -1; error("failed to start"); } } @@ -299,13 +285,10 @@ } /** - * Returns a reference to the Thread for the thread that called the - * function. + * Returns a reference to the Thread with the given id */ - static Thread getThis() + static Thread _getThreadById(thread_id id) { - //printf("getThis(), allThreadsDim = %d\n", allThreadsDim); - thread_id id = GetCurrentThreadId(); for (int i = 0; i < allThreadsDim; i++) { Thread t = allThreads[i]; @@ -314,6 +297,24 @@ return t; } } + return null; + } + /** + * Returns a reference to the Thread for the thread that called the + * function. + */ + static Thread _getThis() + { + //printf("getThis(), allThreadsDim = %d\n", allThreadsDim); + thread_id id = GetCurrentThreadId(); + return _getThreadById(id); + } + + static Thread getThis() + { + Thread t = _getThis(); + if(t) + return t; printf("didn't find it\n"); assert(0); } @@ -397,7 +398,7 @@ /** * */ - static uint nthreads = 1; + static uint nthreads = 0; private: @@ -444,34 +445,124 @@ debug (thread) printf("Ending thread %d\n", t.idx); synchronized (Thread.classinfo) { - allThreads[t.idx] = null; - nthreads--; + t.del_thread(); t.state = TS.TERMINATED; - t.idx = -1; } return result; } + /************************************** + * Add thread to array of all threads and set index into array + * call with synchronized (Thread.classinfo) + */ + void add_thread() + { + // there might be threads that already exist when attaching all existing threads, + // but still call DllMain with DLL_THREAD_ATTACH + if(_getThreadById(id)) + return; + + for (int i = 0; 1; i++) + { + if (i == allThreads.length) + error("too many threads"); + if (!allThreads[i]) + { allThreads[i] = this; + idx = i; + if (i>= allThreadsDim) + allThreadsDim = i + 1; + break; + } + } + nthreads++; + } + /************************************** + * Delete thread from array of all threads + * call with synchronized (Thread.classinfo) + */ + + void del_thread() + { + assert(allThreads[idx] == this); + + allThreads[idx] = null; + nthreads--; + idx = -1; + + while(allThreadsDim> 0 && !allThreads[allThreadsDim - 1]) + allThreadsDim--; + } + + /************************************** * Create a Thread for global main(). */ public static void thread_init() { - Thread t = new Thread(); + assert(!allThreads[0]); + thread_attach(); + } + /************************************** + * Attach to current thread + */ + + public static void thread_attach() + { + thread_attach(GetCurrentThreadId(), Thread.getCurrentThreadHandle(), os_query_stackBottom()); + } + + public static void thread_attach(thread_id id, thread_hdl hdl, void* bottom) + { + // the gc should not be touched before attaching to the thread, because + // it might interfere with threads that use the GC, but won't suspend + // this thread. So we use a Thread object created by an attached thread. + // This is not necessary for the main (first) thread, because there is no + // concurrent thread. + Thread t; + if(nextThread) + { + t = nextThread; + nextThread = null; + } + else + { + t = new Thread(); + } + t.state = TS.RUNNING; - t.id = GetCurrentThreadId(); - t.hdl = Thread.getCurrentThreadHandle(); - t.stackBottom = os_query_stackBottom(); + t.id = id; + t.hdl = hdl; + t.stackBottom = bottom; - assert(!allThreads[0]); - allThreads[0] = t; - allThreadsDim = 1; - t.idx = 0; + synchronized (Thread.classinfo) + t.add_thread(); + + nextThread = new Thread(); } + static Thread nextThread; // preallocate thread to avoid allocation before attaching + + /************************************** + * Detach from current thread + */ + + public static void thread_detach() + { + if(Thread t = _getThis()) + synchronized (Thread.classinfo) + t.del_thread(); + } + + public static void thread_detach(thread_id id) + { + if(Thread t = _getThreadById(id)) + synchronized (Thread.classinfo) + t.del_thread(); + } + static ~this() { if (allThreadsDim) @@ -590,21 +681,7 @@ error("already started"); synchronized (Thread.classinfo) - { - for (int i = 0; 1; i++) - { - if (i == allThreads.length) - error("too many threads"); - if (!allThreads[i]) - { allThreads[i] = this; - idx = i; - if (i>= allThreadsDim) - allThreadsDim = i + 1; - break; - } - } - nthreads++; - } + idx = add_thread(); state = TS.RUNNING; //printf("creating thread x%x\n", this); @@ -884,7 +961,7 @@ sched_yield(); } - static uint nthreads = 1; + static uint nthreads = 0; private: Index: std/thread_helper.d =================================================================== --- std/thread_helper.d (revision 0) +++ std/thread_helper.d (revision 0) @@ -0,0 +1,165 @@ +/** + * This module provides OS specific helper function for threads support + */ + +module std.thread_helper; + +version( Windows ) +{ + import std.c.windows.windows; + import std.c.stdlib; + + public import std.thread; + + /////////////////////////////////////////////////////////////////// + + const SystemProcessInformation = 5; + const STATUS_INFO_LENGTH_MISMATCH = 0xc0000004; + + // abbreviated versions of these structs + struct _SYSTEM_PROCESS_INFORMATION + { + int NextEntryOffset; // When this entry is 0, there are no more processes to be read. + int NumberOfThreads; + int[15] fill1; + int ProcessId; + int[28] fill2; + + // SYSTEM_THREAD_INFORMATION or SYSTEM_EXTENDED_THREAD_INFORMATION structures follow. + } + + struct _SYSTEM_THREAD_INFORMATION + { + int[8] fill1; + int ProcessId; + int ThreadId; + int[6] fill2; + } + + alias extern(Windows) + HRESULT fnNtQuerySystemInformation( uint SystemInformationClass, void* info, uint infoLength, uint* ReturnLength ); + + const ThreadBasicInformation = 0; + + struct THREAD_BASIC_INFORMATION + { + int ExitStatus; + void** TebBaseAddress; + int ProcessId; + int ThreadId; + int AffinityMask; + int Priority; + int BasePriority; + } + + alias extern(Windows) + int fnNtQueryInformationThread( HANDLE ThreadHandle, uint ThreadInformationClass, void* buf, uint size, uint* ReturnLength ); + + /////////////////////////////////////////////////////////////////// + // support attaching to thread other than just executing + void** getTEB( HANDLE hnd ) + { + HANDLE nthnd = GetModuleHandleA( "NTDLL" ); + assert( nthnd, "cannot get module handle for ntdll" ); + fnNtQueryInformationThread* fn = cast(fnNtQueryInformationThread*) GetProcAddress( nthnd, "NtQueryInformationThread" ); + assert( fn, "cannot find NtQueryInformationThread in ntdll" ); + + THREAD_BASIC_INFORMATION tbi; + int Status = (*fn)(hnd, ThreadBasicInformation, &tbi, tbi.sizeof, null); + assert(Status == 0); + + return tbi.TebBaseAddress; + } + + extern(Windows) + HANDLE OpenThread(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId); + + const SYNCHRONIZE = 0x00100000; + const THREAD_GET_CONTEXT = 8; + const THREAD_QUERY_INFORMATION = 0x40; + const THREAD_SUSPEND_RESUME = 2; + + void** getTEB( uint id ) + { + HANDLE hnd = OpenThread( THREAD_QUERY_INFORMATION, FALSE, id ); + assert( hnd, "OpenThread failed" ); + + void** teb = getTEB( hnd ); + CloseHandle( hnd ); + return teb; + } + + void* getThreadStackBottom( HANDLE hnd ) + { + void** teb = getTEB( hnd ); + return teb[1]; + } + + void* getThreadStackBottom( uint id ) + { + void** teb = getTEB( id ); + return teb[1]; + } + + HANDLE OpenThreadHandle( uint id ) + { + return OpenThread( SYNCHRONIZE|THREAD_GET_CONTEXT|THREAD_QUERY_INFORMATION|THREAD_SUSPEND_RESUME, FALSE, id ); + } + + /////////////////////////////////////////////////////////////////// + // support attaching to all running threads + // using unction instead of delegate here to avoid allocating closure + bool enumProcessThreads( uint procid, bool function( uint id, void* context ) dg, void* context ) + { + HANDLE hnd = GetModuleHandleA( "NTDLL" ); + fnNtQuerySystemInformation* fn = cast(fnNtQuerySystemInformation*) GetProcAddress( hnd, "NtQuerySystemInformation" ); + if( !fn ) + return false; + + uint sz = 16384; + uint retLength; + HRESULT rc; + char* buf; + for( ; ; ) + { + buf = cast(char*) std.c.stdlib.malloc(sz); + if(!buf) + return false; + rc = (*fn)( SystemProcessInformation, buf, sz, &retLength ); + if( rc != STATUS_INFO_LENGTH_MISMATCH ) + break; + std.c.stdlib.free( buf ); + sz *= 2; + } + scope(exit) std.c.stdlib.free( buf ); + + if(rc != 0) + return false; + + auto pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) buf; + auto pend = cast(_SYSTEM_PROCESS_INFORMATION*) (buf + retLength); + for( ; pinfo < pend; ) + { + if( pinfo.ProcessId == procid ) + { + auto tinfo = cast(_SYSTEM_THREAD_INFORMATION*)(pinfo + 1); + for( int i = 0; i < pinfo.NumberOfThreads; i++, tinfo++ ) + if( tinfo.ProcessId == procid ) + if( !dg( tinfo.ThreadId, context ) ) + return false; + } + if( pinfo.NextEntryOffset == 0 ) + break; + pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) (cast(char*) pinfo + pinfo.NextEntryOffset); + } + return true; + } + + bool enumProcessThreads( bool function( uint id, void* context ) dg, void* context ) + { + return enumProcessThreads( GetCurrentProcessId(), dg, context ); + } + + +} + Index: win32.mak =================================================================== --- win32.mak (revision 1439) +++ win32.mak (working copy) @@ -76,7 +76,7 @@ std\bitarray.d \ std\signals.d std\typetuple.d std\traits.d std\bind.d \ std\switcherr.d \ - std\thread.d std\moduleinit.d std\boxer.d \ + std\thread.d std\thread_helper.d std\moduleinit.d std\boxer.d \ std\stream.d std\socket.d std\socketstream.d \ std\perf.d std\openrj.d std\conv.d \ std\zip.d std\cstream.d std\loader.d \