The OpenD Programming Language

1 /**
2  * This module provides OS specific helper function for threads support
3  *
4  * Copyright: Copyright Digital Mars 2010 - 2010.
5  * License: Distributed under the
6  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7  *    (See accompanying file LICENSE)
8  * Source:    $(DRUNTIMESRC core/sys/windows/_threadaux.d)
9  * Authors:   Rainer Schuetze
10  */
11 
12 module core.sys.windows.threadaux;
13 version (Windows):
14 
15 import core.sys.windows.basetsd/+ : HANDLE+/;
16 import core.sys.windows.winbase/+ : CloseHandle, GetCurrentThreadId, GetCurrentProcessId,
17     GetModuleHandleA, GetProcAddress+/;
18 import core.sys.windows.windef/+ : BOOL, DWORD, FALSE, HRESULT+/;
19 import core.stdc.stdlib;
20 
21 public import core.thread;
22 
23 version (LDC) import ldc.attributes, ldc.llvmasm;
24 
25 extern(Windows)
26 HANDLE OpenThread(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId) nothrow @nogc;
27 
28 extern (C) extern __gshared int _tls_index;
29 
30 extern (C) // rt.minfo
31 {
32     void rt_moduleTlsCtor();
33     void rt_moduleTlsDtor();
34 }
35 
36 private:
37 ///////////////////////////////////////////////////////////////////
38 struct thread_aux
39 {
40     // don't let symbols leak into other modules
41 
42     enum SystemProcessInformation = 5;
43     enum STATUS_INFO_LENGTH_MISMATCH = 0xc0000004;
44 
45     // structs subject to change according to MSDN, more info at http://undocumented.ntinternals.net
46     // declarations according to http://processhacker.sourceforge.net/doc/ntexapi_8h_source.html
47     // NOTE: the declarations assume default alignment for Win64 and contain some padding data
48     struct UNICODE_STRING
49     {
50         short Length;
51         short MaximumLength;
52         wchar* Buffer;
53     }
54     // process or thread ID, documentation says it is a HANDLE, but it's actually the ID (a DWORD)
55     alias size_t PTID;
56 
57     struct _SYSTEM_PROCESS_INFORMATION
58     {
59         int     NextEntryOffset; // When this entry is 0, there are no more processes to be read.
60         int     NumberOfThreads;
61         long    WorkingSetPrivateSize;
62         uint    HardFaultCount;
63         uint    NumberOfThreadsHighWatermark;
64         ulong   CycleTime;
65         long    CreateTime;
66         long    UserTime;
67         long    KernelTime;
68         UNICODE_STRING      ImageName;
69         int     BasePriority;
70         PTID    /*Unique*/ProcessId;
71         PTID    InheritedFromUniqueProcessId;
72         uint    HandleCount;
73         uint    SessionId;
74         size_t  UniqueProcessKey;
75         size_t  PeakVirtualSize;
76         size_t  VirtualSize;
77         uint    PageFaultCount;
78         size_t  PeakWorkingSetSize;
79         size_t  WorkingSetSize;
80         size_t  QuotaPeakPagedPoolUsage;
81         size_t  QuotaPagedPoolUsage;
82         size_t  QuotaPeakNonPagedPoolUsage;
83         size_t  QuotaNonPagedPoolUsage;
84         size_t  PagefileUsage;
85         size_t  PeakPagefileUsage;
86         size_t  PrivatePageCount;
87         long    ReadOperationCount;
88         long    WriteOperationCount;
89         long    OtherOperationCount;
90         long    ReadTransferCount;
91         long    WriteTransferCount;
92         long    OtherTransferCount;
93 
94         // SYSTEM_THREAD_INFORMATION or SYSTEM_EXTENDED_THREAD_INFORMATION structures follow.
95     }
96 
97     struct _SYSTEM_THREAD_INFORMATION
98     {
99         long    KernelTime;
100         long    UserTime;
101         long    CreateTime;
102         uint    WaitTime;
103         void*   StartAddress;
104         PTID    ProcessId;
105         PTID    ThreadId;
106         int     Priority;
107         int     BasePriority;
108         uint    ContextSwitches;
109         uint    ThreadState;
110         int     WaitReason;
111         int     reserved;
112     }
113 
114     alias fnNtQuerySystemInformation = extern(Windows)
115     HRESULT function( uint SystemInformationClass, void* info, uint infoLength, uint* ReturnLength ) nothrow @nogc;
116 
117     enum ThreadBasicInformation = 0;
118 
119     struct THREAD_BASIC_INFORMATION
120     {
121         int    ExitStatus;
122         void** TebBaseAddress;
123         PTID   ProcessId;
124         PTID   ThreadId;
125         size_t AffinityMask;
126         int    Priority;
127         int    BasePriority;
128     }
129 
130     alias fnNtQueryInformationThread = extern(Windows)
131     int function( HANDLE ThreadHandle, uint ThreadInformationClass, void* buf, uint size, uint* ReturnLength ) nothrow @nogc;
132 
133     enum SYNCHRONIZE = 0x00100000;
134     enum THREAD_GET_CONTEXT = 8;
135     enum THREAD_QUERY_INFORMATION = 0x40;
136     enum THREAD_SUSPEND_RESUME = 2;
137 
138     ///////////////////////////////////////////////////////////////////
139     // get the thread environment block (TEB) of the thread with the given handle
140     static void** getTEB( HANDLE hnd ) nothrow @nogc
141     {
142         HANDLE nthnd = GetModuleHandleA( "NTDLL" );
143         assert( nthnd, "cannot get module handle for ntdll" );
144         fnNtQueryInformationThread fn = cast(fnNtQueryInformationThread) GetProcAddress( nthnd, "NtQueryInformationThread" );
145         assert( fn, "cannot find NtQueryInformationThread in ntdll" );
146 
147         THREAD_BASIC_INFORMATION tbi;
148         int Status = (*fn)(hnd, ThreadBasicInformation, &tbi, tbi.sizeof, null);
149         assert(Status == 0);
150 
151         return tbi.TebBaseAddress;
152     }
153 
154     // get the thread environment block (TEB) of the thread with the given identifier
155     static void** getTEB( uint id ) nothrow @nogc
156     {
157         HANDLE hnd = OpenThread( THREAD_QUERY_INFORMATION, FALSE, id );
158         assert( hnd, "OpenThread failed" );
159 
160         void** teb = getTEB( hnd );
161         CloseHandle( hnd );
162         return teb;
163     }
164 
165     // get linear address of TEB of current thread
166   version (LDC)
167   {
168     static void** getTEB() nothrow @nogc @naked
169     {
170         version (Win32)      return __asm!(void**)("mov %fs:(0x18), $0", "=r");
171         else version (Win64) return __asm!(void**)("mov %gs:0($1), $0", "=r,r", 0x30);
172         else static assert(false);
173     }
174   }
175   else
176   {
177     static void** getTEB() nothrow @nogc
178     {
179         version (Win32)
180         {
181             asm pure nothrow @nogc
182             {
183                 naked;
184                 mov EAX,FS:[0x18];
185                 ret;
186             }
187         }
188         else version (Win64)
189         {
190             asm pure nothrow @nogc
191             {
192                 naked;
193                 mov RAX,0x30;
194                 mov RAX,GS:[RAX]; // immediate value causes fixup
195                 ret;
196             }
197         }
198         else
199         {
200             static assert(false);
201         }
202     }
203   }
204 
205     // get the stack bottom (the top address) of the thread with the given handle
206     static void* getThreadStackBottom( HANDLE hnd ) nothrow @nogc @system
207     {
208         void** teb = getTEB( hnd );
209         return teb[1];
210     }
211 
212     // get the stack bottom (the top address) of the thread with the given identifier
213     static void* getThreadStackBottom( uint id ) nothrow @nogc @system
214     {
215         void** teb = getTEB( id );
216         return teb[1];
217     }
218 
219     // create a thread handle with full access to the thread with the given identifier
220     static HANDLE OpenThreadHandle( uint id ) nothrow @nogc
221     {
222         return OpenThread( SYNCHRONIZE|THREAD_GET_CONTEXT|THREAD_QUERY_INFORMATION|THREAD_SUSPEND_RESUME, FALSE, id );
223     }
224 
225     ///////////////////////////////////////////////////////////////////
226     // enumerate threads of the given process calling the passed function on each thread
227     // using function instead of delegate here to avoid allocating closure
228     static bool enumProcessThreads( uint procid, bool function( uint id, void* context ) dg, void* context ) @system
229     {
230         HANDLE hnd = GetModuleHandleA( "NTDLL" );
231         fnNtQuerySystemInformation fn = cast(fnNtQuerySystemInformation) GetProcAddress( hnd, "NtQuerySystemInformation" );
232         if ( !fn )
233             return false;
234 
235         uint sz = 16384;
236         uint retLength;
237         HRESULT rc;
238         char* buf;
239         for ( ; ; )
240         {
241             buf = cast(char*) core.stdc.stdlib.malloc(sz);
242             if (!buf)
243                 return false;
244             rc = fn( SystemProcessInformation, buf, sz, &retLength );
245             if ( rc != STATUS_INFO_LENGTH_MISMATCH )
246                 break;
247             core.stdc.stdlib.free( buf );
248             sz *= 2;
249         }
250         scope(exit) core.stdc.stdlib.free( buf );
251 
252         if (rc != 0)
253             return false;
254 
255         auto pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) buf;
256         auto pend  = cast(_SYSTEM_PROCESS_INFORMATION*) (buf + retLength);
257         for ( ; pinfo < pend; )
258         {
259             if ( pinfo.ProcessId == procid )
260             {
261                 auto tinfo = cast(_SYSTEM_THREAD_INFORMATION*)(pinfo + 1);
262                 for ( int i = 0; i < pinfo.NumberOfThreads; i++, tinfo++ )
263                     if ( tinfo.ProcessId == procid )
264                         if ( !dg( cast(uint) tinfo.ThreadId, context ) ) // IDs are actually DWORDs
265                             return false;
266             }
267             if ( pinfo.NextEntryOffset == 0 )
268                 break;
269             pinfo = cast(_SYSTEM_PROCESS_INFORMATION*) (cast(char*) pinfo + pinfo.NextEntryOffset);
270         }
271         return true;
272     }
273 
274     static bool enumProcessThreads( bool function( uint id, void* context ) dg, void* context )
275     {
276         return enumProcessThreads( GetCurrentProcessId(), dg, context );
277     }
278 
279     // execute function on the TLS for the given thread
280     alias extern(C) void function() externCVoidFunc;
281     static void impersonate_thread( uint id, externCVoidFunc fn )
282     {
283         impersonate_thread(id, () => fn());
284     }
285 
286     static void impersonate_thread( uint id, scope void delegate() dg) @system
287     {
288         if ( id == GetCurrentThreadId() )
289         {
290             dg();
291             return;
292         }
293 
294         // temporarily set current TLS array pointer to the array pointer of the referenced thread
295         void** curteb = getTEB();
296         void** teb    = getTEB( id );
297         assert( teb && curteb );
298 
299         void** curtlsarray = cast(void**) curteb[11];
300         void** tlsarray    = cast(void**) teb[11];
301         if ( !curtlsarray || !tlsarray )
302             return;
303 
304         curteb[11] = tlsarray;
305 
306         // swap out the TLS slots aswell
307         version (Win64)
308         {
309             enum TEB_offset_TlsSlots = 0x1480;
310             enum TEB_offset_TlsExpansionSlots = 0x1780;
311         }
312         else
313         {
314             enum TEB_offset_TlsSlots = 0xE10;
315             enum TEB_offset_TlsExpansionSlots = 0xF94;
316         }
317         void* tlsSlotsAdr(void** teb) { return cast(void*) teb + TEB_offset_TlsSlots; }
318         ref void* tlsExpansionSlots(void** teb) { return *cast(void**)(cast(void*) teb + TEB_offset_TlsExpansionSlots); }
319 
320         import core.stdc.string;
321         void*[64] slots = void;
322         memcpy(slots.ptr, tlsSlotsAdr(curteb), slots.sizeof);
323         void* extraSlots = tlsExpansionSlots(curteb);
324 
325         memcpy(tlsSlotsAdr(curteb), tlsSlotsAdr(teb), slots.sizeof);
326         tlsExpansionSlots(curteb) = tlsExpansionSlots(teb);
327 
328         dg();
329 
330         curteb[11] = curtlsarray;
331 
332         // copy the TLS slots back in case they have been changed in dg
333         memcpy(tlsSlotsAdr(teb), tlsSlotsAdr(curteb), slots.sizeof);
334         tlsExpansionSlots(teb) = tlsExpansionSlots(curteb);
335 
336         memcpy(tlsSlotsAdr(curteb), slots.ptr, slots.sizeof);
337         tlsExpansionSlots(curteb) = extraSlots;
338     }
339 }
340 
341 public:
342 // forward as few symbols as possible into the "global" name space
343 alias thread_aux.getTEB getTEB;
344 alias thread_aux.getThreadStackBottom getThreadStackBottom;
345 alias thread_aux.OpenThreadHandle OpenThreadHandle;
346 alias thread_aux.enumProcessThreads enumProcessThreads;
347 alias thread_aux.impersonate_thread impersonate_thread;
348 
349 // get the start of the TLS memory of the thread with the given handle
350 void* GetTlsDataAddress( HANDLE hnd ) nothrow @system
351 {
352     if ( void** teb = getTEB( hnd ) )
353         if ( void** tlsarray = cast(void**) teb[11] )
354             return tlsarray[_tls_index];
355     return null;
356 }
357 
358 // get the start of the TLS memory of the thread with the given identifier
359 void* GetTlsDataAddress( uint id ) nothrow
360 {
361     HANDLE hnd = OpenThread( thread_aux.THREAD_QUERY_INFORMATION, FALSE, id );
362     assert( hnd, "OpenThread failed" );
363 
364     void* tls = GetTlsDataAddress( hnd );
365     CloseHandle( hnd );
366     return tls;
367 }
368 
369 // get the address of the entry in the TLS array for the current thread
370 // use C mangling to access it from msvc.c
371 extern(C) void** GetTlsEntryAdr() @system
372 {
373     if( void** teb = getTEB() )
374         if( void** tlsarray = cast(void**) teb[11] )
375             return tlsarray + _tls_index;
376     return null;
377 }
378 
379 ///////////////////////////////////////////////////////////////////
380 // run rt_moduleTlsCtor in the context of the given thread
381 void thread_moduleTlsCtor( uint id )
382 {
383     thread_aux.impersonate_thread(id, &rt_moduleTlsCtor);
384 }
385 
386 ///////////////////////////////////////////////////////////////////
387 // run rt_moduleTlsDtor in the context of the given thread
388 void thread_moduleTlsDtor( uint id )
389 {
390     thread_aux.impersonate_thread(id, &rt_moduleTlsDtor);
391 }