/** * The threadbase module provides OS-independent code * for thread storage 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/threadbase.d) */ /* NOTE: This file has been patched from the original DMD distribution to * work with the GDC compiler. */module core.thread.threadbase; import core.thread.context;import core.thread.types;import core.time;import core.sync.mutex;import core.stdc.stdlib : free, realloc; private{ import core.internal.traits : externDFunc; // interface to rt.tlsgc alias rt_tlsgc_init = externDFunc!("rt.tlsgc.init", void* function() nothrow @nogc); alias rt_tlsgc_destroy = externDFunc!("rt.tlsgc.destroy", void function(void*) nothrow @nogc); alias ScanDg = void delegate(void* pstart, void* pend) nothrow; alias rt_tlsgc_scan = externDFunc!("rt.tlsgc.scan", void function(void*, scope ScanDg) nothrow);} ///////////////////////////////////////////////////////////////////////////////// Thread and Fiber Exceptions/////////////////////////////////////////////////////////////////////////////// /** * Base class for thread exceptions. */class ThreadException : Exception{ @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) { super(msg, file, line, next); } @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, next); }} /*** Base class for thread errors to be used for function inside GC when allocations are unavailable.*/class ThreadError : Error{ @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) { super(msg, file, line, next); } @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, next); }} private{ // Handling unaligned mutexes are not supported on all platforms, so we must // ensure that the address of all shared data are appropriately aligned. enum mutexAlign = __traits(classInstanceAlignment, Mutex); enum mutexClassInstanceSize = __traits(classInstanceSize, Mutex); alias swapContext = externDFunc!("core.thread.osthread.swapContext", void* function(void*) nothrow @nogc);} package{ alias getStackBottom = externDFunc!("core.thread.osthread.getStackBottom", void* function() nothrow @nogc); alias getStackTop = externDFunc!("core.thread.osthread.getStackTop", void* function() nothrow @nogc);} ///////////////////////////////////////////////////////////////////////////////// Thread/////////////////////////////////////////////////////////////////////////////// class ThreadBase{ /////////////////////////////////////////////////////////////////////////// // Initialization /////////////////////////////////////////////////////////////////////////// this(void function() fn, size_t sz = 0) @safe pure nothrow @nogc in(fn) { this(sz); m_call = fn; } this(void delegate() dg, size_t sz = 0) @trusted pure nothrow @nogc in( cast(const void delegate()) dg) { this(sz); m_call = dg; } /** * Cleans up any remaining resources used by this object. */ package bool destructBeforeDtor() nothrow @nogc { destroyDataStorageIfAvail(); bool no_context = m_addr == m_addr.init; bool not_registered = !next && !prev && (sm_tbeg !is this); return (no_context || not_registered); } ref void* tlsGCData() nothrow @nogc { return m_tlsgcdata; } package void tlsRTdataInit() nothrow @nogc { m_tlsrtdata = rt_tlsgc_init(); // Let the selected GC initialize anything it needs. import core.internal.gc.proxy : gc_getProxy; gc_getProxy().initThread(this); } package void initDataStorage() nothrow { assert(m_curr is &m_main); m_main.bstack = getStackBottom(); m_main.tstack = m_main.bstack; tlsRTdataInit(); } package void destroyDataStorage() nothrow @nogc { rt_tlsgc_destroy(m_tlsrtdata); m_tlsrtdata = null; } package void destroyDataStorageIfAvail() nothrow @nogc { if (m_tlsrtdata) destroyDataStorage(); } /////////////////////////////////////////////////////////////////////////// // General Actions /////////////////////////////////////////////////////////////////////////// /** * 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. */ abstract Throwable join(bool rethrow = true); /** * Filter any exceptions that escaped the thread entry point. * This enables a 'grave digger' approach to exceptions. * * By default this method will call the global handler in core.exception. * * Overriding this method allows a per-thread behavior. * * Params: * thrown = The thrown exception, may be null after returned. */ void filterCaughtThrowable(ref Throwable thrown) @system nothrow { import core.exception : filterThreadThrowableHandler; if (thrown is null) return; else if (auto handler = filterThreadThrowableHandler()) handler(thrown); } /////////////////////////////////////////////////////////////////////////// // General Properties /////////////////////////////////////////////////////////////////////////// /** * Gets the OS identifier for this thread. * * Returns: * If the thread hasn't been started yet, returns $(LREF ThreadID)$(D.init). * Otherwise, returns the result of $(D GetCurrentThreadId) on Windows, * and $(D pthread_self) on POSIX. * * The value is unique for the current process. */ final @property ThreadID id() @safe @nogc { synchronized(this) { return m_addr; } } /** * Gets the user-readable label for this thread. * * Returns: * The name of this thread. */ final @property string name() @safe @nogc { 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) @safe @nogc { 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() @safe @nogc { 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) @safe @nogc { synchronized(this) { m_isDaemon = val; } } /** * Tests whether this thread is the main thread, i.e. the thread * that initialized the runtime * * Returns: * true if the thread is the main thread */ final @property bool isMainThread() nothrow @nogc { return this is sm_main; } /** * Tests whether this thread is running. * * Returns: * true if the thread is running, false if not. */ @property bool isRunning() nothrow @nogc { if (m_addr == m_addr.init) return false; return true; } /////////////////////////////////////////////////////////////////////////// // 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 ThreadBase getThis() @safe nothrow @nogc { // NOTE: This function may not be called until thread_init has // completed. See thread_suspendAll for more information // on why this might occur. version (GNU) pragma(inline, false); return sm_this; } /** * Provides a list of all threads currently being tracked by the system. * Note that threads in the returned array might no longer run (see * $(D ThreadBase.)$(LREF isRunning)). * * Returns: * An array containing references to all threads currently being * tracked by the system. The result of deleting any contained * objects is undefined. */ static ThreadBase[] getAll() { static void resize(ref ThreadBase[] buf, size_t nlen) { buf.length = nlen; } return getAllImpl!resize(); } /** * Operates on all threads currently being tracked by the system. The * result of deleting any Thread object is undefined. * Note that threads passed to the callback might no longer run (see * $(D ThreadBase.)$(LREF isRunning)). * * 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 ThreadBase) dg) { static void resize(ref ThreadBase[] buf, size_t nlen) { import core.exception: onOutOfMemoryError; auto newBuf = cast(ThreadBase*)realloc(buf.ptr, nlen * size_t.sizeof); if (newBuf is null) onOutOfMemoryError(); buf = newBuf[0 .. nlen]; } auto buf = getAllImpl!resize; scope(exit) if (buf.ptr) free(buf.ptr); foreach (t; buf) { if (auto res = dg(t)) return res; } return 0; } private static ThreadBase[] getAllImpl(alias resize)() { import core.atomic; ThreadBase[] buf; while (true) { immutable len = atomicLoad!(MemoryOrder.raw)(*cast(shared)&sm_tlen); resize(buf, len); assert(buf.length == len); synchronized (slock) { if (len == sm_tlen) { size_t pos; for (ThreadBase t = sm_tbeg; t; t = t.next) buf[pos++] = t; return buf; } } } } /////////////////////////////////////////////////////////////////////////// // Actions on Calling Thread /////////////////////////////////////////////////////////////////////////// /** * Forces a context switch to occur away from the calling thread. */ private static void yield() @nogc nothrow { thread_yield(); } /////////////////////////////////////////////////////////////////////////// // Stuff That Should Go Away /////////////////////////////////////////////////////////////////////////// // // Initializes a thread object which has no associated executable function. // This is used for the main thread initialized in thread_init(). // package this(size_t sz = 0) @safe pure nothrow @nogc { m_sz = sz; m_curr = &m_main; } // // Thread entry point. Invokes the function or delegate passed on // construction (if any). // package final void run() { m_call(); } package: // // Local storage // static ThreadBase sm_this; // // Main process thread // __gshared ThreadBase sm_main; // // Standard thread data // ThreadID m_addr; Callable m_call; string m_name; size_t m_sz; bool m_isDaemon; Throwable m_unhandled; /////////////////////////////////////////////////////////////////////////// // Storage of Active Thread /////////////////////////////////////////////////////////////////////////// // // Sets a thread-local reference to the current thread object. // package static void setThis(ThreadBase t) nothrow @nogc { sm_this = t; } package(core.thread): Lines 1–500 of 1335. Load more
/** * The threadbase module provides OS-independent code * for thread storage 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/threadbase.d) */ /* NOTE: This file has been patched from the original DMD distribution to * work with the GDC compiler. */ module core.thread.threadbase; import core.thread.context; import core.thread.types; import core.time; import core.sync.mutex; import core.stdc.stdlib : free, realloc; private { import core.internal.traits : externDFunc; // interface to rt.tlsgc alias rt_tlsgc_init = externDFunc!("rt.tlsgc.init", void* function() nothrow @nogc); alias rt_tlsgc_destroy = externDFunc!("rt.tlsgc.destroy", void function(void*) nothrow @nogc); alias ScanDg = void delegate(void* pstart, void* pend) nothrow; alias rt_tlsgc_scan = externDFunc!("rt.tlsgc.scan", void function(void*, scope ScanDg) nothrow); } /////////////////////////////////////////////////////////////////////////////// // Thread and Fiber Exceptions /////////////////////////////////////////////////////////////////////////////// /** * Base class for thread exceptions. */ class ThreadException : Exception { @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) { super(msg, file, line, next); } @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, next); } } /** * Base class for thread errors to be used for function inside GC when allocations are unavailable. */ class ThreadError : Error { @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) { super(msg, file, line, next); } @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, next); } } private { // Handling unaligned mutexes are not supported on all platforms, so we must // ensure that the address of all shared data are appropriately aligned. enum mutexAlign = __traits(classInstanceAlignment, Mutex); enum mutexClassInstanceSize = __traits(classInstanceSize, Mutex); alias swapContext = externDFunc!("core.thread.osthread.swapContext", void* function(void*) nothrow @nogc); } package { alias getStackBottom = externDFunc!("core.thread.osthread.getStackBottom", void* function() nothrow @nogc); alias getStackTop = externDFunc!("core.thread.osthread.getStackTop", void* function() nothrow @nogc); } /////////////////////////////////////////////////////////////////////////////// // Thread /////////////////////////////////////////////////////////////////////////////// class ThreadBase { /////////////////////////////////////////////////////////////////////////// // Initialization /////////////////////////////////////////////////////////////////////////// this(void function() fn, size_t sz = 0) @safe pure nothrow @nogc in(fn) { this(sz); m_call = fn; } this(void delegate() dg, size_t sz = 0) @trusted pure nothrow @nogc in( cast(const void delegate()) dg) { this(sz); m_call = dg; } /** * Cleans up any remaining resources used by this object. */ package bool destructBeforeDtor() nothrow @nogc { destroyDataStorageIfAvail(); bool no_context = m_addr == m_addr.init; bool not_registered = !next && !prev && (sm_tbeg !is this); return (no_context || not_registered); } ref void* tlsGCData() nothrow @nogc { return m_tlsgcdata; } package void tlsRTdataInit() nothrow @nogc { m_tlsrtdata = rt_tlsgc_init(); // Let the selected GC initialize anything it needs. import core.internal.gc.proxy : gc_getProxy; gc_getProxy().initThread(this); } package void initDataStorage() nothrow { assert(m_curr is &m_main); m_main.bstack = getStackBottom(); m_main.tstack = m_main.bstack; tlsRTdataInit(); } package void destroyDataStorage() nothrow @nogc { rt_tlsgc_destroy(m_tlsrtdata); m_tlsrtdata = null; } package void destroyDataStorageIfAvail() nothrow @nogc { if (m_tlsrtdata) destroyDataStorage(); } /////////////////////////////////////////////////////////////////////////// // General Actions /////////////////////////////////////////////////////////////////////////// /** * 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. */ abstract Throwable join(bool rethrow = true); /** * Filter any exceptions that escaped the thread entry point. * This enables a 'grave digger' approach to exceptions. * * By default this method will call the global handler in core.exception. * * Overriding this method allows a per-thread behavior. * * Params: * thrown = The thrown exception, may be null after returned. */ void filterCaughtThrowable(ref Throwable thrown) @system nothrow { import core.exception : filterThreadThrowableHandler; if (thrown is null) return; else if (auto handler = filterThreadThrowableHandler()) handler(thrown); } /////////////////////////////////////////////////////////////////////////// // General Properties /////////////////////////////////////////////////////////////////////////// /** * Gets the OS identifier for this thread. * * Returns: * If the thread hasn't been started yet, returns $(LREF ThreadID)$(D.init). * Otherwise, returns the result of $(D GetCurrentThreadId) on Windows, * and $(D pthread_self) on POSIX. * * The value is unique for the current process. */ final @property ThreadID id() @safe @nogc { synchronized(this) { return m_addr; } } /** * Gets the user-readable label for this thread. * * Returns: * The name of this thread. */ final @property string name() @safe @nogc { 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) @safe @nogc { 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() @safe @nogc { 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) @safe @nogc { synchronized(this) { m_isDaemon = val; } } /** * Tests whether this thread is the main thread, i.e. the thread * that initialized the runtime * * Returns: * true if the thread is the main thread */ final @property bool isMainThread() nothrow @nogc { return this is sm_main; } /** * Tests whether this thread is running. * * Returns: * true if the thread is running, false if not. */ @property bool isRunning() nothrow @nogc { if (m_addr == m_addr.init) return false; return true; } /////////////////////////////////////////////////////////////////////////// // 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 ThreadBase getThis() @safe nothrow @nogc { // NOTE: This function may not be called until thread_init has // completed. See thread_suspendAll for more information // on why this might occur. version (GNU) pragma(inline, false); return sm_this; } /** * Provides a list of all threads currently being tracked by the system. * Note that threads in the returned array might no longer run (see * $(D ThreadBase.)$(LREF isRunning)). * * Returns: * An array containing references to all threads currently being * tracked by the system. The result of deleting any contained * objects is undefined. */ static ThreadBase[] getAll() { static void resize(ref ThreadBase[] buf, size_t nlen) { buf.length = nlen; } return getAllImpl!resize(); } /** * Operates on all threads currently being tracked by the system. The * result of deleting any Thread object is undefined. * Note that threads passed to the callback might no longer run (see * $(D ThreadBase.)$(LREF isRunning)). * * 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 ThreadBase) dg) { static void resize(ref ThreadBase[] buf, size_t nlen) { import core.exception: onOutOfMemoryError; auto newBuf = cast(ThreadBase*)realloc(buf.ptr, nlen * size_t.sizeof); if (newBuf is null) onOutOfMemoryError(); buf = newBuf[0 .. nlen]; } auto buf = getAllImpl!resize; scope(exit) if (buf.ptr) free(buf.ptr); foreach (t; buf) { if (auto res = dg(t)) return res; } return 0; } private static ThreadBase[] getAllImpl(alias resize)() { import core.atomic; ThreadBase[] buf; while (true) { immutable len = atomicLoad!(MemoryOrder.raw)(*cast(shared)&sm_tlen); resize(buf, len); assert(buf.length == len); synchronized (slock) { if (len == sm_tlen) { size_t pos; for (ThreadBase t = sm_tbeg; t; t = t.next) buf[pos++] = t; return buf; } } } } /////////////////////////////////////////////////////////////////////////// // Actions on Calling Thread /////////////////////////////////////////////////////////////////////////// /** * Forces a context switch to occur away from the calling thread. */ private static void yield() @nogc nothrow { thread_yield(); } /////////////////////////////////////////////////////////////////////////// // Stuff That Should Go Away /////////////////////////////////////////////////////////////////////////// // // Initializes a thread object which has no associated executable function. // This is used for the main thread initialized in thread_init(). // package this(size_t sz = 0) @safe pure nothrow @nogc { m_sz = sz; m_curr = &m_main; } // // Thread entry point. Invokes the function or delegate passed on // construction (if any). // package final void run() { m_call(); } package: // // Local storage // static ThreadBase sm_this; // // Main process thread // __gshared ThreadBase sm_main; // // Standard thread data // ThreadID m_addr; Callable m_call; string m_name; size_t m_sz; bool m_isDaemon; Throwable m_unhandled; /////////////////////////////////////////////////////////////////////////// // Storage of Active Thread /////////////////////////////////////////////////////////////////////////// // // Sets a thread-local reference to the current thread object. // package static void setThis(ThreadBase t) nothrow @nogc { sm_this = t; } package(core.thread):