/src/core/thread.d
http://github.com/AlexeyProkhin/druntime · D · 4697 lines · 2890 code · 567 blank · 1240 comment · 347 complexity · 672b56f280060a7ede3f5dea172df202 MD5 · raw file
Large files are truncated click here to view the full file
- /**
- * The thread module provides support for thread creation and management.
- *
- * Copyright: Copyright Sean Kelly 2005 - 2012.
- * License: Distributed under the
- * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * (See accompanying file LICENSE)
- * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak
- * Source: $(DRUNTIMESRC core/_thread.d)
- */
- module core.thread;
- public import core.time; // for Duration
- static import rt.tlsgc;
- // this should be true for most architectures
- version = StackGrowsDown;
- /**
- * Returns the process ID of the calling process, which is guaranteed to be
- * unique on the system. This call is always successful.
- *
- * Example:
- * ---
- * writefln("Current process id: %s", getpid());
- * ---
- */
- version(Posix)
- {
- alias core.sys.posix.unistd.getpid getpid;
- }
- else version (Windows)
- {
- alias core.sys.windows.windows.GetCurrentProcessId getpid;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Thread and Fiber Exceptions
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * Base class for thread exceptions.
- */
- class ThreadException : Exception
- {
- this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
- {
- super(msg, file, line, next);
- }
- this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
- {
- super(msg, file, line, next);
- }
- }
- /**
- * Base class for fiber exceptions.
- */
- class FiberException : Exception
- {
- this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
- {
- super(msg, file, line, next);
- }
- this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
- {
- super(msg, file, line, next);
- }
- }
- private
- {
- import core.sync.mutex;
- import core.atomic;
- //
- // from core.memory
- //
- extern (C) void gc_enable();
- extern (C) void gc_disable();
- extern (C) void* gc_malloc(size_t sz, uint ba = 0);
- //
- // from core.stdc.string
- //
- extern (C) void* memcpy(void*, const void*, size_t);
- //
- // exposed by compiler runtime
- //
- extern (C) void rt_moduleTlsCtor();
- extern (C) void rt_moduleTlsDtor();
- alias void delegate() gc_atom;
- extern (C) void function(scope gc_atom) gc_atomic;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Thread Entry Point and Signal Handlers
- ///////////////////////////////////////////////////////////////////////////////
- version( Windows )
- {
- private
- {
- import core.stdc.stdint : uintptr_t; // for _beginthreadex decl below
- import core.stdc.stdlib; // for malloc, atexit
- import core.sys.windows.windows;
- import core.sys.windows.threadaux; // for OpenThreadHandle
- const DWORD TLS_OUT_OF_INDEXES = 0xFFFFFFFF;
- const CREATE_SUSPENDED = 0x00000004;
- extern (Windows) alias uint function(void*) btex_fptr;
- extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*);
- version( DigitalMars )
- {
- version (Win32)
- {
- // NOTE: The memory between the addresses of _tlsstart and _tlsend
- // is the storage for thread-local data in D 2.0. Both of
- // these are defined in dm\src\win32\tlsseg.asm by DMC.
- extern (C)
- {
- extern int _tlsstart;
- extern int _tlsend;
- }
- }
- version (Win64)
- {
- // NOTE: The memory between the addresses of _tls_start and _tls_end
- // is the storage for thread-local data in D 2.0. Both of
- // these are defined in LIBCMT:tlssub.obj
- extern (C)
- {
- extern int _tls_start;
- extern int _tls_end;
- }
- alias _tls_start _tlsstart;
- alias _tls_end _tlsend;
- }
- }
- else version (MinGW)
- {
- extern (C)
- {
- extern int _tls_start;
- extern int _tls_end;
- }
- alias _tls_start _tlsstart;
- alias _tls_end _tlsend;
- }
- else
- {
- __gshared int _tlsstart;
- alias _tlsstart _tlsend;
- }
- //
- // Entry point for Windows threads
- //
- extern (Windows) uint thread_entryPoint( void* arg )
- {
- Thread obj = cast(Thread) arg;
- assert( obj );
- assert( obj.m_curr is &obj.m_main );
- obj.m_main.bstack = getStackBottom();
- obj.m_main.tstack = obj.m_main.bstack;
- void* pstart = cast(void*) &_tlsstart;
- void* pend = cast(void*) &_tlsend;
- obj.m_tls = pstart[0 .. pend - pstart];
- Thread.setThis( obj );
- //Thread.add( obj );
- scope( exit )
- {
- Thread.remove( obj );
- }
- Thread.add( &obj.m_main );
- obj.m_tlsgcdata = rt.tlsgc.init();
- // NOTE: No GC allocations may occur until the stack pointers have
- // been set and Thread.getThis returns a valid reference to
- // this thread object (this latter condition is not strictly
- // necessary on Windows but it should be followed for the
- // sake of consistency).
- // TODO: Consider putting an auto exception object here (using
- // alloca) forOutOfMemoryError plus something to track
- // whether an exception is in-flight?
- void append( Throwable t )
- {
- if( obj.m_unhandled is null )
- obj.m_unhandled = t;
- else
- {
- Throwable last = obj.m_unhandled;
- while( last.next !is null )
- last = last.next;
- last.next = t;
- }
- }
- version( D_InlineAsm_X86 )
- {
- asm { fninit; }
- }
- try
- {
- rt_moduleTlsCtor();
- try
- {
- obj.run();
- }
- catch( Throwable t )
- {
- append( t );
- }
- rt_moduleTlsDtor();
- }
- catch( Throwable t )
- {
- append( t );
- }
- return 0;
- }
- HANDLE GetCurrentThreadHandle()
- {
- const uint DUPLICATE_SAME_ACCESS = 0x00000002;
- HANDLE curr = GetCurrentThread(),
- proc = GetCurrentProcess(),
- hndl;
- DuplicateHandle( proc, curr, proc, &hndl, 0, TRUE, DUPLICATE_SAME_ACCESS );
- return hndl;
- }
- }
- }
- else version( Posix )
- {
- private
- {
- import core.stdc.errno;
- import core.sys.posix.semaphore;
- import core.sys.posix.stdlib; // for malloc, valloc, free, atexit
- import core.sys.posix.pthread;
- import core.sys.posix.signal;
- import core.sys.posix.time;
- version( OSX )
- {
- import core.sys.osx.mach.thread_act;
- extern (C) mach_port_t pthread_mach_thread_np(pthread_t);
- }
- version( GNU )
- {
- import gcc.builtins;
- }
- version( DigitalMars )
- {
- version( linux )
- {
- extern (C)
- {
- extern int _tlsstart;
- extern int _tlsend;
- }
- }
- else version( OSX )
- {
- extern (C)
- {
- __gshared void[][2] _tls_data_array;
- }
- }
- else version( FreeBSD )
- {
- extern (C)
- {
- extern void* _tlsstart;
- extern void* _tlsend;
- }
- }
- else
- {
- __gshared int _tlsstart;
- alias _tlsstart _tlsend;
- }
- }
- else version (LDC)
- {
- version = LDC_NoTlsBracketSyms;
- }
- else
- {
- __gshared int _tlsstart;
- alias _tlsstart _tlsend;
- }
- //
- // Entry point for POSIX threads
- //
- extern (C) void* thread_entryPoint( void* arg )
- {
- Thread obj = cast(Thread) arg;
- assert( obj );
- assert( obj.m_curr is &obj.m_main );
- obj.m_main.bstack = getStackBottom();
- obj.m_main.tstack = obj.m_main.bstack;
- version (LDC_NoTlsBracketSyms)
- {
- version (OSX)
- {
- import ldc.memory;
- obj.m_tls = getCurrentTLSRange();
- }
- else
- {
- // Nothing to do here for Linux – glibc allocates the TLS
- // data at the start of the stack area, so we scan it anyway.
- }
- }
- else version(OSX)
- {
- // NOTE: OSX does not support TLS, so we do it ourselves. The TLS
- // data output by the compiler is bracketed by _tls_data_array[2],
- // so make a copy of it for each thread.
- const sz0 = (_tls_data_array[0].length + 15) & ~cast(size_t)15;
- const sz2 = sz0 + _tls_data_array[1].length;
- auto p = malloc( sz2 );
- assert( p );
- obj.m_tls = p[0 .. sz2];
- memcpy( p, _tls_data_array[0].ptr, _tls_data_array[0].length );
- memcpy( p + sz0, _tls_data_array[1].ptr, _tls_data_array[1].length );
- scope (exit) { free( p ); obj.m_tls = null; }
- }
- else
- {
- auto pstart = cast(void*) &_tlsstart;
- auto pend = cast(void*) &_tlsend;
- obj.m_tls = pstart[0 .. pend - pstart];
- }
- obj.m_isRunning = true;
- Thread.setThis( obj );
- //Thread.add( obj );
- scope( exit )
- {
- // NOTE: isRunning should be set to false after the thread is
- // removed or a double-removal could occur between this
- // function and thread_suspendAll.
- Thread.remove( obj );
- obj.m_isRunning = false;
- }
- Thread.add( &obj.m_main );
- obj.m_tlsgcdata = rt.tlsgc.init();
- static extern (C) void thread_cleanupHandler( void* arg ) nothrow
- {
- Thread obj = cast(Thread) arg;
- assert( obj );
- // NOTE: If the thread terminated abnormally, just set it as
- // not running and let thread_suspendAll remove it from
- // the thread list. This is safer and is consistent
- // with the Windows thread code.
- obj.m_isRunning = false;
- }
- // NOTE: Using void to skip the initialization here relies on
- // knowledge of how pthread_cleanup is implemented. It may
- // not be appropriate for all platforms. However, it does
- // avoid the need to link the pthread module. If any
- // implementation actually requires default initialization
- // then pthread_cleanup should be restructured to maintain
- // the current lack of a link dependency.
- static if( __traits( compiles, pthread_cleanup ) )
- {
- pthread_cleanup cleanup = void;
- cleanup.push( &thread_cleanupHandler, cast(void*) obj );
- }
- else static if( __traits( compiles, pthread_cleanup_push ) )
- {
- pthread_cleanup_push( &thread_cleanupHandler, cast(void*) obj );
- }
- else
- {
- static assert( false, "Platform not supported." );
- }
- // NOTE: No GC allocations may occur until the stack pointers have
- // been set and Thread.getThis returns a valid reference to
- // this thread object (this latter condition is not strictly
- // necessary on Windows but it should be followed for the
- // sake of consistency).
- // TODO: Consider putting an auto exception object here (using
- // alloca) forOutOfMemoryError plus something to track
- // whether an exception is in-flight?
- void append( Throwable t )
- {
- if( obj.m_unhandled is null )
- obj.m_unhandled = t;
- else
- {
- Throwable last = obj.m_unhandled;
- while( last.next !is null )
- last = last.next;
- last.next = t;
- }
- }
- try
- {
- rt_moduleTlsCtor();
- try
- {
- obj.run();
- }
- catch( Throwable t )
- {
- append( t );
- }
- rt_moduleTlsDtor();
- }
- catch( Throwable t )
- {
- append( t );
- }
- // NOTE: Normal cleanup is handled by scope(exit).
- static if( __traits( compiles, pthread_cleanup ) )
- {
- cleanup.pop( 0 );
- }
- else static if( __traits( compiles, pthread_cleanup_push ) )
- {
- pthread_cleanup_pop( 0 );
- }
- return null;
- }
- //
- // Used to track the number of suspended threads
- //
- __gshared sem_t suspendCount;
- extern (C) void thread_suspendHandler( int sig )
- in
- {
- assert( sig == SIGUSR1 );
- }
- body
- {
- void op(void* sp)
- {
- // NOTE: Since registers are being pushed and popped from the
- // stack, any other stack data used by this function should
- // be gone before the stack cleanup code is called below.
- Thread obj = Thread.getThis();
- // NOTE: The thread reference returned by getThis is set within
- // the thread startup code, so it is possible that this
- // handler may be called before the reference is set. In
- // this case it is safe to simply suspend and not worry
- // about the stack pointers as the thread will not have
- // any references to GC-managed data.
- if( obj && !obj.m_lock )
- {
- obj.m_curr.tstack = getStackTop();
- }
- sigset_t sigres = void;
- int status;
- status = sigfillset( &sigres );
- assert( status == 0 );
- status = sigdelset( &sigres, SIGUSR2 );
- assert( status == 0 );
- status = sem_post( &suspendCount );
- assert( status == 0 );
- sigsuspend( &sigres );
- if( obj && !obj.m_lock )
- {
- obj.m_curr.tstack = obj.m_curr.bstack;
- }
- }
- callWithStackShell(&op);
- }
- extern (C) void thread_resumeHandler( int sig )
- in
- {
- assert( sig == SIGUSR2 );
- }
- body
- {
- }
- }
- }
- else
- {
- // NOTE: This is the only place threading versions are checked. If a new
- // version is added, the module code will need to be searched for
- // places where version-specific code may be required. This can be
- // easily accomlished by searching for 'Windows' or 'Posix'.
- static assert( false, "Unknown threading implementation." );
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Thread
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * This class encapsulates all threading functionality for the D
- * programming language. As thread manipulation is a required facility
- * for garbage collection, all user threads should derive from this
- * class, and instances of this class should never be explicitly deleted.
- * A new thread may be created using either derivation or composition, as
- * in the following example.
- *
- * Example:
- * ----------------------------------------------------------------------------
- *
- * class DerivedThread : Thread
- * {
- * this()
- * {
- * super( &run );
- * }
- *
- * private :
- * void run()
- * {
- * printf( "Derived thread running.\n" );
- * }
- * }
- *
- * void threadFunc()
- * {
- * printf( "Composed thread running.\n" );
- * }
- *
- * // create instances of each type
- * Thread derived = new DerivedThread();
- * Thread composed = new Thread( &threadFunc );
- *
- * // start both threads
- * derived.start();
- * composed.start();
- *
- * ----------------------------------------------------------------------------
- */
- class Thread
- {
- ///////////////////////////////////////////////////////////////////////////
- // Initialization
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Initializes a thread object which is associated with a static
- * D function.
- *
- * Params:
- * fn = The thread function.
- * sz = The stack size for this thread.
- *
- * In:
- * fn must not be null.
- */
- this( void function() fn, size_t sz = 0 )
- in
- {
- assert( fn );
- }
- body
- {
- this();
- m_fn = fn;
- m_sz = sz;
- m_call = Call.FN;
- m_curr = &m_main;
- }
- /**
- * Initializes a thread object which is associated with a dynamic
- * D function.
- *
- * Params:
- * dg = The thread function.
- * sz = The stack size for this thread.
- *
- * In:
- * dg must not be null.
- */
- this( void delegate() dg, size_t sz = 0 )
- in
- {
- assert( dg );
- }
- body
- {
- this();
- m_dg = dg;
- m_sz = sz;
- m_call = Call.DG;
- m_curr = &m_main;
- }
- /**
- * Cleans up any remaining resources used by this object.
- */
- ~this()
- {
- if( m_addr == m_addr.init )
- {
- return;
- }
- version( Windows )
- {
- m_addr = m_addr.init;
- CloseHandle( m_hndl );
- m_hndl = m_hndl.init;
- }
- else version( Posix )
- {
- pthread_detach( m_addr );
- m_addr = m_addr.init;
- }
- version( OSX )
- {
- m_tmach = m_tmach.init;
- }
- rt.tlsgc.destroy( m_tlsgcdata );
- m_tlsgcdata = null;
- }
- ///////////////////////////////////////////////////////////////////////////
- // General Actions
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Starts the thread and invokes the function or delegate passed upon
- * construction.
- *
- * In:
- * This routine may only be called once per thread instance.
- *
- * Throws:
- * ThreadException if the thread fails to start.
- */
- final void start()
- in
- {
- assert( !next && !prev );
- }
- body
- {
- auto wasThreaded = multiThreadedFlag;
- multiThreadedFlag = true;
- scope( failure )
- {
- if( !wasThreaded )
- multiThreadedFlag = false;
- }
- version( Windows ) {} else
- version( Posix )
- {
- pthread_attr_t attr;
- if( pthread_attr_init( &attr ) )
- throw new ThreadException( "Error initializing thread attributes" );
- if( m_sz && pthread_attr_setstacksize( &attr, m_sz ) )
- throw new ThreadException( "Error initializing thread stack size" );
- if( pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) )
- throw new ThreadException( "Error setting thread joinable" );
- }
- version( Windows )
- {
- // NOTE: If a thread is just executing DllMain()
- // while another thread is started here, it holds an OS internal
- // lock that serializes DllMain with CreateThread. As the code
- // might request a synchronization on slock (e.g. in thread_findByAddr()),
- // we cannot hold that lock while creating the thread without
- // creating a deadlock
- //
- // Solution: Create the thread in suspended state and then
- // add and resume it with slock acquired
- assert(m_sz <= uint.max, "m_sz must be less than or equal to uint.max");
- m_hndl = cast(HANDLE) _beginthreadex( null, cast(uint) m_sz, &thread_entryPoint, cast(void*) this, CREATE_SUSPENDED, &m_addr );
- if( cast(size_t) m_hndl == 0 )
- throw new ThreadException( "Error creating thread" );
- }
- // NOTE: The starting thread must be added to the global thread list
- // here rather than within thread_entryPoint to prevent a race
- // with the main thread, which could finish and terminat the
- // app without ever knowing that it should have waited for this
- // starting thread. In effect, not doing the add here risks
- // having thread being treated like a daemon thread.
- synchronized( slock )
- {
- version( Windows )
- {
- if( ResumeThread( m_hndl ) == -1 )
- throw new ThreadException( "Error resuming thread" );
- }
- else version( Posix )
- {
- // NOTE: This is also set to true by thread_entryPoint, but set it
- // here as well so the calling thread will see the isRunning
- // state immediately.
- m_isRunning = true;
- scope( failure ) m_isRunning = false;
- if( pthread_create( &m_addr, &attr, &thread_entryPoint, cast(void*) this ) != 0 )
- throw new ThreadException( "Error creating thread" );
- }
- version( OSX )
- {
- m_tmach = pthread_mach_thread_np( m_addr );
- if( m_tmach == m_tmach.init )
- throw new ThreadException( "Error creating thread" );
- }
- // NOTE: when creating threads from inside a DLL, DllMain(THREAD_ATTACH)
- // might be called before ResumeThread returns, but the dll
- // helper functions need to know whether the thread is created
- // from the runtime itself or from another DLL or the application
- // to just attach to it
- // as a consequence, the new Thread object is added before actual
- // creation of the thread. There should be no problem with the GC
- // calling thread_suspendAll, because of the slock synchronization
- //
- // VERIFY: does this actually also apply to other platforms?
- add( this );
- }
- }
- /**
- * Waits for this thread to complete. If the thread terminated as the
- * result of an unhandled exception, this exception will be rethrown.
- *
- * Params:
- * rethrow = Rethrow any unhandled exception which may have caused this
- * thread to terminate.
- *
- * Throws:
- * ThreadException if the operation fails.
- * Any exception not handled by the joined thread.
- *
- * Returns:
- * Any exception not handled by this thread if rethrow = false, null
- * otherwise.
- */
- final Throwable join( bool rethrow = true )
- {
- version( Windows )
- {
- if( WaitForSingleObject( m_hndl, INFINITE ) != WAIT_OBJECT_0 )
- throw new ThreadException( "Unable to join thread" );
- // NOTE: m_addr must be cleared before m_hndl is closed to avoid
- // a race condition with isRunning. The operation is done
- // with atomicStore to prevent compiler reordering.
- atomicStore!(MemoryOrder.raw)(*cast(shared)&m_addr, m_addr.init);
- CloseHandle( m_hndl );
- m_hndl = m_hndl.init;
- }
- else version( Posix )
- {
- if( pthread_join( m_addr, null ) != 0 )
- throw new ThreadException( "Unable to join thread" );
- // NOTE: pthread_join acts as a substitute for pthread_detach,
- // which is normally called by the dtor. Setting m_addr
- // to zero ensures that pthread_detach will not be called
- // on object destruction.
- m_addr = m_addr.init;
- }
- if( m_unhandled )
- {
- if( rethrow )
- throw m_unhandled;
- return m_unhandled;
- }
- return null;
- }
- ///////////////////////////////////////////////////////////////////////////
- // General Properties
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Gets the user-readable label for this thread.
- *
- * Returns:
- * The name of this thread.
- */
- final @property string name()
- {
- synchronized( this )
- {
- return m_name;
- }
- }
- /**
- * Sets the user-readable label for this thread.
- *
- * Params:
- * val = The new name of this thread.
- */
- final @property void name( string val )
- {
- synchronized( this )
- {
- m_name = val;
- }
- }
- /**
- * Gets the daemon status for this thread. While the runtime will wait for
- * all normal threads to complete before tearing down the process, daemon
- * threads are effectively ignored and thus will not prevent the process
- * from terminating. In effect, daemon threads will be terminated
- * automatically by the OS when the process exits.
- *
- * Returns:
- * true if this is a daemon thread.
- */
- final @property bool isDaemon()
- {
- synchronized( this )
- {
- return m_isDaemon;
- }
- }
- /**
- * Sets the daemon status for this thread. While the runtime will wait for
- * all normal threads to complete before tearing down the process, daemon
- * threads are effectively ignored and thus will not prevent the process
- * from terminating. In effect, daemon threads will be terminated
- * automatically by the OS when the process exits.
- *
- * Params:
- * val = The new daemon status for this thread.
- */
- final @property void isDaemon( bool val )
- {
- synchronized( this )
- {
- m_isDaemon = val;
- }
- }
- /**
- * Tests whether this thread is running.
- *
- * Returns:
- * true if the thread is running, false if not.
- */
- final @property bool isRunning()
- {
- if( m_addr == m_addr.init )
- {
- return false;
- }
- version( Windows )
- {
- uint ecode = 0;
- GetExitCodeThread( m_hndl, &ecode );
- return ecode == STILL_ACTIVE;
- }
- else version( Posix )
- {
- // NOTE: It should be safe to access this value without
- // memory barriers because word-tearing and such
- // really isn't an issue for boolean values.
- return m_isRunning;
- }
- }
- ///////////////////////////////////////////////////////////////////////////
- // Thread Priority Actions
- ///////////////////////////////////////////////////////////////////////////
- /**
- * The minimum scheduling priority that may be set for a thread. On
- * systems where multiple scheduling policies are defined, this value
- * represents the minimum valid priority for the scheduling policy of
- * the process.
- */
- __gshared const int PRIORITY_MIN;
- /**
- * The maximum scheduling priority that may be set for a thread. On
- * systems where multiple scheduling policies are defined, this value
- * represents the minimum valid priority for the scheduling policy of
- * the process.
- */
- __gshared const int PRIORITY_MAX;
- /**
- * Gets the scheduling priority for the associated thread.
- *
- * Returns:
- * The scheduling priority of this thread.
- */
- final @property int priority()
- {
- version( Windows )
- {
- return GetThreadPriority( m_hndl );
- }
- else version( Posix )
- {
- int policy;
- sched_param param;
- if( pthread_getschedparam( m_addr, &policy, ¶m ) )
- throw new ThreadException( "Unable to get thread priority" );
- return param.sched_priority;
- }
- }
- /**
- * Sets the scheduling priority for the associated thread.
- *
- * Params:
- * val = The new scheduling priority of this thread.
- */
- final @property void priority( int val )
- {
- version( Windows )
- {
- if( !SetThreadPriority( m_hndl, val ) )
- throw new ThreadException( "Unable to set thread priority" );
- }
- else version( Posix )
- {
- // NOTE: pthread_setschedprio is not implemented on linux, so use
- // the more complicated get/set sequence below.
- //if( pthread_setschedprio( m_addr, val ) )
- // throw new ThreadException( "Unable to set thread priority" );
- int policy;
- sched_param param;
- if( pthread_getschedparam( m_addr, &policy, ¶m ) )
- throw new ThreadException( "Unable to set thread priority" );
- param.sched_priority = val;
- if( pthread_setschedparam( m_addr, policy, ¶m ) )
- throw new ThreadException( "Unable to set thread priority" );
- }
- }
- ///////////////////////////////////////////////////////////////////////////
- // Actions on Calling Thread
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Suspends the calling thread for at least the supplied period. This may
- * result in multiple OS calls if period is greater than the maximum sleep
- * duration supported by the operating system.
- *
- * Params:
- * val = The minimum duration the calling thread should be suspended.
- *
- * In:
- * period must be non-negative.
- *
- * Example:
- * ------------------------------------------------------------------------
- *
- * Thread.sleep( dur!("msecs")( 50 ) ); // sleep for 50 milliseconds
- * Thread.sleep( dur!("seconds")( 5 ) ); // sleep for 5 seconds
- *
- * ------------------------------------------------------------------------
- */
- static void sleep( Duration val )
- in
- {
- assert( !val.isNegative );
- }
- body
- {
- version( Windows )
- {
- auto maxSleepMillis = dur!("msecs")( uint.max - 1 );
- // NOTE: In instances where all other threads in the process have a
- // lower priority than the current thread, the current thread
- // will not yield with a sleep time of zero. However, unlike
- // yield(), the user is not asking for a yield to occur but
- // only for execution to suspend for the requested interval.
- // Therefore, expected performance may not be met if a yield
- // is forced upon the user.
- while( val > maxSleepMillis )
- {
- Sleep( cast(uint)
- maxSleepMillis.total!"msecs" );
- val -= maxSleepMillis;
- }
- Sleep( cast(uint) val.total!"msecs" );
- }
- else version( Posix )
- {
- timespec tin = void;
- timespec tout = void;
- if( val.total!"seconds" > tin.tv_sec.max )
- {
- tin.tv_sec = tin.tv_sec.max;
- tin.tv_nsec = cast(typeof(tin.tv_nsec)) val.fracSec.nsecs;
- }
- else
- {
- tin.tv_sec = cast(typeof(tin.tv_sec)) val.total!"seconds";
- tin.tv_nsec = cast(typeof(tin.tv_nsec)) val.fracSec.nsecs;
- }
- while( true )
- {
- if( !nanosleep( &tin, &tout ) )
- return;
- if( errno != EINTR )
- throw new ThreadException( "Unable to sleep for the specified duration" );
- tin = tout;
- }
- }
- }
- /**
- * $(RED Deprecated. It will be removed in December 2012. Please use the
- * version which takes a $(D Duration) instead.)
- *
- * Suspends the calling thread for at least the supplied period. This may
- * result in multiple OS calls if period is greater than the maximum sleep
- * duration supported by the operating system.
- *
- * Params:
- * period = The minimum duration the calling thread should be suspended,
- * in 100 nanosecond intervals.
- *
- * In:
- * period must be non-negative.
- *
- * Example:
- * ------------------------------------------------------------------------
- *
- * Thread.sleep( 500_000 ); // sleep for 50 milliseconds
- * Thread.sleep( 50_000_000 ); // sleep for 5 seconds
- *
- * ------------------------------------------------------------------------
- */
- deprecated("Please use the overload of sleep which takes a Duration.")
- static void sleep( long period )
- in
- {
- assert( period >= 0 );
- }
- body
- {
- sleep( dur!"hnsecs"( period ) );
- }
- /**
- * Forces a context switch to occur away from the calling thread.
- */
- static void yield()
- {
- version( Windows )
- SwitchToThread();
- else version( Posix )
- sched_yield();
- }
- ///////////////////////////////////////////////////////////////////////////
- // Thread Accessors
- ///////////////////////////////////////////////////////////////////////////
- /**
- * Provides a reference to the calling thread.
- *
- * Returns:
- * The thread object representing the calling thread. The result of
- * deleting this object is undefined. If the current thread is not
- * attached to the runtime, a null reference is returned.
- */
- static Thread getThis()
- {
- // NOTE: This function may not be called until thread_init has
- // completed. See thread_suspendAll for more information
- // on why this might occur.
- version( Windows )
- {
- auto t = cast(Thread) TlsGetValue( sm_this );
- // NOTE: If this thread was attached via thread_attachByAddr then
- // this TLS lookup won't initially be set, so when the TLS
- // lookup fails, try an exhaustive search.
- if( t is null )
- {
- t = thread_findByAddr( GetCurrentThreadId() );
- setThis( t );
- }
- return t;
- }
- else version( Posix )
- {
- auto t = cast(Thread) pthread_getspecific( sm_this );
- // NOTE: See the comment near thread_findByAddr() for why the
- // secondary thread_findByAddr lookup can't be done on
- // Posix. However, because thread_attachByAddr() is for
- // Windows only, the secondary lookup is pointless anyway.
- return t;
- }
- }
- /**
- * Provides a list of all threads currently being tracked by the system.
- *
- * Returns:
- * An array containing references to all threads currently being
- * tracked by the system. The result of deleting any contained
- * objects is undefined.
- */
- static Thread[] getAll()
- {
- synchronized( slock )
- {
- size_t pos = 0;
- Thread[] buf = new Thread[sm_tlen];
- foreach( Thread t; Thread )
- {
- buf[pos++] = t;
- }
- return buf;
- }
- }
- /**
- * Operates on all threads currently being tracked by the system. The
- * result of deleting any Thread object is undefined.
- *
- * Params:
- * dg = The supplied code as a delegate.
- *
- * Returns:
- * Zero if all elemented are visited, nonzero if not.
- */
- static int opApply( scope int delegate( ref Thread ) dg )
- {
- synchronized( slock )
- {
- int ret = 0;
- for( Thread t = sm_tbeg; t; t = t.next )
- {
- ret = dg( t );
- if( ret )
- break;
- }
- return ret;
- }
- }
- ///////////////////////////////////////////////////////////////////////////
- // Static Initalizer
- ///////////////////////////////////////////////////////////////////////////
- /**
- * This initializer is used to set thread constants. All functional
- * initialization occurs within thread_init().
- */
- shared static this()
- {
- version( Windows )
- {
- PRIORITY_MIN = -15;
- PRIORITY_MAX = 15;
- }
- else version( Posix )
- {
- int policy;
- sched_param param;
- pthread_t self = pthread_self();
- int status = pthread_getschedparam( self, &policy, ¶m );
- assert( status == 0 );
- PRIORITY_MIN = sched_get_priority_min( policy );
- assert( PRIORITY_MIN != -1 );
- PRIORITY_MAX = sched_get_priority_max( policy );
- assert( PRIORITY_MAX != -1 );
- }
- }
- ///////////////////////////////////////////////////////////////////////////
- // Stuff That Should Go Away
- ///////////////////////////////////////////////////////////////////////////
- private:
- //
- // Initializes a thread object which has no associated executable function.
- // This is used for the main thread initialized in thread_init().
- //
- this()
- {
- m_call = Call.NO;
- m_curr = &m_main;
- version (LDC_NoTlsBracketSyms) {} else
- version (OSX)
- {
- //printf("test2 %p %p\n", _tls_data_array[0].ptr, &_tls_data_array[1][length]);
- //printf("test2 %p %p\n", &_tls_beg, &_tls_end);
- // NOTE: OSX does not support TLS, so we do it ourselves. The TLS
- // data output by the compiler is bracketed by _tls_data_array2],
- // so make a copy of it for each thread.
- const sz0 = (_tls_data_array[0].length + 15) & ~cast(size_t)15;
- const sz2 = sz0 + _tls_data_array[1].length;
- auto p = malloc( sz2 );
- assert( p );
- m_tls = p[0 .. sz2];
- memcpy( p, _tls_data_array[0].ptr, _tls_data_array[0].length );
- memcpy( p + sz0, _tls_data_array[1].ptr, _tls_data_array[1].length );
- // The free must happen at program end, if anywhere.
- }
- else
- {
- auto pstart = cast(void*) &_tlsstart;
- auto pend = cast(void*) &_tlsend;
- m_tls = pstart[0 .. pend - pstart];
- }
- }
- //
- // Thread entry point. Invokes the function or delegate passed on
- // construction (if any).
- //
- final void run()
- {
- switch( m_call )
- {
- case Call.FN:
- m_fn();
- break;
- case Call.DG:
- m_dg();
- break;
- default:
- break;
- }
- }
- private:
- //
- // The type of routine passed on thread construction.
- //
- enum Call
- {
- NO,
- FN,
- DG
- }
- //
- // Standard types
- //
- version( Windows )
- {
- alias uint TLSKey;
- alias uint ThreadAddr;
- }
- else version( Posix )
- {
- alias pthread_key_t TLSKey;
- alias pthread_t ThreadAddr;
- }
- //
- // Local storage
- //
- __gshared TLSKey sm_this;
- //
- // Main process thread
- //
- __gshared Thread sm_main;
- //
- // Standard thread data
- //
- version( Windows )
- {
- HANDLE m_hndl;
- }
- else version( OSX )
- {
- mach_port_t m_tmach;
- }
- ThreadAddr m_addr;
- Call m_call;
- string m_name;
- union
- {
- void function() m_fn;
- void delegate() m_dg;
- }
- size_t m_sz;
- version( Posix )
- {
- bool m_isRunning;
- }
- bool m_isDaemon;
- bool m_isInCriticalRegion;
- Throwable m_unhandled;
- private:
- ///////////////////////////////////////////////////////////////////////////
- // Storage of Active Thread
- ///////////////////////////////////////////////////////////////////////////
- //
- // Sets a thread-local reference to the current thread object.
- //
- static void setThis( Thread t )
- {
- version( Windows )
- {
- TlsSetValue( sm_this, cast(void*) t );
- }
- else version( Posix )
- {
- pthread_setspecific( sm_this, cast(void*) t );
- }
- }
- private:
- ///////////////////////////////////////////////////////////////////////////
- // Thread Context and GC Scanning Support
- ///////////////////////////////////////////////////////////////////////////
- final void pushContext( Context* c )
- in
- {
- assert( !c.within );
- }
- body
- {
- c.within = m_curr;
- m_curr = c;
- }
- final void popContext()
- in
- {
- assert( m_curr && m_curr.within );
- }
- body
- {
- Context* c = m_curr;
- m_curr = c.within;
- c.within = null;
- }
- final Context* topContext()
- in
- {
- assert( m_curr );
- }
- body
- {
- return m_curr;
- }
- static struct Context
- {
- void* bstack,
- tstack;
- Context* within;
- Context* next,
- prev;
- }
- Context m_main;
- Context* m_curr;
- bool m_lock;
- void[] m_tls; // spans implicit thread local storage
- rt.tlsgc.Data* m_tlsgcdata;
- version( Windows )
- {
- version( X86 )
- {
- uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax
- }
- else version( X86_64 )
- {
- ulong[16] m_reg; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax
- // r8,r9,r10,r11,r12,r13,r14,r15
- }
- else
- {
- static assert(false, "Architecture not supported." );
- }
- }
- else version( OSX )
- {
- version( X86 )
- {
- uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax
- }
- else version( X86_64 )
- {
- ulong[16] m_reg; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax
- // r8,r9,r10,r11,r12,r13,r14,r15
- }
- else
- {
- static assert(false, "Architecture not supported." );
- }
- }
- private:
- ///////////////////////////////////////////////////////////////////////////
- // GC Scanning Support
- ///////////////////////////////////////////////////////////////////////////
- // NOTE: The GC scanning process works like so:
- //
- // 1. Suspend all threads.
- // 2. Scan the stacks of all suspended threads for roots.
- // 3. Resume all threads.
- //
- // Step 1 and 3 require a list of all threads in the system, while
- // step 2 requires a list of all thread stacks (each represented by
- // a Context struct). Traditionally, there was one stack per thread
- // and the Context structs were not necessary. However, Fibers have
- // changed things so that each thread has its own 'main' stack plus
- // an arbitrary number of nested stacks (normally referenced via
- // m_curr). Also, there may be 'free-floating' stacks in the system,
- // which are Fibers that are not currently executing on any specific
- // thread but are still being processed and still contain valid
- // roots.
- //
- // To support all of this, the Context struct has been created to
- // represent a stack range, and a global list of Context structs has
- // been added to enable scanning of these stack ranges. The lifetime
- // (and presence in the Context list) of a thread's 'main' stack will
- // be equivalent to the thread's lifetime. So the Ccontext will be
- // added to the list on thread entry, and removed from the list on
- // thread exit (which is essentially the same as the presence of a
- // Thread object in its own global list). The lifetime of a Fiber's
- // context, however, will be tied to the lifetime of the Fiber object
- // itself, and Fibers are expected to add/remove their Context struct
- // on construction/deletion.
- //
- // All use of the global lists should synchronize on this lock.
- //
- @property static Mutex slock()
- {
- __gshared Mutex m;
- __gshared byte[__traits(classInstanceSize, Mutex)] ms;
- if (m is null)
- {
- // Initialization doesn't need to be synchronized because
- // creating a thread will lock this mutex.
- ms[] = Mutex.classinfo.init[];
- m = cast(Mutex)ms.ptr;
- m.__ctor();
- extern(C) void destroy() { m.__dtor(); }
- atexit(&destroy);
- }
- return m;
- }
- __gshared Context* sm_cbeg;
- __gshared size_t sm_clen;
- __gshared Thread sm_tbeg;
- __gshared size_t sm_tlen;
- //
- // Used for ordering threads in the global thread list.
- //
- Thread prev;
- Thread next;
- ///////////////////////////////////////////////////////////////////////////
- // Global Context List Operations
- ///////////////////////////////////////////////////////////////////////////
- //
- // Add a context to the global context list.
- //
- static void add( Context* c )
- in
- {
- assert( c );
- assert( !c.next && !c.prev );
- }
- body
- {
- // NOTE: This loop is necessary to avoid a race between newly created
- // threads and the GC. If a collection starts between the time
- // Thread.start is called and the new thread calls Thread.add,
- // the thread will have its stack scanned without first having
- // been properly suspended. Testing has shown this to sometimes
- // cause a deadlock.
- while( true )
- {
- synchronized( slock )
- {
- if( !suspendDepth )
- {
- if( sm_cbeg )
- {
- c.next = sm_cbeg;
- sm_cbeg.prev = c;
- }
- sm_cbeg = c;
- ++sm_clen;
- return;
- }
- }
- yield();
- }
- }
- //
- // Remove a context from the global context list.
- //
- static void remove( Context* c )
- in
- {
- assert( c );
- assert( c.next || c.prev );
- }
- body
- {
- synchronized( slock )
- {
- if( c.prev )
- c.prev.next = c.next;
- if( c.next )
- c.next.prev = c.prev;
- if( sm_cbeg == c )
- sm_cbeg = c.next;
- --sm_clen;
- }
- // NOTE: Don't null out c.next or c.prev because opApply currently
- // follows c.next after removing a node. This could be easily
- // addressed by simply returning the next node from this
- // function, however, a context should never be re-added to the
- // list anyway and having next and prev be non-null is a good way
- // to ensure that.
- }
- ///////////////////////////////////////////////////////////////////////////
- // Global Thread List Operations
- ///////////////////////////////////////////////////////////////////////////
- //
- // Add a thread to the global thread list.
- //
- static void add( Thread t )
- in
- {
- assert( t );
- assert( !t.next && !t.prev );
- //assert( t.isRunning );
- }
- body
- {
- // NOTE: This loop is necessary to avoid a race between newly created
- // threads and the GC. If a collection starts between the time
- // Thread.start is called and the new thread calls Thread.add,
- // the thread could manipulate global state while the collection
- // is running, and by being…