andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "primpl.h" andre@0: #include /* for _beginthreadex() */ andre@0: andre@0: #if defined(_MSC_VER) && _MSC_VER <= 1200 andre@0: /* andre@0: * VC++ 6.0 doesn't have DWORD_PTR. andre@0: */ andre@0: andre@0: typedef DWORD DWORD_PTR; andre@0: #endif /* _MSC_VER <= 1200 */ andre@0: andre@0: /* --- globals ------------------------------------------------ */ andre@0: #ifdef _PR_USE_STATIC_TLS andre@0: __declspec(thread) struct PRThread *_pr_thread_last_run; andre@0: __declspec(thread) struct PRThread *_pr_currentThread; andre@0: __declspec(thread) struct _PRCPU *_pr_currentCPU; andre@0: #else andre@0: DWORD _pr_currentThreadIndex; andre@0: DWORD _pr_lastThreadIndex; andre@0: DWORD _pr_currentCPUIndex; andre@0: #endif andre@0: int _pr_intsOff = 0; andre@0: _PRInterruptTable _pr_interruptTable[] = { { 0 } }; andre@0: andre@0: void andre@0: _PR_MD_EARLY_INIT() andre@0: { andre@0: #ifndef _PR_USE_STATIC_TLS andre@0: _pr_currentThreadIndex = TlsAlloc(); andre@0: _pr_lastThreadIndex = TlsAlloc(); andre@0: _pr_currentCPUIndex = TlsAlloc(); andre@0: #endif andre@0: } andre@0: andre@0: void _PR_MD_CLEANUP_BEFORE_EXIT(void) andre@0: { andre@0: _PR_NT_FreeSids(); andre@0: andre@0: _PR_MD_CleanupSockets(); andre@0: andre@0: WSACleanup(); andre@0: andre@0: #ifndef _PR_USE_STATIC_TLS andre@0: TlsFree(_pr_currentThreadIndex); andre@0: TlsFree(_pr_lastThreadIndex); andre@0: TlsFree(_pr_currentCPUIndex); andre@0: #endif andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_INIT_THREAD(PRThread *thread) andre@0: { andre@0: if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { andre@0: /* andre@0: ** Warning: andre@0: ** -------- andre@0: ** NSPR requires a real handle to every thread. andre@0: ** GetCurrentThread() returns a pseudo-handle which andre@0: ** is not suitable for some thread operations (e.g., andre@0: ** suspending). Therefore, get a real handle from andre@0: ** the pseudo handle via DuplicateHandle(...) andre@0: */ andre@0: DuplicateHandle( andre@0: GetCurrentProcess(), /* Process of source handle */ andre@0: GetCurrentThread(), /* Pseudo Handle to dup */ andre@0: GetCurrentProcess(), /* Process of handle */ andre@0: &(thread->md.handle), /* resulting handle */ andre@0: 0L, /* access flags */ andre@0: FALSE, /* Inheritable */ andre@0: DUPLICATE_SAME_ACCESS); /* Options */ andre@0: } andre@0: andre@0: /* Create the blocking IO semaphore */ andre@0: thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL); andre@0: if (thread->md.blocked_sema == NULL) andre@0: return PR_FAILURE; andre@0: else andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: static unsigned __stdcall andre@0: pr_root(void *arg) andre@0: { andre@0: PRThread *thread = (PRThread *)arg; andre@0: thread->md.start(thread); andre@0: return 0; andre@0: } andre@0: andre@0: PRStatus andre@0: _PR_MD_CREATE_THREAD(PRThread *thread, andre@0: void (*start)(void *), andre@0: PRThreadPriority priority, andre@0: PRThreadScope scope, andre@0: PRThreadState state, andre@0: PRUint32 stackSize) andre@0: { andre@0: andre@0: thread->md.start = start; andre@0: thread->md.handle = (HANDLE) _beginthreadex( andre@0: NULL, andre@0: thread->stack->stackSize, andre@0: pr_root, andre@0: (void *)thread, andre@0: CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, andre@0: &(thread->id)); andre@0: if(!thread->md.handle) { andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: thread->md.id = thread->id; andre@0: /* andre@0: * On windows, a thread is created with a thread priority of andre@0: * THREAD_PRIORITY_NORMAL. andre@0: */ andre@0: if (priority != PR_PRIORITY_NORMAL) { andre@0: _PR_MD_SET_PRIORITY(&(thread->md), priority); andre@0: } andre@0: andre@0: /* Activate the thread */ andre@0: if ( ResumeThread( thread->md.handle ) != -1) andre@0: return PR_SUCCESS; andre@0: andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: void andre@0: _PR_MD_YIELD(void) andre@0: { andre@0: /* Can NT really yield at all? */ andre@0: Sleep(0); andre@0: } andre@0: andre@0: void andre@0: _PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri) andre@0: { andre@0: int nativePri; andre@0: BOOL rv; andre@0: andre@0: if (newPri < PR_PRIORITY_FIRST) { andre@0: newPri = PR_PRIORITY_FIRST; andre@0: } else if (newPri > PR_PRIORITY_LAST) { andre@0: newPri = PR_PRIORITY_LAST; andre@0: } andre@0: switch (newPri) { andre@0: case PR_PRIORITY_LOW: andre@0: nativePri = THREAD_PRIORITY_BELOW_NORMAL; andre@0: break; andre@0: case PR_PRIORITY_NORMAL: andre@0: nativePri = THREAD_PRIORITY_NORMAL; andre@0: break; andre@0: case PR_PRIORITY_HIGH: andre@0: nativePri = THREAD_PRIORITY_ABOVE_NORMAL; andre@0: break; andre@0: case PR_PRIORITY_URGENT: andre@0: nativePri = THREAD_PRIORITY_HIGHEST; andre@0: } andre@0: rv = SetThreadPriority(thread->handle, nativePri); andre@0: PR_ASSERT(rv); andre@0: if (!rv) { andre@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, andre@0: ("PR_SetThreadPriority: can't set thread priority\n")); andre@0: } andre@0: return; andre@0: } andre@0: andre@0: const DWORD MS_VC_EXCEPTION = 0x406D1388; andre@0: andre@0: #pragma pack(push,8) andre@0: typedef struct tagTHREADNAME_INFO andre@0: { andre@0: DWORD dwType; // Must be 0x1000. andre@0: LPCSTR szName; // Pointer to name (in user addr space). andre@0: DWORD dwThreadID; // Thread ID (-1=caller thread). andre@0: DWORD dwFlags; // Reserved for future use, must be zero. andre@0: } THREADNAME_INFO; andre@0: #pragma pack(pop) andre@0: andre@0: void andre@0: _PR_MD_SET_CURRENT_THREAD_NAME(const char *name) andre@0: { andre@0: #ifdef _MSC_VER andre@0: THREADNAME_INFO info; andre@0: andre@0: if (!IsDebuggerPresent()) andre@0: return; andre@0: andre@0: info.dwType = 0x1000; andre@0: info.szName = (char*) name; andre@0: info.dwThreadID = -1; andre@0: info.dwFlags = 0; andre@0: andre@0: __try { andre@0: RaiseException(MS_VC_EXCEPTION, andre@0: 0, andre@0: sizeof(info) / sizeof(ULONG_PTR), andre@0: (ULONG_PTR*)&info); andre@0: } __except(EXCEPTION_CONTINUE_EXECUTION) { andre@0: } andre@0: #endif andre@0: } andre@0: andre@0: void andre@0: _PR_MD_CLEAN_THREAD(PRThread *thread) andre@0: { andre@0: BOOL rv; andre@0: andre@0: if (thread->md.blocked_sema) { andre@0: rv = CloseHandle(thread->md.blocked_sema); andre@0: PR_ASSERT(rv); andre@0: thread->md.blocked_sema = 0; andre@0: } andre@0: andre@0: if (thread->md.handle) { andre@0: rv = CloseHandle(thread->md.handle); andre@0: PR_ASSERT(rv); andre@0: thread->md.handle = 0; andre@0: } andre@0: } andre@0: andre@0: void andre@0: _PR_MD_EXIT_THREAD(PRThread *thread) andre@0: { andre@0: _PR_MD_CLEAN_THREAD(thread); andre@0: _PR_MD_SET_CURRENT_THREAD(NULL); andre@0: } andre@0: andre@0: andre@0: void andre@0: _PR_MD_EXIT(PRIntn status) andre@0: { andre@0: _exit(status); andre@0: } andre@0: andre@0: PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask ) andre@0: { andre@0: #ifdef WINCE andre@0: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); andre@0: return -1; andre@0: #else andre@0: DWORD_PTR rv; andre@0: andre@0: rv = SetThreadAffinityMask(thread->md.handle, mask); andre@0: andre@0: return rv?0:-1; andre@0: #endif andre@0: } andre@0: andre@0: PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask) andre@0: { andre@0: #ifdef WINCE andre@0: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); andre@0: return -1; andre@0: #else andre@0: BOOL rv; andre@0: DWORD_PTR process_mask; andre@0: DWORD_PTR system_mask; andre@0: andre@0: rv = GetProcessAffinityMask(GetCurrentProcess(), andre@0: &process_mask, &system_mask); andre@0: if (rv) andre@0: *mask = (PRUint32)process_mask; andre@0: andre@0: return rv?0:-1; andre@0: #endif andre@0: } andre@0: andre@0: void andre@0: _PR_MD_SUSPEND_CPU(_PRCPU *cpu) andre@0: { andre@0: _PR_MD_SUSPEND_THREAD(cpu->thread); andre@0: } andre@0: andre@0: void andre@0: _PR_MD_RESUME_CPU(_PRCPU *cpu) andre@0: { andre@0: _PR_MD_RESUME_THREAD(cpu->thread); andre@0: } andre@0: andre@0: void andre@0: _PR_MD_SUSPEND_THREAD(PRThread *thread) andre@0: { andre@0: if (_PR_IS_NATIVE_THREAD(thread)) { andre@0: DWORD previousSuspendCount; andre@0: /* XXXMB - SuspendThread() is not a blocking call; how do we andre@0: * know when the thread is *REALLY* suspended? andre@0: */ andre@0: previousSuspendCount = SuspendThread(thread->md.handle); andre@0: PR_ASSERT(previousSuspendCount == 0); andre@0: } andre@0: } andre@0: andre@0: void andre@0: _PR_MD_RESUME_THREAD(PRThread *thread) andre@0: { andre@0: if (_PR_IS_NATIVE_THREAD(thread)) { andre@0: DWORD previousSuspendCount; andre@0: previousSuspendCount = ResumeThread(thread->md.handle); andre@0: PR_ASSERT(previousSuspendCount == 1); andre@0: } andre@0: } andre@0: andre@0: PRThread* andre@0: _MD_CURRENT_THREAD(void) andre@0: { andre@0: PRThread *thread; andre@0: andre@0: thread = _MD_GET_ATTACHED_THREAD(); andre@0: andre@0: if (NULL == thread) { andre@0: thread = _PRI_AttachThread( andre@0: PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, NULL, 0); andre@0: } andre@0: PR_ASSERT(thread != NULL); andre@0: return thread; andre@0: } andre@0: andre@0: #ifdef NSPR_STATIC andre@0: andre@0: // The following code is from Chromium src/base/thread_local_storage_win.cc, andre@0: // r11329. andre@0: andre@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. andre@0: // andre@0: // Redistribution and use in source and binary forms, with or without andre@0: // modification, are permitted provided that the following conditions are andre@0: // met: andre@0: // andre@0: // * Redistributions of source code must retain the above copyright andre@0: // notice, this list of conditions and the following disclaimer. andre@0: // * Redistributions in binary form must reproduce the above andre@0: // copyright notice, this list of conditions and the following disclaimer andre@0: // in the documentation and/or other materials provided with the andre@0: // distribution. andre@0: // * Neither the name of Google Inc. nor the names of its andre@0: // contributors may be used to endorse or promote products derived from andre@0: // this software without specific prior written permission. andre@0: // andre@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS andre@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT andre@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR andre@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT andre@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, andre@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT andre@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, andre@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY andre@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT andre@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE andre@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. andre@0: andre@0: // Thread Termination Callbacks. andre@0: // Windows doesn't support a per-thread destructor with its andre@0: // TLS primitives. So, we build it manually by inserting a andre@0: // function to be called on each thread's exit. andre@0: // This magic is from http://www.codeproject.com/threads/tls.asp andre@0: // and it works for VC++ 7.0 and later. andre@0: andre@0: // Force a reference to _tls_used to make the linker create the TLS directory andre@0: // if it's not already there. (e.g. if __declspec(thread) is not used). andre@0: // Force a reference to p_thread_callback_nspr to prevent whole program andre@0: // optimization from discarding the variable. andre@0: #ifdef _WIN64 andre@0: andre@0: #pragma comment(linker, "/INCLUDE:_tls_used") andre@0: #pragma comment(linker, "/INCLUDE:p_thread_callback_nspr") andre@0: andre@0: #else // _WIN64 andre@0: andre@0: #pragma comment(linker, "/INCLUDE:__tls_used") andre@0: #pragma comment(linker, "/INCLUDE:_p_thread_callback_nspr") andre@0: andre@0: #endif // _WIN64 andre@0: andre@0: // Static callback function to call with each thread termination. andre@0: static void NTAPI PR_OnThreadExit(PVOID module, DWORD reason, PVOID reserved) andre@0: { andre@0: PRThread *me; andre@0: andre@0: switch (reason) { andre@0: case DLL_PROCESS_ATTACH: andre@0: break; andre@0: case DLL_THREAD_ATTACH: andre@0: break; andre@0: case DLL_THREAD_DETACH: andre@0: if (_pr_initialized) { andre@0: me = _MD_GET_ATTACHED_THREAD(); andre@0: if ((me != NULL) && (me->flags & _PR_ATTACHED)) andre@0: _PRI_DetachThread(); andre@0: } andre@0: break; andre@0: case DLL_PROCESS_DETACH: andre@0: break; andre@0: } andre@0: } andre@0: andre@0: // .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are andre@0: // called automatically by the OS loader code (not the CRT) when the module is andre@0: // loaded and on thread creation. They are NOT called if the module has been andre@0: // loaded by a LoadLibrary() call. It must have implicitly been loaded at andre@0: // process startup. andre@0: // By implicitly loaded, I mean that it is directly referenced by the main EXE andre@0: // or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being andre@0: // implicitly loaded. andre@0: // andre@0: // See VC\crt\src\tlssup.c for reference. andre@0: andre@0: // The linker must not discard p_thread_callback_nspr. (We force a reference andre@0: // to this variable with a linker /INCLUDE:symbol pragma to ensure that.) If andre@0: // this variable is discarded, the PR_OnThreadExit function will never be andre@0: // called. andre@0: #ifdef _WIN64 andre@0: andre@0: // .CRT section is merged with .rdata on x64 so it must be constant data. andre@0: #pragma const_seg(".CRT$XLB") andre@0: // When defining a const variable, it must have external linkage to be sure the andre@0: // linker doesn't discard it. andre@0: extern const PIMAGE_TLS_CALLBACK p_thread_callback_nspr; andre@0: const PIMAGE_TLS_CALLBACK p_thread_callback_nspr = PR_OnThreadExit; andre@0: andre@0: // Reset the default section. andre@0: #pragma const_seg() andre@0: andre@0: #else // _WIN64 andre@0: andre@0: #pragma data_seg(".CRT$XLB") andre@0: PIMAGE_TLS_CALLBACK p_thread_callback_nspr = PR_OnThreadExit; andre@0: andre@0: // Reset the default section. andre@0: #pragma data_seg() andre@0: andre@0: #endif // _WIN64 andre@0: andre@0: #endif // NSPR_STATIC