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 |
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 | KJ_LOG(ERROR, "uncaught exception thrown by detached thread" , *e); |
129 | } |
130 | |
131 | delete this; |
132 | } |
133 | } |
134 | |
135 | #if _WIN32 |
136 | DWORD Thread::runThread(void* ptr) { |
137 | #else |
138 | void* Thread::runThread(void* ptr) { |
139 | #endif |
140 | ThreadState* state = reinterpret_cast<ThreadState*>(ptr); |
141 | KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() { |
142 | state->initializer(kj::mv(state->func)); |
143 | })) { |
144 | state->exception = kj::mv(*exception); |
145 | } |
146 | state->unref(); |
147 | return 0; |
148 | } |
149 | |
150 | } // namespace kj |
151 | |