capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

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