Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Ticket #643: Thread.RegisterExternal.d

File Thread.RegisterExternal.d, 90.4 kB (added by CyberShadow, 1 year ago)

Failed attempt #2

Line 
1 /**
2  * The thread module provides support for thread creation and management.
3  *
4  * Copyright: Copyright (C) 2005-2006 Sean Kelly.  All rights reserved.
5  * License:   BSD style: $(LICENSE)
6  * Authors:   Sean Kelly
7  */
8 module tango.core.Thread;
9
10
11 // this should be true for most architectures
12 version = StackGrowsDown;
13
14
15 public
16 {
17     import tango.core.Type : Interval;
18 }
19 private
20 {
21     import tango.core.Exception;
22
23
24     //
25     // exposed by compiler runtime
26     //
27     extern (C) void* rt_stackBottom();
28     extern (C) void* rt_stackTop();
29
30
31     void* getStackBottom()
32     {
33         return rt_stackBottom();
34     }
35
36
37     void* getStackTop()
38     {
39         version( D_InlineAsm_X86 )
40         {
41             asm
42             {
43                 naked;
44                 mov EAX, ESP;
45                 ret;
46             }
47         }
48         else
49         {
50             return rt_stackTop();
51         }
52     }
53 }
54
55
56 ////////////////////////////////////////////////////////////////////////////////
57 // Thread Entry Point and Signal Handlers
58 ////////////////////////////////////////////////////////////////////////////////
59
60
61 version( Win32 )
62 {
63     private
64     {
65         import tango.stdc.stdint : uintptr_t; // for _beginthreadex decl below
66         import tango.sys.win32.UserGdi;
67
68         const DWORD TLS_OUT_OF_INDEXES  = 0xFFFFFFFF;
69
70         //
71         // avoid multiple imports via tango.sys.windows.process
72         //
73         extern (Windows) alias uint function(void*) btex_fptr;
74         extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*);
75
76
77         //
78         // entry point for Windows threads
79         //
80         extern (Windows) uint thread_entryPoint( void* arg )
81         {
82             Thread  obj = cast(Thread) arg;
83             assert( obj );
84             scope( exit ) Thread.remove( obj );
85
86             assert( obj.m_curr is &obj.m_main );
87             obj.m_main.bstack = getStackBottom();
88             obj.m_main.tstack = obj.m_main.bstack;
89             Thread.add( &obj.m_main );
90             Thread.setThis( obj );
91
92             // NOTE: No GC allocations may occur until the stack pointers have
93             //       been set and Thread.getThis returns a valid reference to
94             //       this thread object (this latter condition is not strictly
95             //       necessary on Win32 but it should be followed for the sake
96             //       of consistency).
97
98             // TODO: Consider putting an auto exception object here (using
99             //       alloca) forOutOfMemoryError plus something to track
100             //       whether an exception is in-flight?
101
102             try
103             {
104                 obj.run();
105             }
106             catch( Object o )
107             {
108                 obj.m_unhandled = o;
109             }
110             return 0;
111         }
112
113
114         //
115         // copy of the same-named function in phobos.std.thread--it uses the
116         // Windows naming convention to be consistent with GetCurrentThreadId
117         //
118         HANDLE GetCurrentThreadHandle()
119         {
120             const uint DUPLICATE_SAME_ACCESS = 0x00000002;
121
122             HANDLE curr = GetCurrentThread(),
123                    proc = GetCurrentProcess(),
124                    hndl;
125
126             DuplicateHandle( proc, curr, proc, &hndl, 0, TRUE, DUPLICATE_SAME_ACCESS );
127             return hndl;
128         }
129     }
130 }
131 else version( Posix )
132 {
133     private
134     {
135         import tango.stdc.posix.semaphore;
136         import tango.stdc.posix.pthread;
137         import tango.stdc.posix.signal;
138         import tango.stdc.posix.time;
139         import tango.stdc.errno;
140
141         extern (C) int getErrno();
142
143         version( GNU )
144         {
145             import gcc.builtins;
146         }
147
148
149         //
150         // entry point for POSIX threads
151         //
152         extern (C) void* thread_entryPoint( void* arg )
153         {
154             Thread  obj = cast(Thread) arg;
155             assert( obj );
156             scope( exit )
157             {
158                 // NOTE: isRunning should be set to false after the thread is
159                 //       removed or a double-removal could occur between this
160                 //       function and thread_suspendAll.
161                 Thread.remove( obj );
162                 obj.m_isRunning = false;
163             }
164
165             static extern (C) void thread_cleanupHandler( void* arg )
166             {
167                 Thread  obj = cast(Thread) arg;
168                 assert( obj );
169
170                 // NOTE: If the thread terminated abnormally, just set it as
171                 //       not running and let thread_suspendAll remove it from
172                 //       the thread list.  This is safer and is consistent
173                 //       with the Windows thread code.
174                 obj.m_isRunning = false;
175             }
176
177             // NOTE: Using void to skip the initialization here relies on
178             //       knowledge of how pthread_cleanup is implemented.  It may
179             //       not be appropriate for all platforms.  However, it does
180             //       avoid the need to link the pthread module.  If any
181             //       implementation actually requires default initialization
182             //       then pthread_cleanup should be restructured to maintain
183             //       the current lack of a link dependency.
184             pthread_cleanup cleanup = void;
185             cleanup.push( &thread_cleanupHandler, cast(void*) obj );
186
187             // NOTE: For some reason this does not always work for threads.
188             //obj.m_main.bstack = getStackBottom();
189             version( D_InlineAsm_X86 )
190             {
191                 static void* getBasePtr()
192                 {
193                     asm
194                     {
195                         naked;
196                         mov EAX, EBP;
197                         ret;
198                     }
199                 }
200
201                 obj.m_main.bstack = getBasePtr();
202             }
203             else version( StackGrowsDown )
204                 obj.m_main.bstack = &obj + 1;
205             else
206                 obj.m_main.bstack = &obj;
207             obj.m_main.tstack = obj.m_main.bstack;
208             assert( obj.m_curr == &obj.m_main );
209             Thread.add( &obj.m_main );
210             Thread.setThis( obj );
211
212             // NOTE: No GC allocations may occur until the stack pointers have
213             //       been set and Thread.getThis returns a valid reference to
214             //       this thread object (this latter condition is not strictly
215             //       necessary on Win32 but it should be followed for the sake
216             //       of consistency).
217
218             // TODO: Consider putting an auto exception object here (using
219             //       alloca) forOutOfMemoryError plus something to track
220             //       whether an exception is in-flight?
221
222             try
223             {
224                 obj.run();
225             }
226             catch( Object o )
227             {
228                 obj.m_unhandled = o;
229             }
230             return null;
231         }
232
233
234         //
235         // used to track the number of suspended threads
236         //
237         sem_t   suspendCount;
238
239
240         extern (C) void thread_suspendHandler( int sig )
241         in
242         {
243             assert( sig == SIGUSR1 );
244         }
245         body
246         {
247             version( D_InlineAsm_X86 )
248             {
249                 asm
250                 {
251                     pushad;
252                 }
253             }
254             else version( GNU )
255             {
256                 __builtin_unwind_init();
257             }
258             else
259             {
260                 static assert( false, "Architecture not supported." );
261             }
262
263             // NOTE: Since registers are being pushed and popped from the stack,
264             //       any other stack data used by this function should be gone
265             //       before the stack cleanup code is called below.
266             {
267                 Thread  obj = Thread.getThis();
268
269                 // NOTE: The thread reference returned by getThis is set within
270                 //       the thread startup code, so it is possible that this
271                 //       handler may be called before the reference is set.  In
272                 //       this case it is safe to simply suspend and not worry
273                 //       about the stack pointers as the thread will not have
274                 //       any references to GC-managed data.
275                 if( obj && !obj.m_lock )
276                 {
277                     obj.m_curr.tstack = getStackTop();
278                 }
279
280                 sigset_t    sigres = void;
281                 int         status;
282
283                 status = sigfillset( &sigres );
284                 assert( status == 0 );
285
286                 status = sigdelset( &sigres, SIGUSR2 );
287                 assert( status == 0 );
288
289                 status = sem_post( &suspendCount );
290                 assert( status == 0 );
291
292                 sigsuspend( &sigres );
293
294                 if( obj && !obj.m_lock )
295                 {
296                     obj.m_curr.tstack = obj.m_curr.bstack;
297                 }
298             }
299
300             version( D_InlineAsm_X86 )
301             {
302                 asm
303                 {
304                     popad;
305                 }
306             }
307             else version( GNU )
308             {
309                 // registers will be popped automatically
310             }
311             else
312             {
313                 static assert( false, "Architecture not supported." );
314             }
315         }
316
317
318         extern (C) void thread_resumeHandler( int sig )
319         in
320         {
321             assert( sig == SIGUSR2 );
322         }
323         body
324         {
325
326         }
327     }
328 }
329 else
330 {
331     // NOTE: This is the only place threading versions are checked.  If a new
332     //       version is added, the module code will need to be searched for
333     //       places where version-specific code may be required.  This can be
334     //       easily accomlished by searching for 'Windows' or 'Posix'.
335     static assert( false, "Unknown threading implementation." );
336 }
337
338
339 ////////////////////////////////////////////////////////////////////////////////
340 // Thread
341 ////////////////////////////////////////////////////////////////////////////////
342
343
344 /**
345  * This class encapsulates all threading functionality for the D
346  * programming language.  As thread manipulation is a required facility
347  * for garbage collection, all user threads should derive from this
348  * class, and instances of this class should never be explicitly deleted.
349  * A new thread may be created using either derivation or composition, as
350  * in the following example.
351  *
352  * Example:
353  * -----------------------------------------------------------------------------
354  *
355  * class DerivedThread : Thread
356  * {
357  *     this()
358  *     {
359  *         super( &run );
360  *     }
361  *
362  * private :
363  *     void run()
364  *     {
365  *         printf( "Derived thread running.\n" );
366  *     }
367  * }
368  *
369  * void threadFunc()
370  * {
371  *     printf( "Composed thread running.\n" );
372  * }
373  *
374  * // create instances of each type
375  * Thread derived = new DerivedThread();
376  * Thread composed = new Thread( &threadFunc );
377  *
378  * // start both threads
379  * derived.start();
380  * composed.start();
381  *
382  * -----------------------------------------------------------------------------
383  */
384 class Thread
385 {
386     ////////////////////////////////////////////////////////////////////////////
387     // Initialization
388     ////////////////////////////////////////////////////////////////////////////
389
390
391     /**
392      * Initializes a thread object which is associated with a static
393      * D function.
394      *
395      * Params:
396      *  fn = The thread function.
397      *  sz = The stack size for this thread.
398      *
399      * In:
400      *  fn must not be null.
401      */
402     this( void function() fn, size_t sz = 0 )
403     in
404     {
405         assert( fn );
406     }
407     body
408     {
409         m_fn   = fn;
410         m_sz   = sz;
411         m_call = Call.FN;
412         m_curr = &m_main;
413     }
414
415
416     /**
417      * Initializes a thread object which is associated with a dynamic
418      * D function.
419      *
420      * Params:
421      *  dg = The thread function.
422      *  sz = The stack size for this thread.
423      *
424      * In:
425      *  dg must not be null.
426      */
427     this( void delegate() dg, size_t sz = 0 )
428     in
429     {
430         assert( dg );
431     }
432     body
433     {
434         m_dg   = dg;
435         m_sz   = sz;
436         m_call = Call.DG;
437         m_curr = &m_main;
438     }
439
440
441     /**
442      * Cleans up any remaining resources used by this object.
443      */
444     ~this()
445     {
446         if( m_addr == m_addr.init )
447         {
448             return;
449         }
450
451         version( Win32 )
452         {
453             m_addr = m_addr.init;
454             CloseHandle( m_hndl );
455             m_hndl = m_hndl.init;
456         }
457         else version( Posix )
458         {
459             pthread_detach( m_addr );
460             m_addr = m_addr.init;
461         }
462     }
463
464
465     ////////////////////////////////////////////////////////////////////////////
466     // General Actions
467     ////////////////////////////////////////////////////////////////////////////
468
469
470     /**
471      * Registers an "external" thread (a thread not created using Tango's API).
472      */
473     static void registerExternalThread()
474     {
475         Thread t = new Thread();
476
477         version(Win32)
478         {
479             t.m_addr  = GetCurrentThreadId();
480             t.m_hndl  = GetCurrentThreadHandle();
481         }
482         else
483         {
484             unimplemented();
485         }
486
487         t.m_isDaemon = true;
488
489         t.m_main.bstack = getStackBottom();
490         t.m_main.tstack = t.m_main.bstack;
491         Thread.add( &t.m_main );
492         Thread.setThis( t );
493         Thread.add(t);
494     }
495
496     /**
497      * Starts the thread and invokes the function or delegate passed upon
498      * construction.
499      *
500      * In:
501      *  This routine may only be called once per thread instance.
502      *
503      * Throws:
504      *  ThreadException if the thread fails to start.
505      */
506     final void start()
507     in
508     {
509         assert( !next && !prev );
510     }
511     body
512     {
513         version( Win32 ) {} else
514         version( Posix )
515         {
516             pthread_attr_t  attr;
517
518             if( pthread_attr_init( &attr ) )
519                 throw new ThreadException( "Error initializing thread attributes" );
520             if( m_sz && pthread_attr_setstacksize( &attr, m_sz ) )
521                 throw new ThreadException( "Error initializing thread stack size" );
522         }
523
524         // NOTE: This operation needs to be synchronized to avoid a race
525         //       condition with the GC.  Without this lock, the thread
526         //       could start and allocate memory before being added to
527         //       the global thread list, preventing it from being scanned
528         //       and causing memory to be collected that is still in use.
529         synchronized( slock )
530         {
531             version( Win32 )
532             {
533                 m_hndl = cast(HANDLE) _beginthreadex( null, m_sz, &thread_entryPoint, cast(void*) this, 0, &m_addr );
534                 if( cast(size_t) m_hndl == 0 )
535                     throw new ThreadException( "Error creating thread" );
536             }
537             else version( Posix )
538             {
539                 m_isRunning = true;
540                 scope( failure ) m_isRunning = false;
541
542                 if( pthread_create( &m_addr, &attr, &thread_entryPoint, cast(void*) this ) != 0 )
543                     throw new ThreadException( "Error creating thread" );
544             }
545             multiThreadedFlag = true;
546             add( this );
547         }
548     }
549
550
551     /**
552      * Waits for this thread to complete.  If the thread terminated as the
553      * result of an unhandled exception, this exception will be rethrown.
554      *
555      * Params:
556      *  rethrow = Rethrow any unhandled exception which may have caused this
557      *            thread to terminate.
558      *
559      * Throws:
560      *  ThreadException if the operation fails.
561      *  Any exception not handled by the joined thread.
562      */
563     final void join( bool rethrow = true )
564     {
565         version( Win32 )
566         {
567             if( WaitForSingleObject( m_hndl, INFINITE ) != WAIT_OBJECT_0 )
568                 throw new ThreadException( "Unable to join thread" );
569             // NOTE: m_addr must be cleared before m_hndl is closed to avoid
570             //       a race condition with isRunning.  The operation is labeled
571             //       volatile to prevent compiler reordering.
572             volatile m_addr = m_addr.init;
573             CloseHandle( m_hndl );
574             m_hndl = m_hndl.init;
575         }
576         else version( Posix )
577         {
578             if( pthread_join( m_addr, null ) != 0 )
579                 throw new ThreadException( "Unable to join thread" );
580             // NOTE: pthread_join acts as a substitute for pthread_detach,
581             //       which is normally called by the dtor.  Setting m_addr
582             //       to zero ensures that pthread_detach will not be called
583             //       on object destruction.
584             volatile m_addr = m_addr.init;
585         }
586         if( rethrow && m_unhandled )
587         {
588             throw m_unhandled;
589         }
590     }
591
592
593     ////////////////////////////////////////////////////////////////////////////
594     // General Properties
595     ////////////////////////////////////////////////////////////////////////////
596
597
598     /**
599      * Gets the user-readable label for this thread.
600      *
601      * Returns:
602      *  The name of this thread.
603      */
604     final char[] name()
605     {
606         synchronized( this )
607         {
608             return m_name;
609         }
610     }
611
612
613     /**
614      * Sets the user-readable label for this thread.
615      *
616      * Params:
617      *  val = The new name of this thread.
618      */
619     final void name( char[] val )
620     {
621         synchronized( this )
622         {
623             m_name = val.dup;
624         }
625     }
626
627
628     /**
629      * Gets the daemon status for this thread.
630      *
631      * Returns:
632      *  true if this is a daemon thread.
633      */
634     final bool isDaemon()
635     {
636         synchronized( this )
637         {
638             return m_isDaemon;
639         }
640     }
641
642
643     /**
644      * Sets the daemon status for this thread.
645      *
646      * Params:
647      *  val = The new daemon status for this thread.
648      */
649     final void isDaemon( bool val )
650     {
651         synchronized( this )
652         {
653             m_isDaemon = val;
654         }
655     }
656
657
658     /**
659      * Tests whether this thread is running.
660      *
661      * Returns:
662      *  true if the thread is running, false if not.
663      */
664     final bool isRunning()
665     {
666         if( m_addr == m_addr.init )
667         {
668             return false;
669         }
670
671         version( Win32 )
672         {
673             uint ecode = 0;
674             GetExitCodeThread( m_hndl, &ecode );
675             return ecode == STILL_ACTIVE;
676         }
677         else version( Posix )
678         {
679             // NOTE: It should be safe to access this value without
680             //       memory barriers because word-tearing and such
681             //       really isn't an issue for boolean values.
682             return m_isRunning;
683         }
684     }
685
686
687     ////////////////////////////////////////////////////////////////////////////
688     // Thread Priority Actions
689     ////////////////////////////////////////////////////////////////////////////
690
691
692     /**
693      * The minimum scheduling priority that may be set for a thread.  On
694      * systems where multiple scheduling policies are defined, this value
695      * represents the minimum valid priority for the scheduling policy of
696      * the process.
697      */
698     static const int PRIORITY_MIN;
699
700
701     /**
702      * The maximum scheduling priority that may be set for a thread.  On
703      * systems where multiple scheduling policies are defined, this value
704      * represents the minimum valid priority for the scheduling policy of
705      * the process.
706      */
707     static const int PRIORITY_MAX;
708
709
710     /**
711      * Gets the scheduling priority for the associated thread.
712      *
713      * Returns:
714      *  The scheduling priority of this thread.
715      */
716     final int priority()
717     {
718         version( Win32 )
719         {
720             return GetThreadPriority( m_hndl );
721         }
722         else version( Posix )
723         {
724             int         policy;
725             sched_param param;
726
727             if( pthread_getschedparam( m_addr, &policy, &param ) )
728                 throw new ThreadException( "Unable to get thread priority" );
729             return param.sched_priority;
730         }
731     }
732
733
734     /**
735      * Sets the scheduling priority for the associated thread.
736      *
737      * Params:
738      *  val = The new scheduling priority of this thread.
739      */
740     final void priority( int val )
741     {
742         version( Win32 )
743         {
744             if( !SetThreadPriority( m_hndl, val ) )
745                 throw new ThreadException( "Unable to set thread priority" );
746         }
747         else version( Posix )
748         {
749             // NOTE: pthread_setschedprio is not implemented on linux, so use
750             //       the more complicated get/set sequence below.
751             //if( pthread_setschedprio( m_addr, val ) )
752             //    throw new ThreadException( "Unable to set thread priority" );
753
754             int         policy;
755             sched_param param;
756
757             if( pthread_getschedparam( m_addr, &policy, &param ) )
758                 throw new ThreadException( "Unable to set thread priority" );
759             param.sched_priority = val;
760             if( pthread_setschedparam( m_addr, policy, &param ) )
761                 throw new Thr