thread.c++ (4916B)
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #include "thread.h" 23 #include "debug.h" 24 25 #if _WIN32 26 #include <windows.h> 27 #include "windows-sanity.h" 28 #else 29 #include <pthread.h> 30 #include <signal.h> 31 #endif 32 33 namespace kj { 34 35 #if _WIN32 36 37 Thread::Thread(Function<void()> func): state(new ThreadState(kj::mv(func))) { 38 threadHandle = CreateThread(nullptr, 0, &runThread, state, 0, nullptr); 39 if (threadHandle == nullptr) { 40 state->unref(); 41 KJ_FAIL_ASSERT("CreateThread failed."); 42 } 43 } 44 45 Thread::~Thread() noexcept(false) { 46 if (!detached) { 47 KJ_DEFER(state->unref()); 48 49 KJ_ASSERT(WaitForSingleObject(threadHandle, INFINITE) != WAIT_FAILED); 50 51 KJ_IF_MAYBE(e, state->exception) { 52 Exception ecopy = kj::mv(*e); 53 state->exception = nullptr; // don't complain of uncaught exception when deleting 54 kj::throwRecoverableException(kj::mv(ecopy)); 55 } 56 } 57 } 58 59 void Thread::detach() { 60 KJ_ASSERT(CloseHandle(threadHandle)); 61 detached = true; 62 } 63 64 #else // _WIN32 65 66 Thread::Thread(Function<void()> func): state(new ThreadState(kj::mv(func))) { 67 static_assert(sizeof(threadId) >= sizeof(pthread_t), 68 "pthread_t is larger than a long long on your platform. Please port."); 69 70 int pthreadResult = pthread_create(reinterpret_cast<pthread_t*>(&threadId), 71 nullptr, &runThread, state); 72 if (pthreadResult != 0) { 73 state->unref(); 74 KJ_FAIL_SYSCALL("pthread_create", pthreadResult); 75 } 76 } 77 78 Thread::~Thread() noexcept(false) { 79 if (!detached) { 80 KJ_DEFER(state->unref()); 81 82 int pthreadResult = pthread_join(*reinterpret_cast<pthread_t*>(&threadId), nullptr); 83 if (pthreadResult != 0) { 84 KJ_FAIL_SYSCALL("pthread_join", pthreadResult) { break; } 85 } 86 87 KJ_IF_MAYBE(e, state->exception) { 88 Exception ecopy = kj::mv(*e); 89 state->exception = nullptr; // don't complain of uncaught exception when deleting 90 kj::throwRecoverableException(kj::mv(ecopy)); 91 } 92 } 93 } 94 95 void Thread::sendSignal(int signo) { 96 int pthreadResult = pthread_kill(*reinterpret_cast<pthread_t*>(&threadId), signo); 97 if (pthreadResult != 0) { 98 KJ_FAIL_SYSCALL("pthread_kill", pthreadResult) { break; } 99 } 100 } 101 102 void Thread::detach() { 103 int pthreadResult = pthread_detach(*reinterpret_cast<pthread_t*>(&threadId)); 104 if (pthreadResult != 0) { 105 KJ_FAIL_SYSCALL("pthread_detach", pthreadResult) { break; } 106 } 107 detached = true; 108 state->unref(); 109 } 110 111 #endif // _WIN32, else 112 113 Thread::ThreadState::ThreadState(Function<void()> func) 114 : func(kj::mv(func)), 115 initializer(getExceptionCallback().getThreadInitializer()), 116 exception(nullptr), 117 refcount(2) {} 118 119 void Thread::ThreadState::unref() { 120 #if _MSC_VER && !defined(__clang__) 121 if (_InterlockedDecrement(&refcount) == 0) { 122 #else 123 if (__atomic_sub_fetch(&refcount, 1, __ATOMIC_RELEASE) == 0) { 124 __atomic_thread_fence(__ATOMIC_ACQUIRE); 125 #endif 126 127 KJ_IF_MAYBE(e, exception) { 128 // If the exception is still present in ThreadState, this must be a detached thread, so 129 // the exception will never be rethrown. We should at least log it. 130 // 131 // We need to run the thread initializer again before we log anything because the main 132 // purpose of the thread initializer is to set up a logging callback. 133 initializer([&]() { 134 KJ_LOG(ERROR, "uncaught exception thrown by detached thread", *e); 135 }); 136 } 137 138 delete this; 139 } 140 } 141 142 #if _WIN32 143 DWORD Thread::runThread(void* ptr) { 144 #else 145 void* Thread::runThread(void* ptr) { 146 #endif 147 ThreadState* state = reinterpret_cast<ThreadState*>(ptr); 148 KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() { 149 state->initializer(kj::mv(state->func)); 150 })) { 151 state->exception = kj::mv(*exception); 152 } 153 state->unref(); 154 return 0; 155 } 156 157 } // namespace kj