/** * The osthread module provides low-level, OS-dependent code * 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/osthread.d) */ module core.thread.osthread; import core.atomic;import core.exception : onOutOfMemoryError;import core.internal.traits : externDFunc;import core.memory : GC, pageSize;import core.thread.context;import core.thread.threadbase;import core.thread.types;import core.time; ///////////////////////////////////////////////////////////////////////////////// Platform Detection and Memory Allocation/////////////////////////////////////////////////////////////////////////////// version (OSX) version = Darwin;else version (iOS) version = Darwin;else version (TVOS) version = Darwin;else version (WatchOS) version = Darwin; version (D_InlineAsm_X86){ version (Windows) version = AsmX86_Windows; else version (Posix) version = AsmX86_Posix;}else version (D_InlineAsm_X86_64){ version (Windows) { version = AsmX86_64_Windows; } else version (Posix) { version = AsmX86_64_Posix; }} version (Windows){ import core.stdc.stdint : uintptr_t; // for _beginthreadex decl below import core.stdc.stdlib : free, malloc, realloc; import core.sys.windows.basetsd /+: HANDLE+/; import core.sys.windows.threadaux /+: getThreadStackBottom, impersonate_thread, OpenThreadHandle+/; import core.sys.windows.winbase /+: CloseHandle, CREATE_SUSPENDED, DuplicateHandle, GetCurrentThread, GetCurrentThreadId, GetCurrentProcess, GetExitCodeThread, GetSystemInfo, GetThreadContext, GetThreadPriority, INFINITE, ResumeThread, SetThreadPriority, Sleep, STILL_ACTIVE, SuspendThread, SwitchToThread, SYSTEM_INFO, THREAD_PRIORITY_IDLE, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_TIME_CRITICAL, WAIT_OBJECT_0, WaitForSingleObject+/; import core.sys.windows.windef /+: TRUE+/; import core.sys.windows.winnt /+: CONTEXT, CONTEXT_CONTROL, CONTEXT_INTEGER+/; private extern (Windows) alias btex_fptr = uint function(void*); private extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*) nothrow @nogc;}else version (Posix){ static import core.sys.posix.pthread; static import core.sys.posix.signal; import core.stdc.errno : EINTR, errno; import core.sys.posix.pthread : pthread_atfork, pthread_attr_destroy, pthread_attr_getstack, pthread_attr_init, pthread_attr_setstacksize, pthread_create, pthread_detach, pthread_getschedparam, pthread_join, pthread_self, pthread_setschedparam, sched_get_priority_max, sched_get_priority_min, sched_param, sched_yield; import core.sys.posix.semaphore : sem_init, sem_post, sem_t, sem_wait; import core.sys.posix.signal : pthread_kill, sigaction, sigaction_t, sigdelset, sigfillset, sigset_t, sigsuspend, SIGUSR1, stack_t; import core.sys.posix.stdlib : free, malloc, realloc; import core.sys.posix.sys.types : pthread_attr_t, pthread_key_t, pthread_t; import core.sys.posix.time : nanosleep, timespec; version (Darwin) { // Use macOS threads for suspend/resume import core.sys.darwin.mach.kern_return : KERN_SUCCESS; import core.sys.darwin.mach.port : mach_port_t; import core.sys.darwin.mach.thread_act : mach_msg_type_number_t, thread_get_state, thread_resume, thread_suspend; import core.sys.darwin.pthread : pthread_mach_thread_np; version (X86) { import core.sys.darwin.mach.thread_act : x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT, x86_thread_state32_t; } else version (X86_64) { import core.sys.darwin.mach.thread_act : x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT, x86_thread_state64_t; } else version (AArch64) { import core.sys.darwin.mach.thread_act : ARM_THREAD_STATE64, ARM_THREAD_STATE64_COUNT, arm_thread_state64_t; } else version (PPC) { import core.sys.darwin.mach.thread_act : PPC_THREAD_STATE, PPC_THREAD_STATE_COUNT, ppc_thread_state_t; } else version (PPC64) { import core.sys.darwin.mach.thread_act : PPC_THREAD_STATE64, PPC_THREAD_STATE64_COUNT, ppc_thread_state64_t; } } else version (Solaris) { // Use Solaris threads for suspend/resume import core.sys.posix.sys.wait : idtype_t; import core.sys.solaris.sys.priocntl : PC_CLNULL, PC_GETCLINFO, PC_GETPARMS, PC_SETPARMS, pcinfo_t, pcparms_t, priocntl; import core.sys.solaris.sys.types : P_MYID, pri_t; import core.sys.solaris.thread : thr_stksegment, thr_suspend, thr_continue; import core.sys.solaris.sys.procfs : PR_STOPPED, lwpstatus_t; } else { // Use POSIX threads for suspend/resume }} version (GNU){ import gcc.builtins;} /** * Hook for whatever EH implementation is used to save/restore some data * per stack. * * Params: * newContext = The return value of the prior call to this function * where the stack was last swapped out, or null when a fiber stack * is switched in for the first time. */private extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc; version (DigitalMars){ version (Windows) { extern(D) void* swapContext(void* newContext) nothrow @nogc { return _d_eh_swapContext(newContext); } } else { extern(C) void* _d_eh_swapContextDwarf(void* newContext) nothrow @nogc; extern(D) void* swapContext(void* newContext) nothrow @nogc { /* Detect at runtime which scheme is being used. * Eventually, determine it statically. */ static int which = 0; final switch (which) { case 0: { assert(newContext == null); auto p = _d_eh_swapContext(newContext); auto pdwarf = _d_eh_swapContextDwarf(newContext); if (p) { which = 1; return p; } else if (pdwarf) { which = 2; return pdwarf; } return null; } case 1: return _d_eh_swapContext(newContext); case 2: return _d_eh_swapContextDwarf(newContext); } } }}else{ extern(D) void* swapContext(void* newContext) nothrow @nogc { return _d_eh_swapContext(newContext); }} ///////////////////////////////////////////////////////////////////////////////// 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. */class Thread : ThreadBase{ // // Standard thread data // version (Windows) { private HANDLE m_hndl; } version (Posix) { private shared bool m_isRunning; } version (Darwin) { private mach_port_t m_tmach; } version (Solaris) { private __gshared bool m_isRTClass; } // // Standard types // version (Windows) { alias TLSKey = uint; } else version (Posix) { alias TLSKey = pthread_key_t; } /////////////////////////////////////////////////////////////////////////// // 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 ) @safe pure nothrow @nogc { super(fn, sz); } /** * 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 ) @safe pure nothrow @nogc { super(dg, sz); } package this( size_t sz = 0 ) @safe pure nothrow @nogc { super(sz); } /** * Cleans up any remaining resources used by this object. */ ~this() nothrow @nogc { if (super.destructBeforeDtor()) return; version (Windows) { m_addr = m_addr.init; CloseHandle( m_hndl ); m_hndl = m_hndl.init; } else version (Posix) { if (m_addr != m_addr.init) pthread_detach( m_addr ); m_addr = m_addr.init; } version (Darwin) { m_tmach = m_tmach.init; } } // // Thread entry point. Invokes the function or delegate passed on // construction (if any). // private final void run() { super.run(); } /** * 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() @safe nothrow @nogc { return ThreadBase.getThis().toThread; } /////////////////////////////////////////////////////////////////////////// // Thread Context and GC Scanning Support /////////////////////////////////////////////////////////////////////////// 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 (Darwin) { 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 version (AArch64) { ulong[33] m_reg; // x0-x31, pc } else version (ARM) { uint[16] m_reg; // r0-r15 } else version (PPC) { // Make the assumption that we only care about non-fp and non-vr regs. // ??? : it seems plausible that a valid address can be copied into a VR. uint[32] m_reg; // r0-31 } else version (PPC64) { // As above. ulong[32] m_reg; // r0-31 } else { static assert(false, "Architecture not supported." ); } } else version (Solaris) { 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 version (SPARC) { int[33] m_reg; // g0-7, o0-7, l0-7, i0-7, pc } else version (SPARC64) { long[33] m_reg; // g0-7, o0-7, l0-7, i0-7, pc } else { static assert(false, "Architecture not supported." ); } } override final void[] savedRegisters() nothrow @nogc { version (Windows) { return m_reg; } else version (Darwin) { return m_reg; } else version (Solaris) { return m_reg; } else { return 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 Thread start() nothrow in { assert( !next && !prev ); } do { auto wasThreaded = multiThreadedFlag; multiThreadedFlag = true; scope( failure ) { if ( !wasThreaded ) multiThreadedFlag = false; } version (Windows) {} else version (Posix) { size_t stksz = adjustStackSize( m_sz ); pthread_attr_t attr; if ( pthread_attr_init( &attr ) ) onThreadError( "Error initializing thread attributes" ); if ( stksz && pthread_attr_setstacksize( &attr, stksz ) ) onThreadError( "Error initializing thread stack size" ); } 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 thenLines 1–500 of 3139. Load more
/** * The osthread module provides low-level, OS-dependent code * 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/osthread.d) */ module core.thread.osthread; import core.atomic; import core.exception : onOutOfMemoryError; import core.internal.traits : externDFunc; import core.memory : GC, pageSize; import core.thread.context; import core.thread.threadbase; import core.thread.types; import core.time; /////////////////////////////////////////////////////////////////////////////// // Platform Detection and Memory Allocation /////////////////////////////////////////////////////////////////////////////// version (OSX) version = Darwin; else version (iOS) version = Darwin; else version (TVOS) version = Darwin; else version (WatchOS) version = Darwin; version (D_InlineAsm_X86) { version (Windows) version = AsmX86_Windows; else version (Posix) version = AsmX86_Posix; } else version (D_InlineAsm_X86_64) { version (Windows) { version = AsmX86_64_Windows; } else version (Posix) { version = AsmX86_64_Posix; } } version (Windows) { import core.stdc.stdint : uintptr_t; // for _beginthreadex decl below import core.stdc.stdlib : free, malloc, realloc; import core.sys.windows.basetsd /+: HANDLE+/; import core.sys.windows.threadaux /+: getThreadStackBottom, impersonate_thread, OpenThreadHandle+/; import core.sys.windows.winbase /+: CloseHandle, CREATE_SUSPENDED, DuplicateHandle, GetCurrentThread, GetCurrentThreadId, GetCurrentProcess, GetExitCodeThread, GetSystemInfo, GetThreadContext, GetThreadPriority, INFINITE, ResumeThread, SetThreadPriority, Sleep, STILL_ACTIVE, SuspendThread, SwitchToThread, SYSTEM_INFO, THREAD_PRIORITY_IDLE, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_TIME_CRITICAL, WAIT_OBJECT_0, WaitForSingleObject+/; import core.sys.windows.windef /+: TRUE+/; import core.sys.windows.winnt /+: CONTEXT, CONTEXT_CONTROL, CONTEXT_INTEGER+/; private extern (Windows) alias btex_fptr = uint function(void*); private extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*) nothrow @nogc; } else version (Posix) { static import core.sys.posix.pthread; static import core.sys.posix.signal; import core.stdc.errno : EINTR, errno; import core.sys.posix.pthread : pthread_atfork, pthread_attr_destroy, pthread_attr_getstack, pthread_attr_init, pthread_attr_setstacksize, pthread_create, pthread_detach, pthread_getschedparam, pthread_join, pthread_self, pthread_setschedparam, sched_get_priority_max, sched_get_priority_min, sched_param, sched_yield; import core.sys.posix.semaphore : sem_init, sem_post, sem_t, sem_wait; import core.sys.posix.signal : pthread_kill, sigaction, sigaction_t, sigdelset, sigfillset, sigset_t, sigsuspend, SIGUSR1, stack_t; import core.sys.posix.stdlib : free, malloc, realloc; import core.sys.posix.sys.types : pthread_attr_t, pthread_key_t, pthread_t; import core.sys.posix.time : nanosleep, timespec; version (Darwin) { // Use macOS threads for suspend/resume import core.sys.darwin.mach.kern_return : KERN_SUCCESS; import core.sys.darwin.mach.port : mach_port_t; import core.sys.darwin.mach.thread_act : mach_msg_type_number_t, thread_get_state, thread_resume, thread_suspend; import core.sys.darwin.pthread : pthread_mach_thread_np; version (X86) { import core.sys.darwin.mach.thread_act : x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT, x86_thread_state32_t; } else version (X86_64) { import core.sys.darwin.mach.thread_act : x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT, x86_thread_state64_t; } else version (AArch64) { import core.sys.darwin.mach.thread_act : ARM_THREAD_STATE64, ARM_THREAD_STATE64_COUNT, arm_thread_state64_t; } else version (PPC) { import core.sys.darwin.mach.thread_act : PPC_THREAD_STATE, PPC_THREAD_STATE_COUNT, ppc_thread_state_t; } else version (PPC64) { import core.sys.darwin.mach.thread_act : PPC_THREAD_STATE64, PPC_THREAD_STATE64_COUNT, ppc_thread_state64_t; } } else version (Solaris) { // Use Solaris threads for suspend/resume import core.sys.posix.sys.wait : idtype_t; import core.sys.solaris.sys.priocntl : PC_CLNULL, PC_GETCLINFO, PC_GETPARMS, PC_SETPARMS, pcinfo_t, pcparms_t, priocntl; import core.sys.solaris.sys.types : P_MYID, pri_t; import core.sys.solaris.thread : thr_stksegment, thr_suspend, thr_continue; import core.sys.solaris.sys.procfs : PR_STOPPED, lwpstatus_t; } else { // Use POSIX threads for suspend/resume } } version (GNU) { import gcc.builtins; } /** * Hook for whatever EH implementation is used to save/restore some data * per stack. * * Params: * newContext = The return value of the prior call to this function * where the stack was last swapped out, or null when a fiber stack * is switched in for the first time. */ private extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc; version (DigitalMars) { version (Windows) { extern(D) void* swapContext(void* newContext) nothrow @nogc { return _d_eh_swapContext(newContext); } } else { extern(C) void* _d_eh_swapContextDwarf(void* newContext) nothrow @nogc; extern(D) void* swapContext(void* newContext) nothrow @nogc { /* Detect at runtime which scheme is being used. * Eventually, determine it statically. */ static int which = 0; final switch (which) { case 0: { assert(newContext == null); auto p = _d_eh_swapContext(newContext); auto pdwarf = _d_eh_swapContextDwarf(newContext); if (p) { which = 1; return p; } else if (pdwarf) { which = 2; return pdwarf; } return null; } case 1: return _d_eh_swapContext(newContext); case 2: return _d_eh_swapContextDwarf(newContext); } } } } else { extern(D) void* swapContext(void* newContext) nothrow @nogc { return _d_eh_swapContext(newContext); } } /////////////////////////////////////////////////////////////////////////////// // 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. */ class Thread : ThreadBase { // // Standard thread data // version (Windows) { private HANDLE m_hndl; } version (Posix) { private shared bool m_isRunning; } version (Darwin) { private mach_port_t m_tmach; } version (Solaris) { private __gshared bool m_isRTClass; } // // Standard types // version (Windows) { alias TLSKey = uint; } else version (Posix) { alias TLSKey = pthread_key_t; } /////////////////////////////////////////////////////////////////////////// // 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 ) @safe pure nothrow @nogc { super(fn, sz); } /** * 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 ) @safe pure nothrow @nogc { super(dg, sz); } package this( size_t sz = 0 ) @safe pure nothrow @nogc { super(sz); } /** * Cleans up any remaining resources used by this object. */ ~this() nothrow @nogc { if (super.destructBeforeDtor()) return; version (Windows) { m_addr = m_addr.init; CloseHandle( m_hndl ); m_hndl = m_hndl.init; } else version (Posix) { if (m_addr != m_addr.init) pthread_detach( m_addr ); m_addr = m_addr.init; } version (Darwin) { m_tmach = m_tmach.init; } } // // Thread entry point. Invokes the function or delegate passed on // construction (if any). // private final void run() { super.run(); } /** * 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() @safe nothrow @nogc { return ThreadBase.getThis().toThread; } /////////////////////////////////////////////////////////////////////////// // Thread Context and GC Scanning Support /////////////////////////////////////////////////////////////////////////// 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 (Darwin) { 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 version (AArch64) { ulong[33] m_reg; // x0-x31, pc } else version (ARM) { uint[16] m_reg; // r0-r15 } else version (PPC) { // Make the assumption that we only care about non-fp and non-vr regs. // ??? : it seems plausible that a valid address can be copied into a VR. uint[32] m_reg; // r0-31 } else version (PPC64) { // As above. ulong[32] m_reg; // r0-31 } else { static assert(false, "Architecture not supported." ); } } else version (Solaris) { 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 version (SPARC) { int[33] m_reg; // g0-7, o0-7, l0-7, i0-7, pc } else version (SPARC64) { long[33] m_reg; // g0-7, o0-7, l0-7, i0-7, pc } else { static assert(false, "Architecture not supported." ); } } override final void[] savedRegisters() nothrow @nogc { version (Windows) { return m_reg; } else version (Darwin) { return m_reg; } else version (Solaris) { return m_reg; } else { return 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 Thread start() nothrow in { assert( !next && !prev ); } do { auto wasThreaded = multiThreadedFlag; multiThreadedFlag = true; scope( failure ) { if ( !wasThreaded ) multiThreadedFlag = false; } version (Windows) {} else version (Posix) { size_t stksz = adjustStackSize( m_sz ); pthread_attr_t attr; if ( pthread_attr_init( &attr ) ) onThreadError( "Error initializing thread attributes" ); if ( stksz && pthread_attr_setstacksize( &attr, stksz ) ) onThreadError( "Error initializing thread stack size" ); } 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