2 * The thread module provides support for thread creation and management.
4 * Copyright: Copyright (c) 2005-2008, The D Runtime Project
5 * License: BSD Style, see LICENSE
11 // this should be true for most architectures
12 version = StackGrowsDown;
15 ///////////////////////////////////////////////////////////////////////////////
16 // Thread and Fiber Exceptions
17 ///////////////////////////////////////////////////////////////////////////////
21 * Base class for thread exceptions.
23 class ThreadException : Exception
33 * Base class for fiber exceptions.
35 class FiberException : Exception
47 // exposed by compiler runtime
49 extern (C) void* rt_stackBottom();
50 extern (C) void* rt_stackTop();
53 void* getStackBottom()
55 return rt_stackBottom();
61 version( D_InlineAsm_X86 )
78 ///////////////////////////////////////////////////////////////////////////////
79 // Thread Entry Point and Signal Handlers
80 ///////////////////////////////////////////////////////////////////////////////
87 import stdc.stdint : uintptr_t; // for _beginthreadex decl below
88 import sys.windows.windows;
90 const DWORD TLS_OUT_OF_INDEXES = 0xFFFFFFFF;
92 extern (Windows) alias uint function(void*) btex_fptr;
93 extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*);
97 // entry point for Windows threads
99 extern (Windows) uint thread_entryPoint( void* arg )
101 Thread obj = cast(Thread) arg;
103 scope( exit ) Thread.remove( obj );
105 assert( obj.m_curr is &obj.m_main );
106 obj.m_main.bstack = getStackBottom();
107 obj.m_main.tstack = obj.m_main.bstack;
108 Thread.add( &obj.m_main );
109 Thread.setThis( obj );
111 // NOTE: No GC allocations may occur until the stack pointers have
112 // been set and Thread.getThis returns a valid reference to
113 // this thread object (this latter condition is not strictly
114 // necessary on Win32 but it should be followed for the sake
117 // TODO: Consider putting an auto exception object here (using
118 // alloca) forOutOfMemoryError plus something to track
119 // whether an exception is in-flight?
134 // copy of the same-named function in phobos.std.thread--it uses the
135 // Windows naming convention to be consistent with GetCurrentThreadId
137 HANDLE GetCurrentThreadHandle()
139 const uint DUPLICATE_SAME_ACCESS = 0x00000002;
141 HANDLE curr = GetCurrentThread(),
142 proc = GetCurrentProcess(),
145 DuplicateHandle( proc, curr, proc, &hndl, 0, TRUE, DUPLICATE_SAME_ACCESS );
150 else version( Posix )
154 import stdc.posix.semaphore;
155 import stdc.posix.pthread;
156 import stdc.posix.signal;
157 import stdc.posix.time;
167 // entry point for POSIX threads
169 extern (C) void* thread_entryPoint( void* arg )
171 Thread obj = cast(Thread) arg;
175 // NOTE: isRunning should be set to false after the thread is
176 // removed or a double-removal could occur between this
177 // function and thread_suspendAll.
178 Thread.remove( obj );
179 obj.m_isRunning = false;
182 static extern (C) void thread_cleanupHandler( void* arg )
184 Thread obj = cast(Thread) arg;
187 // NOTE: If the thread terminated abnormally, just set it as
188 // not running and let thread_suspendAll remove it from
189 // the thread list. This is safer and is consistent
190 // with the Windows thread code.
191 obj.m_isRunning = false;
194 // NOTE: Using void to skip the initialization here relies on
195 // knowledge of how pthread_cleanup is implemented. It may
196 // not be appropriate for all platforms. However, it does
197 // avoid the need to link the pthread module. If any
198 // implementation actually requires default initialization
199 // then pthread_cleanup should be restructured to maintain
200 // the current lack of a link dependency.
201 pthread_cleanup cleanup = void;
202 cleanup.push( &thread_cleanupHandler, cast(void*) obj );
204 // NOTE: For some reason this does not always work for threads.
205 //obj.m_main.bstack = getStackBottom();
206 version( D_InlineAsm_X86 )
208 static void* getBasePtr()
218 obj.m_main.bstack = getBasePtr();
220 else version( StackGrowsDown )
221 obj.m_main.bstack = &obj + 1;
223 obj.m_main.bstack = &obj;
224 obj.m_main.tstack = obj.m_main.bstack;
225 assert( obj.m_curr == &obj.m_main );
226 Thread.add( &obj.m_main );
227 Thread.setThis( obj );
229 // NOTE: No GC allocations may occur until the stack pointers have
230 // been set and Thread.getThis returns a valid reference to
231 // this thread object (this latter condition is not strictly
232 // necessary on Win32 but it should be followed for the sake
235 // TODO: Consider putting an auto exception object here (using
236 // alloca) forOutOfMemoryError plus something to track
237 // whether an exception is in-flight?
252 // used to track the number of suspended threads
257 extern (C) void thread_suspendHandler( int sig )
260 assert( sig == SIGUSR1 );
264 version( D_InlineAsm_X86 )
273 __builtin_unwind_init();
277 static assert( false, "Architecture not supported." );
280 // NOTE: Since registers are being pushed and popped from the
281 // stack, any other stack data used by this function should
282 // be gone before the stack cleanup code is called below.
284 Thread obj = Thread.getThis();
286 // NOTE: The thread reference returned by getThis is set within
287 // the thread startup code, so it is possible that this
288 // handler may be called before the reference is set. In
289 // this case it is safe to simply suspend and not worry
290 // about the stack pointers as the thread will not have
291 // any references to GC-managed data.
292 if( obj && !obj.m_lock )
294 obj.m_curr.tstack = getStackTop();
297 sigset_t sigres = void;
300 status = sigfillset( &sigres );
301 assert( status == 0 );
303 status = sigdelset( &sigres, SIGUSR2 );
304 assert( status == 0 );
306 status = sem_post( &suspendCount );
307 assert( status == 0 );
309 sigsuspend( &sigres );
311 if( obj && !obj.m_lock )
313 obj.m_curr.tstack = obj.m_curr.bstack;
317 version( D_InlineAsm_X86 )
326 // registers will be popped automatically
330 static assert( false, "Architecture not supported." );
335 extern (C) void thread_resumeHandler( int sig )
338 assert( sig == SIGUSR2 );
348 // NOTE: This is the only place threading versions are checked. If a new
349 // version is added, the module code will need to be searched for
350 // places where version-specific code may be required. This can be
351 // easily accomlished by searching for 'Windows' or 'Posix'.
352 static assert( false, "Unknown threading implementation." );
356 ///////////////////////////////////////////////////////////////////////////////
358 ///////////////////////////////////////////////////////////////////////////////
362 * This class encapsulates all threading functionality for the D
363 * programming language. As thread manipulation is a required facility
364 * for garbage collection, all user threads should derive from this
365 * class, and instances of this class should never be explicitly deleted.
366 * A new thread may be created using either derivation or composition, as
367 * in the following example.
370 * ----------------------------------------------------------------------------
372 * class DerivedThread : Thread
382 * printf( "Derived thread running.\n" );
388 * printf( "Composed thread running.\n" );
391 * // create instances of each type
392 * Thread derived = new DerivedThread();
393 * Thread composed = new Thread( &threadFunc );
395 * // start both threads
399 * ----------------------------------------------------------------------------
403 ///////////////////////////////////////////////////////////////////////////
405 ///////////////////////////////////////////////////////////////////////////
409 * Initializes a thread object which is associated with a static
413 * fn = The thread function.
414 * sz = The stack size for this thread.
417 * fn must not be null.
419 this( void function() fn, size_t sz = 0 )
434 * Initializes a thread object which is associated with a dynamic
438 * dg = The thread function.
439 * sz = The stack size for this thread.
442 * dg must not be null.
444 this( void delegate() dg, size_t sz = 0 )
459 * Cleans up any remaining resources used by this object.
463 if( m_addr == m_addr.init )
470 m_addr = m_addr.init;
471 CloseHandle( m_hndl );
472 m_hndl = m_hndl.init;
474 else version( Posix )
476 pthread_detach( m_addr );
477 m_addr = m_addr.init;
482 ///////////////////////////////////////////////////////////////////////////
484 ///////////////////////////////////////////////////////////////////////////
488 * Starts the thread and invokes the function or delegate passed upon
492 * This routine may only be called once per thread instance.
495 * ThreadException if the thread fails to start.
500 assert( !next && !prev );
504 version( Win32 ) {} else
509 if( pthread_attr_init( &attr ) )
510 throw new ThreadException( "Error initializing thread attributes" );
511 if( m_sz && pthread_attr_setstacksize( &attr, m_sz ) )
512 throw new ThreadException( "Error initializing thread stack size" );
513 if( pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) )
514 throw new ThreadException( "Error setting thread joinable" );
517 // NOTE: This operation needs to be synchronized to avoid a race
518 // condition with the GC. Without this lock, the thread
519 // could start and allocate memory before being added to
520 // the global thread list, preventing it from being scanned
521 // and causing memory to be collected that is still in use.
522 synchronized( slock )
526 m_hndl = cast(HANDLE) _beginthreadex( null, m_sz, &thread_entryPoint, cast(void*) this, 0, &m_addr );
527 if( cast(size_t) m_hndl == 0 )
528 throw new ThreadException( "Error creating thread" );
530 else version( Posix )
533 scope( failure ) m_isRunning = false;
535 if( pthread_create( &m_addr, &attr, &thread_entryPoint, cast(void*) this ) != 0 )
536 throw new ThreadException( "Error creating thread" );
538 multiThreadedFlag = true;
545 * Waits for this thread to complete. If the thread terminated as the
546 * result of an unhandled exception, this exception will be rethrown.
549 * rethrow = Rethrow any unhandled exception which may have caused this
550 * thread to terminate.
553 * ThreadException if the operation fails.
554 * Any exception not handled by the joined thread.
557 * Any exception not handled by this thread if rethrow = false, null
560 final Object join( bool rethrow = true )
564 if( WaitForSingleObject( m_hndl, INFINITE ) != WAIT_OBJECT_0 )
565 throw new ThreadException( "Unable to join thread" );
566 // NOTE: m_addr must be cleared before m_hndl is closed to avoid
567 // a race condition with isRunning. The operation is labeled
568 // volatile to prevent compiler reordering.
569 volatile m_addr = m_addr.init;
570 CloseHandle( m_hndl );
571 m_hndl = m_hndl.init;
573 else version( Posix )
575 if( pthread_join( m_addr, null ) != 0 )
576 throw new ThreadException( "Unable to join thread" );
577 // NOTE: pthread_join acts as a substitute for pthread_detach,
578 // which is normally called by the dtor. Setting m_addr
579 // to zero ensures that pthread_detach will not be called
580 // on object destruction.
581 volatile m_addr = m_addr.init;
593 ///////////////////////////////////////////////////////////////////////////
594 // General Properties
595 ///////////////////////////////////////////////////////////////////////////
599 * Gets the user-readable label for this thread.
602 * The name of this thread.
614 * Sets the user-readable label for this thread.
617 * val = The new name of this thread.
619 final void name( char[] val )
629 * Gets the daemon status for this thread. While the runtime will wait for
630 * all normal threads to complete before tearing down the process, daemon
631 * threads are effectively ignored and thus will not prevent the process
632 * from terminating. In effect, daemon threads will be terminated
633 * automatically by the OS when the process exits.
636 * true if this is a daemon thread.
638 final bool isDaemon()
648 * Sets the daemon status for this thread. While the runtime will wait for
649 * all normal threads to complete before tearing down the process, daemon
650 * threads are effectively ignored and thus will not prevent the process
651 * from terminating. In effect, daemon threads will be terminated
652 * automatically by the OS when the process exits.
655 * val = The new daemon status for this thread.
657 final void isDaemon( bool val )
667 * Tests whether this thread is running.
670 * true if the thread is running, false if not.
672 final bool isRunning()
674 if( m_addr == m_addr.init )
682 GetExitCodeThread( m_hndl, &ecode );
683 return ecode == STILL_ACTIVE;
685 else version( Posix )
687 // NOTE: It should be safe to access this value without
688 // memory barriers because word-tearing and such
689 // really isn't an issue for boolean values.
695 ///////////////////////////////////////////////////////////////////////////
696 // Thread Priority Actions
697 ///////////////////////////////////////////////////////////////////////////
701 * The minimum scheduling priority that may be set for a thread. On
702 * systems where multiple scheduling policies are defined, this value
703 * represents the minimum valid priority for the scheduling policy of
706 static const int PRIORITY_MIN;
710 * The maximum scheduling priority that may be set for a thread. On
711 * systems where multiple scheduling policies are defined, this value
712 * represents the minimum valid priority for the scheduling policy of
715 static const int PRIORITY_MAX;
719 * Gets the scheduling priority for the associated thread.
722 * The scheduling priority of this thread.
728 return GetThreadPriority( m_hndl );
730 else version( Posix )
735 if( pthread_getschedparam( m_addr, &policy, ¶m ) )
736 throw new ThreadException( "Unable to get thread priority" );
737 return param.sched_priority;
743 * Sets the scheduling priority for the associated thread.
746 * val = The new scheduling priority of this thread.
748 final void priority( int val )
752 if( !SetThreadPriority( m_hndl, val ) )
753 throw new ThreadException( "Unable to set thread priority" );
755 else version( Posix )
757 // NOTE: pthread_setschedprio is not implemented on linux, so use
758 // the more complicated get/set sequence below.
759 //if( pthread_setschedprio( m_addr, val ) )
760 // throw new ThreadException( "Unable to set thread priority" );
765 if( pthread_getschedparam( m_addr, &policy, ¶m ) )
766 throw new ThreadException( "Unable to set thread priority" );
767 param.sched_priority = val;
768 if( pthread_setschedparam( m_addr, policy, ¶m ) )
769 throw new ThreadException( "Unable to set thread priority" );
774 ///////////////////////////////////////////////////////////////////////////
775 // Actions on Calling Thread
776 ///////////////////////////////////////////////////////////////////////////
780 * Suspends the calling thread for at least the supplied time, up to a
781 * maximum of (uint.max - 1) milliseconds.
784 * period = The minimum duration the calling thread should be suspended,
785 * in seconds. Sub-second durations are specified as fractional
789 * period must be less than (uint.max - 1) milliseconds.
792 * ------------------------------------------------------------------------
794 * Thread.sleep( 0.05 ); // sleep for 50 milliseconds
795 * Thread.sleep( 5 ); // sleep for 5 seconds
797 * ------------------------------------------------------------------------
799 static void sleep( double period )
802 // NOTE: The fractional value added to period is to correct fp error.
803 assert( period * 1000 + 0.1 < uint.max - 1 );
809 Sleep( cast(uint)( period * 1000 + 0.1 ) );
811 else version( Posix )
814 timespec tout = void;
816 period += 0.000_000_000_1;
818 if( tin.tv_sec.max < period )
820 tin.tv_sec = tin.tv_sec.max;
825 tin.tv_sec = cast(typeof(tin.tv_sec)) period;
826 tin.tv_nsec = cast(typeof(tin.tv_nsec)) ((period % 1.0) * 1_000_000_000);
831 if( !nanosleep( &tin, &tout ) )
833 if( getErrno() != EINTR )
834 throw new ThreadException( "Unable to sleep for specified duration" );
842 * Forces a context switch to occur away from the calling thread.
848 // NOTE: Sleep(1) is necessary because Sleep(0) does not give
849 // lower priority threads any timeslice, so looping on
850 // Sleep(0) could be resource-intensive in some cases.
853 else version( Posix )
860 ///////////////////////////////////////////////////////////////////////////
862 ///////////////////////////////////////////////////////////////////////////
866 * Provides a reference to the calling thread.
869 * The thread object representing the calling thread. The result of
870 * deleting this object is undefined.
872 static Thread getThis()
874 // NOTE: This function may not be called until thread_init has
875 // completed. See thread_suspendAll for more information
876 // on why this might occur.
879 return cast(Thread) TlsGetValue( sm_this );
881 else version( Posix )
883 return cast(Thread) pthread_getspecific( sm_this );
889 * Provides a list of all threads currently being tracked by the system.
892 * An array containing references to all threads currently being
893 * tracked by the system. The result of deleting any contained
894 * objects is undefined.
896 static Thread[] getAll()
898 synchronized( slock )
901 Thread[] buf = new Thread[sm_tlen];
903 foreach( Thread t; Thread )
913 * Operates on all threads currently being tracked by the system. The
914 * result of deleting any Thread object is undefined.
917 * dg = The supplied code as a delegate.
920 * Zero if all elemented are visited, nonzero if not.
922 static int opApply( int delegate( inout Thread ) dg )
924 synchronized( slock )
928 for( Thread t = sm_tbeg; t; t = t.next )
939 ///////////////////////////////////////////////////////////////////////////
940 // Local Storage Actions
941 ///////////////////////////////////////////////////////////////////////////
945 * Indicates the number of local storage pointers available at program
946 * startup. It is recommended that this number be at least 64.
948 static const uint LOCAL_MAX = 64;
952 * Reserves a local storage pointer for use and initializes this location
953 * to null for all running threads.
956 * A key representing the array offset of this memory location.
958 static uint createLocal()
960 synchronized( slock )
962 foreach( uint key, inout bool set; sm_local )
966 //foreach( Thread t; sm_tbeg ) Bug in GDC 0.24 SVN (r139)
967 for( Thread t = sm_tbeg; t; t = t.next )
969 t.m_local[key] = null;
975 throw new ThreadException( "No more local storage slots available" );
981 * Marks the supplied key as available and sets the associated location
982 * to null for all running threads. It is assumed that any key passed
983 * to this function is valid. The result of calling this function for
984 * a key which is still in use is undefined.
987 * key = The key to delete.
989 static void deleteLocal( uint key )
991 synchronized( slock )
993 sm_local[key] = false;
994 // foreach( Thread t; sm_tbeg ) Bug in GDC 0.24 SVN (r139)
995 for( Thread t = sm_tbeg; t; t = t.next )
997 t.m_local[key] = null;
1004 * Loads the value stored at key within a thread-local static array. It is
1005 * assumed that any key passed to this function is valid.
1008 * key = The location which holds the desired data.
1011 * The data associated with the supplied key.
1013 static void* getLocal( uint key )
1015 return getThis().m_local[key];
1020 * Stores the supplied value at key within a thread-local static array. It
1021 * is assumed that any key passed to this function is valid.
1024 * key = The location to store the supplied data.
1025 * val = The data to store.
1028 * A copy of the data which has just been stored.
1030 static void* setLocal( uint key, void* val )
1032 return getThis().m_local[key] = val;
1036 ///////////////////////////////////////////////////////////////////////////
1037 // Static Initalizer
1038 ///////////////////////////////////////////////////////////////////////////
1042 * This initializer is used to set thread constants. All functional
1043 * initialization occurs within thread_init().
1052 else version( Posix )
1056 pthread_t self = pthread_self();
1058 int status = pthread_getschedparam( self, &policy, ¶m );
1059 assert( status == 0 );
1061 PRIORITY_MIN = sched_get_priority_min( policy );
1062 assert( PRIORITY_MIN != -1 );
1064 PRIORITY_MAX = sched_get_priority_max( policy );
1065 assert( PRIORITY_MAX != -1 );
1072 // Initializes a thread object which has no associated executable function.
1073 // This is used for the main thread initialized in thread_init().
1083 // Thread entry point. Invokes the function or delegate passed on
1084 // construction (if any).
1104 // The type of routine passed on thread construction.
1120 alias uint ThreadAddr;
1122 else version( Posix )
1124 alias pthread_key_t TLSKey;
1125 alias pthread_t ThreadAddr;
1132 static bool[LOCAL_MAX] sm_local;
1133 static TLSKey sm_this;
1135 void*[LOCAL_MAX] m_local;
1139 // Standard thread data
1150 void function() m_fn;
1151 void delegate() m_dg;
1163 ///////////////////////////////////////////////////////////////////////////
1164 // Storage of Active Thread
1165 ///////////////////////////////////////////////////////////////////////////
1169 // Sets a thread-local reference to the current thread object.
1171 static void setThis( Thread t )
1175 TlsSetValue( sm_this, cast(void*) t );
1177 else version( Posix )
1179 pthread_setspecific( sm_this, cast(void*) t );
1185 ///////////////////////////////////////////////////////////////////////////
1186 // Thread Context and GC Scanning Support
1187 ///////////////////////////////////////////////////////////////////////////
1190 final void pushContext( Context* c )
1193 assert( !c.within );
1202 final void popContext()
1205 assert( m_curr && m_curr.within );
1209 Context* c = m_curr;
1215 final Context* topContext()
1226 static struct Context
1242 uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax
1247 ///////////////////////////////////////////////////////////////////////////
1248 // GC Scanning Support
1249 ///////////////////////////////////////////////////////////////////////////
1252 // NOTE: The GC scanning process works like so:
1254 // 1. Suspend all threads.
1255 // 2. Scan the stacks of all suspended threads for roots.
1256 // 3. Resume all threads.
1258 // Step 1 and 3 require a list of all threads in the system, while
1259 // step 2 requires a list of all thread stacks (each represented by
1260 // a Context struct). Traditionally, there was one stack per thread
1261 // and the Context structs were not necessary. However, Fibers have
1262 // changed things so that each thread has its own 'main' stack plus
1263 // an arbitrary number of nested stacks (normally referenced via
1264 // m_curr). Also, there may be 'free-floating' stacks in the system,
1265 // which are Fibers that are not currently executing on any specific
1266 // thread but are still being processed and still contain valid
1269 // To support all of this, the Context struct has been created to
1270 // represent a stack range, and a global list of Context structs has
1271 // been added to enable scanning of these stack ranges. The lifetime
1272 // (and presence in the Context list) of a thread's 'main' stack will
1273 // be equivalent to the thread's lifetime. So the Ccontext will be
1274 // added to the list on thread entry, and removed from the list on
1275 // thread exit (which is essentially the same as the presence of a
1276 // Thread object in its own global list). The lifetime of a Fiber's
1277 // context, however, will be tied to the lifetime of the Fiber object
1278 // itself, and Fibers are expected to add/remove their Context struct
1279 // on construction/deletion.
1283 // All use of the global lists should synchronize on this lock.
1285 static Object slock()
1287 return Thread.classinfo;
1291 static Context* sm_cbeg;
1292 static size_t sm_clen;
1294 static Thread sm_tbeg;
1295 static size_t sm_tlen;
1298 // Used for ordering threads in the global thread list.
1304 ///////////////////////////////////////////////////////////////////////////
1305 // Global Context List Operations
1306 ///////////////////////////////////////////////////////////////////////////
1310 // Add a context to the global context list.
1312 static void add( Context* c )
1316 assert( !c.next && !c.prev );
1320 synchronized( slock )
1334 // Remove a context from the global context list.
1336 static void remove( Context* c )
1340 assert( c.next || c.prev );
1344 synchronized( slock )
1347 c.prev.next = c.next;
1349 c.next.prev = c.prev;
1354 // NOTE: Don't null out c.next or c.prev because opApply currently
1355 // follows c.next after removing a node. This could be easily
1356 // addressed by simply returning the next node from this
1357 // function, however, a context should never be re-added to the
1358 // list anyway and having next and prev be non-null is a good way
1363 ///////////////////////////////////////////////////////////////////////////
1364 // Global Thread List Operations
1365 ///////////////////////////////////////////////////////////////////////////
1369 // Add a thread to the global thread list.
1371 static void add( Thread t )
1375 assert( !t.next && !t.prev );
1376 assert( t.isRunning );
1380 synchronized( slock )
1394 // Remove a thread from the global thread list.
1396 static void remove( Thread t )
1400 assert( t.next || t.prev );
1403 // NOTE: This doesn't work for Posix as m_isRunning must be set to
1404 // false after the thread is removed during normal execution.
1405 assert( !t.isRunning );
1410 synchronized( slock )
1412 // NOTE: When a thread is removed from the global thread list its
1413 // main context is invalid and should be removed as well.
1414 // It is possible that t.m_curr could reference more
1415 // than just the main context if the thread exited abnormally
1416 // (if it was terminated), but we must assume that the user
1417 // retains a reference to them and that they may be re-used
1418 // elsewhere. Therefore, it is the responsibility of any
1419 // object that creates contexts to clean them up properly
1420 // when it is done with them.
1421 remove( &t.m_main );
1424 t.prev.next = t.next;
1426 t.next.prev = t.prev;
1431 // NOTE: Don't null out t.next or t.prev because opApply currently
1432 // follows t.next after removing a node. This could be easily
1433 // addressed by simply returning the next node from this
1434 // function, however, a thread should never be re-added to the
1435 // list anyway and having next and prev be non-null is a good way
1441 ///////////////////////////////////////////////////////////////////////////////
1442 // GC Support Routines
1443 ///////////////////////////////////////////////////////////////////////////////
1447 * Initializes the thread module. This function must be called by the
1448 * garbage collector on startup and before any other thread routines
1451 extern (C) void thread_init()
1453 // NOTE: If thread_init itself performs any allocations then the thread
1454 // routines reserved for garbage collector use may be called while
1455 // thread_init is being processed. However, since no memory should
1456 // exist to be scanned at this point, it is sufficient for these
1457 // functions to detect the condition and return immediately.
1461 Thread.sm_this = TlsAlloc();
1462 assert( Thread.sm_this != TLS_OUT_OF_INDEXES );
1464 else version( Posix )
1467 sigaction_t sigusr1 = void;
1468 sigaction_t sigusr2 = void;
1470 // This is a quick way to zero-initialize the structs without using
1471 // memset or creating a link dependency on their static initializer.
1472 (cast(byte*) &sigusr1)[0 .. sigaction_t.sizeof] = 0;
1473 (cast(byte*) &sigusr2)[0 .. sigaction_t.sizeof] = 0;
1475 // NOTE: SA_RESTART indicates that system calls should restart if they
1476 // are interrupted by a signal, but this is not available on all
1477 // Posix systems, even those that support multithreading.
1478 static if( is( typeof( SA_RESTART ) ) )
1479 sigusr1.sa_flags = SA_RESTART;
1481 sigusr1.sa_flags = 0;
1482 sigusr1.sa_handler = &thread_suspendHandler;
1483 // NOTE: We want to ignore all signals while in this handler, so fill
1484 // sa_mask to indicate this.
1485 status = sigfillset( &sigusr1.sa_mask );
1486 assert( status == 0 );
1488 // NOTE: Since SIGUSR2 should only be issued for threads within the
1489 // suspend handler, we don't want this signal to trigger a
1491 sigusr2.sa_flags = 0;
1492 sigusr2.sa_handler = &thread_resumeHandler;
1493 // NOTE: We want to ignore all signals while in this handler, so fill
1494 // sa_mask to indicate this.
1495 status = sigfillset( &sigusr2.sa_mask );
1496 assert( status == 0 );
1498 status = sigaction( SIGUSR1, &sigusr1, null );
1499 assert( status == 0 );
1501 status = sigaction( SIGUSR2, &sigusr2, null );
1502 assert( status == 0 );
1504 status = sem_init( &suspendCount, 0, 0 );
1505 assert( status == 0 );
1507 status = pthread_key_create( &Thread.sm_this, null );
1508 assert( status == 0 );
1511 thread_attachThis();
1516 * Registers the calling thread for use with the D Runtime. If this routine
1517 * is called for a thread which is already registered, the result is undefined.
1519 extern (C) void thread_attachThis()
1523 Thread thisThread = new Thread();
1524 Thread.Context* thisContext = &thisThread.m_main;
1525 assert( thisContext == thisThread.m_curr );
1527 thisThread.m_addr = GetCurrentThreadId();
1528 thisThread.m_hndl = GetCurrentThreadHandle();
1529 thisContext.bstack = getStackBottom();
1530 thisContext.tstack = thisContext.bstack;
1532 thisThread.m_isDaemon = true;
1534 Thread.setThis( thisThread );
1536 else version( Posix )
1538 Thread thisThread = new Thread();
1539 Thread.Context* thisContext = thisThread.m_curr;
1540 assert( thisContext == &thisThread.m_main );
1542 thisThread.m_addr = pthread_self();
1543 thisContext.bstack = getStackBottom();
1544 thisContext.tstack = thisContext.bstack;
1546 thisThread.m_isRunning = true;
1547 thisThread.m_isDaemon = true;
1549 Thread.setThis( thisThread );
1552 Thread.add( thisThread );
1553 Thread.add( thisContext );
1558 * Deregisters the calling thread from use with the runtime. If this routine
1559 * is called for a thread which is already registered, the result is undefined.
1561 extern (C) void thread_detachThis()
1563 Thread.remove( Thread.getThis() );
1568 * Joins all non-daemon threads that are currently running. This is done by
1569 * performing successive scans through the thread list until a scan consists
1570 * of only daemon threads.
1572 extern (C) void thread_joinAll()
1577 Thread nonDaemon = null;
1579 foreach( t; Thread )
1587 if( nonDaemon is null )
1595 * Performs intermediate shutdown of the thread module.
1599 // NOTE: The functionality related to garbage collection must be minimally
1600 // operable after this dtor completes. Therefore, only minimal
1601 // cleanup may occur.
1603 for( Thread t = Thread.sm_tbeg; t; t = t.next )
1611 // Used for needLock below
1612 private bool multiThreadedFlag = false;
1616 * This function is used to determine whether the the process is
1617 * multi-threaded. Optimizations may only be performed on this
1618 * value if the programmer can guarantee that no path from the
1619 * enclosed code will start a thread.
1622 * True if Thread.start() has been called in this process.
1624 extern (C) bool thread_needLock()
1626 return multiThreadedFlag;
1630 // Used for suspendAll/resumeAll below
1631 private uint suspendDepth = 0;
1635 * Suspend all threads but the calling thread for "stop the world" garbage
1636 * collection runs. This function may be called multiple times, and must
1637 * be followed by a matching number of calls to thread_resumeAll before
1638 * processing is resumed.
1641 * ThreadException if the suspend operation fails for a running thread.
1643 extern (C) void thread_suspendAll()
1646 * Suspend the specified thread and load stack and register information for
1647 * use by thread_scanAll. If the supplied thread is the calling thread,
1648 * stack and register information will be loaded but the thread will not
1649 * be suspended. If the suspend operation fails and the thread is not
1650 * running then it will be removed from the global thread list, otherwise
1651 * an exception will be thrown.
1654 * t = The thread to suspend.
1657 * ThreadException if the suspend operation fails for a running thread.
1659 void suspend( Thread t )
1663 if( t.m_addr != GetCurrentThreadId() && SuspendThread( t.m_hndl ) == 0xFFFFFFFF )
1670 throw new ThreadException( "Unable to suspend thread" );
1673 CONTEXT context = void;
1674 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
1676 if( !GetThreadContext( t.m_hndl, &context ) )
1677 throw new ThreadException( "Unable to load thread context" );
1679 t.m_curr.tstack = cast(void*) context.Esp;
1680 // edi,esi,ebp,esp,ebx,edx,ecx,eax
1681 t.m_reg[0] = context.Edi;
1682 t.m_reg[1] = context.Esi;
1683 t.m_reg[2] = context.Ebp;
1684 t.m_reg[3] = context.Esp;
1685 t.m_reg[4] = context.Ebx;
1686 t.m_reg[5] = context.Edx;
1687 t.m_reg[6] = context.Ecx;
1688 t.m_reg[7] = context.Eax;
1690 else version( Posix )
1692 if( t.m_addr != pthread_self() )
1694 if( pthread_kill( t.m_addr, SIGUSR1 ) != 0 )
1701 throw new ThreadException( "Unable to suspend thread" );
1703 // NOTE: It's really not ideal to wait for each thread to
1704 // signal individually -- rather, it would be better to
1705 // suspend them all and wait once at the end. However,
1706 // semaphores don't really work this way, and the obvious
1707 // alternative (looping on an atomic suspend count)
1708 // requires either the atomic module (which only works on
1709 // x86) or other specialized functionality. It would
1710 // also be possible to simply loop on sem_wait at the
1711 // end, but I'm not convinced that this would be much
1712 // faster than the current approach.
1713 sem_wait( &suspendCount );
1715 else if( !t.m_lock )
1717 t.m_curr.tstack = getStackTop();
1723 // NOTE: We've got an odd chicken & egg problem here, because while the GC
1724 // is required to call thread_init before calling any other thread
1725 // routines, thread_init may allocate memory which could in turn
1726 // trigger a collection. Thus, thread_suspendAll, thread_scanAll,
1727 // and thread_resumeAll must be callable before thread_init
1728 // completes, with the assumption that no other GC memory has yet
1729 // been allocated by the system, and thus there is no risk of losing
1730 // data if the global thread list is empty. The check of
1731 // Thread.sm_tbeg below is done to ensure thread_init has completed,
1732 // and therefore that calling Thread.getThis will not result in an
1733 // error. For the short time when Thread.sm_tbeg is null, there is
1734 // no reason not to simply call the multithreaded code below, with
1735 // the expectation that the foreach loop will never be entered.
1736 if( !multiThreadedFlag && Thread.sm_tbeg )
1738 if( ++suspendDepth == 1 )
1739 suspend( Thread.getThis() );
1742 synchronized( Thread.slock )
1744 if( ++suspendDepth > 1 )
1747 // NOTE: I'd really prefer not to check isRunning within this loop but
1748 // not doing so could be problematic if threads are termianted
1749 // abnormally and a new thread is created with the same thread
1750 // address before the next GC run. This situation might cause
1751 // the same thread to be suspended twice, which would likely
1752 // cause the second suspend to fail, the garbage collection to
1753 // abort, and Bad Things to occur.
1754 for( Thread t = Thread.sm_tbeg; t; t = t.next )
1764 // wait on semaphore -- see note in suspend for
1765 // why this is currently not implemented
1772 * Resume all threads but the calling thread for "stop the world" garbage
1773 * collection runs. This function must be called once for each preceding
1774 * call to thread_suspendAll before the threads are actually resumed.
1777 * This routine must be preceded by a call to thread_suspendAll.
1780 * ThreadException if the resume operation fails for a running thread.
1782 extern (C) void thread_resumeAll()
1785 assert( suspendDepth > 0 );
1790 * Resume the specified thread and unload stack and register information.
1791 * If the supplied thread is the calling thread, stack and register
1792 * information will be unloaded but the thread will not be resumed. If
1793 * the resume operation fails and the thread is not running then it will
1794 * be removed from the global thread list, otherwise an exception will be
1798 * t = The thread to resume.
1801 * ThreadException if the resume fails for a running thread.
1803 void resume( Thread t )
1807 if( t.m_addr != GetCurrentThreadId() && ResumeThread( t.m_hndl ) == 0xFFFFFFFF )
1814 throw new ThreadException( "Unable to resume thread" );
1818 t.m_curr.tstack = t.m_curr.bstack;
1819 t.m_reg[0 .. $] = 0;
1821 else version( Posix )
1823 if( t.m_addr != pthread_self() )
1825 if( pthread_kill( t.m_addr, SIGUSR2 ) != 0 )
1832 throw new ThreadException( "Unable to resume thread" );
1835 else if( !t.m_lock )
1837 t.m_curr.tstack = t.m_curr.bstack;
1843 // NOTE: See thread_suspendAll for the logic behind this.
1844 if( !multiThreadedFlag && Thread.sm_tbeg )
1846 if( --suspendDepth == 0 )
1847 resume( Thread.getThis() );
1850 synchronized( Thread.slock )
1852 if( --suspendDepth > 0 )
1855 for( Thread t = Thread.sm_tbeg; t; t = t.next )
1863 private alias void delegate( void*, void* ) scanAllThreadsFn;
1867 * The main entry point for garbage collection. The supplied delegate
1868 * will be passed ranges representing both stack and register values.
1871 * scan = The scanner function. It should scan from p1 through p2 - 1.
1872 * curStackTop = An optional pointer to the top of the calling thread's stack.
1875 * This routine must be preceded by a call to thread_suspendAll.
1877 extern (C) void thread_scanAll( scanAllThreadsFn scan, void* curStackTop = null )
1880 assert( suspendDepth > 0 );
1884 Thread thisThread = null;
1885 void* oldStackTop = null;
1887 if( curStackTop && Thread.sm_tbeg )
1889 thisThread = Thread.getThis();
1890 if( !thisThread.m_lock )
1892 oldStackTop = thisThread.m_curr.tstack;
1893 thisThread.m_curr.tstack = curStackTop;
1899 if( curStackTop && Thread.sm_tbeg )
1901 if( !thisThread.m_lock )
1903 thisThread.m_curr.tstack = oldStackTop;
1908 // NOTE: Synchronizing on Thread.slock is not needed because this
1909 // function may only be called after all other threads have
1910 // been suspended from within the same lock.
1911 for( Thread.Context* c = Thread.sm_cbeg; c; c = c.next )
1913 version( StackGrowsDown )
1915 // NOTE: We can't index past the bottom of the stack
1916 // so don't do the "+1" for StackGrowsDown.
1917 if( c.tstack && c.tstack < c.bstack )
1918 scan( c.tstack, c.bstack );
1922 if( c.bstack && c.bstack < c.tstack )
1923 scan( c.bstack, c.tstack + 1 );
1928 for( Thread t = Thread.sm_tbeg; t; t = t.next )
1930 scan( &t.m_reg[0], &t.m_reg[0] + t.m_reg.length );
1936 ///////////////////////////////////////////////////////////////////////////////
1938 ///////////////////////////////////////////////////////////////////////////////
1942 * This class encapsulates the operations required to initialize, access, and
1943 * destroy thread local data.
1945 class ThreadLocal( T )
1947 ///////////////////////////////////////////////////////////////////////////
1949 ///////////////////////////////////////////////////////////////////////////
1953 * Initializes thread local storage for the indicated value which will be
1954 * initialized to def for all threads.
1957 * def = The default value to return if no value has been explicitly set.
1959 this( T def = T.init )
1962 m_key = Thread.createLocal();
1968 Thread.deleteLocal( m_key );
1972 ///////////////////////////////////////////////////////////////////////////
1974 ///////////////////////////////////////////////////////////////////////////
1978 * Gets the value last set by the calling thread, or def if no such value
1982 * The stored value or def if no value is stored.
1986 Wrap* wrap = cast(Wrap*) Thread.getLocal( m_key );
1988 return wrap ? wrap.val : m_def;
1993 * Copies newval to a location specific to the calling thread, and returns
1997 * newval = The value to set.
2000 * The value passed to this function.
2004 Wrap* wrap = cast(Wrap*) Thread.getLocal( m_key );
2009 Thread.setLocal( m_key, wrap );
2018 // A wrapper for the stored data. This is needed for determining whether
2019 // set has ever been called for this thread (and therefore whether the
2020 // default value should be returned) and also to flatten the differences
2021 // between data that is smaller and larger than (void*).sizeof. The
2022 // obvious tradeoff here is an extra per-thread allocation for each
2023 // ThreadLocal value as compared to calling the Thread routines directly.
2036 ///////////////////////////////////////////////////////////////////////////////
2038 ///////////////////////////////////////////////////////////////////////////////
2042 * This class is intended to simplify certain common programming techniques.
2047 * Creates and starts a new Thread object that executes fn and adds it to
2048 * the list of tracked threads.
2051 * fn = The thread function.
2054 * A reference to the newly created thread.
2056 final Thread create( void function() fn )
2058 Thread t = new Thread( fn );
2061 synchronized( this )
2070 * Creates and starts a new Thread object that executes dg and adds it to
2071 * the list of tracked threads.
2074 * dg = The thread function.
2077 * A reference to the newly created thread.
2079 final Thread create( void delegate() dg )
2081 Thread t = new Thread( dg );
2084 synchronized( this )
2093 * Add t to the list of tracked threads if it is not already being tracked.
2096 * t = The thread to add.
2099 * t must not be null.
2101 final void add( Thread t )
2108 synchronized( this )
2116 * Removes t from the list of tracked threads. No operation will be
2117 * performed if t is not currently being tracked by this object.
2120 * t = The thread to remove.
2123 * t must not be null.
2125 final void remove( Thread t )
2132 synchronized( this )
2140 * Operates on all threads currently tracked by this object.
2142 final int opApply( int delegate( inout Thread ) dg )
2144 synchronized( this )
2148 // NOTE: This loop relies on the knowledge that m_all uses the
2149 // Thread object for both the key and the mapped value.
2150 foreach( Thread t; m_all.keys )
2162 * Iteratively joins all tracked threads. This function will block add,
2163 * remove, and opApply until it completes.
2166 * rethrow = Rethrow any unhandled exception which may have caused the
2167 * current thread to terminate.
2170 * Any exception not handled by the joined threads.
2172 final void joinAll( bool rethrow = true )
2174 synchronized( this )
2176 // NOTE: This loop relies on the knowledge that m_all uses the
2177 // Thread object for both the key and the mapped value.
2178 foreach( Thread t; m_all.keys )
2187 Thread[Thread] m_all;
2191 ///////////////////////////////////////////////////////////////////////////////
2192 // Fiber Platform Detection and Memory Allocation
2193 ///////////////////////////////////////////////////////////////////////////////
2198 version( D_InlineAsm_X86 )
2207 version = AsmX86_Win32;
2208 else version( Posix )
2209 version = AsmX86_Posix;
2215 version = AsmPPC_Posix;
2221 import stdc.posix.unistd; // for sysconf
2222 import stdc.posix.sys.mman; // for mmap
2223 import stdc.posix.stdlib; // for malloc, valloc, free
2225 version( AsmX86_Win32 ) {} else
2226 version( AsmX86_Posix ) {} else
2227 version( AsmPPC_Posix ) {} else
2229 // NOTE: The ucontext implementation requires architecture specific
2230 // data definitions to operate so testing for it must be done
2231 // by checking for the existence of ucontext_t rather than by
2232 // a version identifier. Please note that this is considered
2233 // an obsolescent feature according to the POSIX spec, so a
2234 // custom solution is still preferred.
2235 import stdc.posix.ucontext;
2239 const size_t PAGESIZE;
2245 static if( is( typeof( GetSystemInfo ) ) )
2248 GetSystemInfo( &info );
2250 PAGESIZE = info.dwPageSize;
2251 assert( PAGESIZE < int.max );
2253 else static if( is( typeof( sysconf ) ) &&
2254 is( typeof( _SC_PAGESIZE ) ) )
2256 PAGESIZE = cast(size_t) sysconf( _SC_PAGESIZE );
2257 assert( PAGESIZE < int.max );
2269 ///////////////////////////////////////////////////////////////////////////////
2270 // Fiber Entry Point and Context Switch
2271 ///////////////////////////////////////////////////////////////////////////////
2276 extern (C) void fiber_entryPoint()
2278 Fiber obj = Fiber.getThis();
2281 assert( Thread.getThis().m_curr is obj.m_ctxt );
2282 volatile Thread.getThis().m_lock = false;
2283 obj.m_ctxt.tstack = obj.m_ctxt.bstack;
2284 obj.m_state = Fiber.State.EXEC;
2292 obj.m_unhandled = o;
2295 static if( is( ucontext_t ) )
2296 obj.m_ucur = &obj.m_utxt;
2298 obj.m_state = Fiber.State.TERM;
2303 // NOTE: If AsmPPC_Posix is defined then the context switch routine will
2304 // be defined externally until GDC supports inline PPC ASM.
2305 version( AsmPPC_Posix )
2306 extern (C) void fiber_switchContext( void** oldp, void* newp );
2308 extern (C) void fiber_switchContext( void** oldp, void* newp )
2310 // NOTE: The data pushed and popped in this routine must match the
2311 // default stack created by Fiber.initStack or the initial
2312 // switch into a new context will fail.
2314 version( AsmX86_Win32 )
2320 // save current stack state
2324 push dword ptr FS:[0];
2325 push dword ptr FS:[4];
2326 push dword ptr FS:[8];
2331 // store oldp again with more accurate address
2332 mov EAX, dword ptr 8[EBP];
2334 // load newp to begin context switch
2335 mov ESP, dword ptr 12[EBP];
2337 // load saved state from new stack
2341 pop dword ptr FS:[8];
2342 pop dword ptr FS:[4];
2343 pop dword ptr FS:[0];
2347 // 'return' to complete switch
2351 else version( AsmX86_Posix )
2357 // save current stack state
2365 // store oldp again with more accurate address
2366 mov EAX, dword ptr 8[EBP];
2368 // load newp to begin context switch
2369 mov ESP, dword ptr 12[EBP];
2371 // load saved state from new stack
2378 // 'return' to complete switch
2382 else static if( is( ucontext_t ) )
2384 Fiber cfib = Fiber.getThis();
2385 void* ucur = cfib.m_ucur;
2388 swapcontext( **(cast(ucontext_t***) oldp),
2389 *(cast(ucontext_t**) newp) );
2395 ///////////////////////////////////////////////////////////////////////////////
2397 ///////////////////////////////////////////////////////////////////////////////
2401 * This class provides a cooperative concurrency mechanism integrated with the
2402 * threading and garbage collection functionality. Calling a fiber may be
2403 * considered a blocking operation that returns when the fiber yields (via
2404 * Fiber.yield()). Execution occurs within the context of the calling thread
2405 * so synchronization is not necessary to guarantee memory visibility so long
2406 * as the same thread calls the fiber each time. Please note that there is no
2407 * requirement that a fiber be bound to one specific thread. Rather, fibers
2408 * may be freely passed between threads so long as they are not currently
2409 * executing. Like threads, a new fiber thread may be created using either
2410 * derivation or composition, as in the following example.
2413 * ----------------------------------------------------------------------
2415 * class DerivedFiber : Fiber
2425 * printf( "Derived fiber running.\n" );
2431 * printf( "Composed fiber running.\n" );
2433 * printf( "Composed fiber running.\n" );
2436 * // create instances of each type
2437 * Fiber derived = new DerivedFiber();
2438 * Fiber composed = new Fiber( &fiberFunc );
2440 * // call both fibers once
2443 * printf( "Execution returned to calling context.\n" );
2446 * // since each fiber has run to completion, each should have state TERM
2447 * assert( derived.state == Fiber.State.TERM );
2448 * assert( composed.state == Fiber.State.TERM );
2450 * ----------------------------------------------------------------------
2452 * Authors: Based on a design by Mikola Lysenko.
2456 ///////////////////////////////////////////////////////////////////////////
2458 ///////////////////////////////////////////////////////////////////////////
2462 * Initializes a fiber object which is associated with a static
2466 * fn = The thread function.
2467 * sz = The stack size for this fiber.
2470 * fn must not be null.
2472 this( void function() fn, size_t sz = PAGESIZE )
2481 m_state = State.HOLD;
2488 * Initializes a fiber object which is associated with a dynamic
2492 * dg = The thread function.
2493 * sz = The stack size for this fiber.
2496 * dg must not be null.
2498 this( void delegate() dg, size_t sz = PAGESIZE )
2507 m_state = State.HOLD;
2514 * Cleans up any remaining resources used by this object.
2518 // NOTE: A live reference to this object will exist on its associated
2519 // stack from the first time its call() method has been called
2520 // until its execution completes with State.TERM. Thus, the only
2521 // times this dtor should be called are either if the fiber has
2522 // terminated (and therefore has no active stack) or if the user
2523 // explicitly deletes this object. The latter case is an error
2524 // but is not easily tested for, since State.HOLD may imply that
2525 // the fiber was just created but has never been run. There is
2526 // not a compelling case to create a State.INIT just to offer a
2527 // means of ensuring the user isn't violating this object's
2528 // contract, so for now this requirement will be enforced by
2529 // documentation only.
2534 ///////////////////////////////////////////////////////////////////////////
2536 ///////////////////////////////////////////////////////////////////////////
2540 * Transfers execution to this fiber object. The calling context will be
2541 * suspended until the fiber calls Fiber.yield() or until it terminates
2542 * via an unhandled exception.
2545 * rethrow = Rethrow any unhandled exception which may have caused this
2546 * fiber to terminate.
2549 * This fiber must be in state HOLD.
2552 * Any exception not handled by the joined thread.
2555 * Any exception not handled by this fiber if rethrow = false, null
2558 final Object call( bool rethrow = true )
2561 assert( m_state == State.HOLD );
2565 Fiber cur = getThis();
2567 static if( is( ucontext_t ) )
2568 m_ucur = cur ? &cur.m_utxt : &Fiber.sm_utxt;
2574 static if( is( ucontext_t ) )
2577 // NOTE: If the fiber has terminated then the stack pointers must be
2578 // reset. This ensures that the stack for this fiber is not
2579 // scanned if the fiber has terminated. This is necessary to
2580 // prevent any references lingering on the stack from delaying
2581 // the collection of otherwise dead objects. The most notable
2582 // being the current object, which is referenced at the top of
2583 // fiber_entryPoint.
2584 if( m_state == State.TERM )
2586 m_ctxt.tstack = m_ctxt.bstack;
2590 Object obj = m_unhandled;
2601 * Resets this fiber so that it may be re-used. This routine may only be
2602 * called for fibers that have terminated, as doing otherwise could result
2603 * in scope-dependent functionality that is not executed. Stack-based
2604 * classes, for example, may not be cleaned up properly if a fiber is reset
2605 * before it has terminated.
2608 * This fiber must be in state TERM.
2613 assert( m_state == State.TERM );
2614 assert( m_ctxt.tstack == m_ctxt.bstack );
2618 m_state = State.HOLD;
2624 ///////////////////////////////////////////////////////////////////////////
2625 // General Properties
2626 ///////////////////////////////////////////////////////////////////////////
2630 * A fiber may occupy one of three states: HOLD, EXEC, and TERM. The HOLD
2631 * state applies to any fiber that is suspended and ready to be called.
2632 * The EXEC state will be set for any fiber that is currently executing.
2633 * And the TERM state is set when a fiber terminates. Once a fiber
2634 * terminates, it must be reset before it may be called again.
2645 * Gets the current state of this fiber.
2648 * The state of this fiber as an enumerated value.
2656 ///////////////////////////////////////////////////////////////////////////
2657 // Actions on Calling Fiber
2658 ///////////////////////////////////////////////////////////////////////////
2662 * Forces a context switch to occur away from the calling fiber.
2666 Fiber cur = getThis();
2667 assert( cur, "Fiber.yield() called with no active fiber" );
2668 assert( cur.m_state == State.EXEC );
2670 static if( is( ucontext_t ) )
2671 cur.m_ucur = &cur.m_utxt;
2673 cur.m_state = State.HOLD;
2675 cur.m_state = State.EXEC;
2680 * Forces a context switch to occur away from the calling fiber and then
2681 * throws obj in the calling fiber.
2684 * obj = The object to throw.
2687 * obj must not be null.
2689 static void yieldAndThrow( Object obj )
2696 Fiber cur = getThis();
2697 assert( cur, "Fiber.yield() called with no active fiber" );
2698 assert( cur.m_state == State.EXEC );
2700 static if( is( ucontext_t ) )
2701 cur.m_ucur = &cur.m_utxt;
2703 cur.m_unhandled = obj;
2704 cur.m_state = State.HOLD;
2706 cur.m_state = State.EXEC;
2710 ///////////////////////////////////////////////////////////////////////////
2712 ///////////////////////////////////////////////////////////////////////////
2716 * Provides a reference to the calling fiber or null if no fiber is
2720 * The fiber object representing the calling fiber or null if no fiber
2721 * is currently active. The result of deleting this object is undefined.
2723 static Fiber getThis()
2727 return cast(Fiber) TlsGetValue( sm_this );
2729 else version( Posix )
2731 return cast(Fiber) pthread_getspecific( sm_this );
2736 ///////////////////////////////////////////////////////////////////////////
2737 // Static Initialization
2738 ///////////////////////////////////////////////////////////////////////////
2745 sm_this = TlsAlloc();
2746 assert( sm_this != TLS_OUT_OF_INDEXES );
2748 else version( Posix )
2752 status = pthread_key_create( &sm_this, null );
2753 assert( status == 0 );
2755 static if( is( ucontext_t ) )
2757 status = getcontext( &sm_utxt );
2758 assert( status == 0 );
2766 // Initializes a fiber object which has no associated executable function.
2775 // Fiber entry point. Invokes the function or delegate passed on
2776 // construction (if any).
2796 // The type of routine passed on fiber construction.
2807 // Standard fiber data
2812 void function() m_fn;
2813 void delegate() m_dg;
2821 ///////////////////////////////////////////////////////////////////////////
2823 ///////////////////////////////////////////////////////////////////////////
2827 // Allocate a new stack for this fiber.
2829 final void allocStack( size_t sz )
2832 assert( !m_pmem && !m_ctxt );
2836 // adjust alloc size to a multiple of PAGESIZE
2838 sz -= sz % PAGESIZE;
2840 // NOTE: This instance of Thread.Context is dynamic so Fiber objects
2841 // can be collected by the GC so long as no user level references
2842 // to the object exist. If m_ctxt were not dynamic then its
2843 // presence in the global context list would be enough to keep
2844 // this object alive indefinitely. An alternative to allocating
2845 // room for this struct explicitly would be to mash it into the
2846 // base of the stack being allocated below. However, doing so
2847 // requires too much special logic to be worthwhile.
2848 m_ctxt = new Thread.Context;
2850 static if( is( typeof( VirtualAlloc ) ) )
2852 // reserve memory for stack
2853 m_pmem = VirtualAlloc( null,
2859 throw new FiberException( "Unable to reserve memory for stack" );
2862 version( StackGrowsDown )
2864 void* stack = m_pmem + PAGESIZE;
2865 void* guard = m_pmem;
2866 void* pbase = stack + sz;
2870 void* stack = m_pmem;
2871 void* guard = m_pmem + sz;
2872 void* pbase = stack;
2875 // allocate reserved stack segment
2876 stack = VirtualAlloc( stack,
2882 throw new FiberException( "Unable to allocate memory for stack" );
2885 // allocate reserved guard page
2886 guard = VirtualAlloc( guard,
2889 PAGE_READWRITE | PAGE_GUARD );
2892 throw new FiberException( "Unable to create guard page for stack" );
2895 m_ctxt.bstack = pbase;
2896 m_ctxt.tstack = pbase;
2900 { static if( is( typeof( mmap ) ) )
2902 m_pmem = mmap( null,
2904 PROT_READ | PROT_WRITE,
2905 MAP_PRIVATE | MAP_ANON,
2908 if( m_pmem == MAP_FAILED )
2911 else static if( is( typeof( valloc ) ) )
2913 m_pmem = valloc( sz );
2915 else static if( is( typeof( malloc ) ) )
2917 m_pmem = malloc( sz );
2926 throw new FiberException( "Unable to allocate memory for stack" );
2929 version( StackGrowsDown )
2931 m_ctxt.bstack = m_pmem + sz;
2932 m_ctxt.tstack = m_pmem + sz;
2936 m_ctxt.bstack = m_pmem;
2937 m_ctxt.tstack = m_pmem;
2942 Thread.add( m_ctxt );
2947 // Free this fiber's stack.
2949 final void freeStack()
2952 assert( m_pmem && m_ctxt );
2956 // NOTE: Since this routine is only ever expected to be called from
2957 // the dtor, pointers to freed data are not set to null.
2959 // NOTE: m_ctxt is guaranteed to be alive because it is held in the
2960 // global context list.
2961 Thread.remove( m_ctxt );
2963 static if( is( typeof( VirtualAlloc ) ) )
2965 VirtualFree( m_pmem, 0, MEM_RELEASE );
2967 else static if( is( typeof( mmap ) ) )
2969 munmap( m_pmem, m_size );
2971 else static if( is( typeof( valloc ) ) )
2975 else static if( is( typeof( malloc ) ) )
2984 // Initialize the allocated stack.
2986 final void initStack()
2989 assert( m_ctxt.tstack && m_ctxt.tstack == m_ctxt.bstack );
2990 assert( cast(size_t) m_ctxt.bstack % (void*).sizeof == 0 );
2994 void* pstack = m_ctxt.tstack;
2995 scope( exit ) m_ctxt.tstack = pstack;
2997 void push( size_t val )
2999 version( StackGrowsDown )
3001 pstack -= size_t.sizeof;
3002 *(cast(size_t*) pstack) = val;
3006 pstack += size_t.sizeof;
3007 *(cast(size_t*) pstack) = val;
3011 // NOTE: On OS X the stack must be 16-byte aligned according to the
3015 pstack = cast(void*)(cast(uint)(pstack) - (cast(uint)(pstack) & 0x0F));
3018 version( AsmX86_Win32 )
3020 push( cast(size_t) &fiber_entryPoint ); // EIP
3021 push( 0xFFFFFFFF ); // EBP
3022 push( 0x00000000 ); // EAX
3023 push( 0xFFFFFFFF ); // FS:[0]
3024 version( StackGrowsDown )
3026 push( cast(size_t) m_ctxt.bstack ); // FS:[4]
3027 push( cast(size_t) m_ctxt.bstack - m_size ); // FS:[8]
3031 push( cast(size_t) m_ctxt.bstack ); // FS:[4]
3032 push( cast(size_t) m_ctxt.bstack + m_size ); // FS:[8]
3034 push( 0x00000000 ); // EBX
3035 push( 0x00000000 ); // ESI
3036 push( 0x00000000 ); // EDI
3038 else version( AsmX86_Posix )
3040 push( cast(size_t) &fiber_entryPoint ); // EIP
3041 push( 0x00000000 ); // EBP
3042 push( 0x00000000 ); // EAX
3043 push( 0x00000000 ); // EBX
3044 push( 0x00000000 ); // ESI
3045 push( 0x00000000 ); // EDI
3047 else version( AsmPPC_Posix )
3049 version( StackGrowsDown )
3051 pstack -= int.sizeof * 5;
3055 pstack += int.sizeof * 5;
3058 push( cast(size_t) &fiber_entryPoint ); // link register
3059 push( 0x00000000 ); // control register
3060 push( 0x00000000 ); // old stack pointer
3063 version( StackGrowsDown )
3065 pstack -= int.sizeof * 20;
3069 pstack += int.sizeof * 20;
3072 assert( cast(uint) pstack & 0x0f == 0 );
3074 else static if( is( ucontext_t ) )
3076 getcontext( &m_utxt );
3077 m_utxt.uc_stack.ss_sp = m_ctxt.bstack;
3078 m_utxt.uc_stack.ss_size = m_size;
3079 makecontext( &m_utxt, &fiber_entryPoint, 0 );
3080 // NOTE: If ucontext is being used then the top of the stack will
3081 // be a pointer to the ucontext_t struct for that fiber.
3082 push( cast(size_t) &m_utxt );
3087 Thread.Context* m_ctxt;
3091 static if( is( ucontext_t ) )
3093 // NOTE: The static ucontext instance is used to represent the context
3094 // of the main application thread.
3095 static ucontext_t sm_utxt = void;
3096 ucontext_t m_utxt = void;
3097 ucontext_t* m_ucur = null;
3102 ///////////////////////////////////////////////////////////////////////////
3103 // Storage of Active Fiber
3104 ///////////////////////////////////////////////////////////////////////////
3108 // Sets a thread-local reference to the current fiber object.
3110 static void setThis( Fiber f )
3114 TlsSetValue( sm_this, cast(void*) f );
3116 else version( Posix )
3118 pthread_setspecific( sm_this, cast(void*) f );
3123 static Thread.TLSKey sm_this;
3127 ///////////////////////////////////////////////////////////////////////////
3128 // Context Switching
3129 ///////////////////////////////////////////////////////////////////////////
3133 // Switches into the stack held by this fiber.
3135 final void switchIn()
3137 Thread tobj = Thread.getThis();
3138 void** oldp = &tobj.m_curr.tstack;
3139 void* newp = m_ctxt.tstack;
3141 // NOTE: The order of operations here is very important. The current
3142 // stack top must be stored before m_lock is set, and pushContext
3143 // must not be called until after m_lock is set. This process
3144 // is intended to prevent a race condition with the suspend
3145 // mechanism used for garbage collection. If it is not followed,
3146 // a badly timed collection could cause the GC to scan from the
3147 // bottom of one stack to the top of another, or to miss scanning
3148 // a stack that still contains valid data. The old stack pointer
3149 // oldp will be set again before the context switch to guarantee
3150 // that it points to exactly the correct stack location so the
3151 // successive pop operations will succeed.
3152 *oldp = getStackTop();
3153 volatile tobj.m_lock = true;
3154 tobj.pushContext( m_ctxt );
3156 fiber_switchContext( oldp, newp );
3158 // NOTE: As above, these operations must be performed in a strict order
3159 // to prevent Bad Things from happening.
3161 volatile tobj.m_lock = false;
3162 tobj.m_curr.tstack = tobj.m_curr.bstack;
3167 // Switches out of the current stack and into the enclosing stack.
3169 final void switchOut()
3171 Thread tobj = Thread.getThis();
3172 void** oldp = &m_ctxt.tstack;
3173 void* newp = tobj.m_curr.within.tstack;
3175 // NOTE: The order of operations here is very important. The current
3176 // stack top must be stored before m_lock is set, and pushContext
3177 // must not be called until after m_lock is set. This process
3178 // is intended to prevent a race condition with the suspend
3179 // mechanism used for garbage collection. If it is not followed,
3180 // a badly timed collection could cause the GC to scan from the
3181 // bottom of one stack to the top of another, or to miss scanning
3182 // a stack that still contains valid data. The old stack pointer
3183 // oldp will be set again before the context switch to guarantee
3184 // that it points to exactly the correct stack location so the
3185 // successive pop operations will succeed.
3186 *oldp = getStackTop();
3187 volatile tobj.m_lock = true;
3189 fiber_switchContext( oldp, newp );
3191 // NOTE: As above, these operations must be performed in a strict order
3192 // to prevent Bad Things from happening.
3193 volatile tobj.m_lock = false;
3194 tobj.m_curr.tstack = tobj.m_curr.bstack;