diff options
Diffstat (limited to 'src/threading.h')
-rw-r--r-- | src/threading.h | 262 |
1 files changed, 43 insertions, 219 deletions
diff --git a/src/threading.h b/src/threading.h index 38a021f..fc35730 100644 --- a/src/threading.h +++ b/src/threading.h | |||
@@ -1,25 +1,13 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | /* | ||
4 | * win32-pthread: | ||
5 | * define HAVE_WIN32_PTHREAD and PTW32_INCLUDE_WINDOWS_H in your project configuration when building for win32-pthread. | ||
6 | * link against pthreadVC2.lib, and of course have pthreadVC2.dll somewhere in your path. | ||
7 | */ | ||
8 | #include "platform.h" | 3 | #include "platform.h" |
9 | 4 | ||
10 | #include <time.h> | 5 | #include <thread> |
11 | |||
12 | /* Note: ERROR is a defined entity on Win32 | ||
13 | PENDING: The Lua VM hasn't done anything yet. | ||
14 | RUNNING, WAITING: Thread is inside the Lua VM. If the thread is forcefully stopped, we can't lua_close() the Lua State. | ||
15 | DONE, ERROR_ST, CANCELLED: Thread execution is outside the Lua VM. It can be lua_close()d. | ||
16 | */ | ||
17 | enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; | ||
18 | 6 | ||
19 | #define THREADAPI_WINDOWS 1 | 7 | #define THREADAPI_WINDOWS 1 |
20 | #define THREADAPI_PTHREAD 2 | 8 | #define THREADAPI_PTHREAD 2 |
21 | 9 | ||
22 | #if( defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) && !defined( HAVE_WIN32_PTHREAD) | 10 | #if( defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) |
23 | //#pragma message ( "THREADAPI_WINDOWS" ) | 11 | //#pragma message ( "THREADAPI_WINDOWS" ) |
24 | #define THREADAPI THREADAPI_WINDOWS | 12 | #define THREADAPI THREADAPI_WINDOWS |
25 | #else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 13 | #else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
@@ -27,22 +15,24 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; | |||
27 | #define THREADAPI THREADAPI_PTHREAD | 15 | #define THREADAPI THREADAPI_PTHREAD |
28 | #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 16 | #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
29 | 17 | ||
30 | /*---=== Locks & Signals ===--- | 18 | static constexpr int THREAD_PRIO_DEFAULT{ -999 }; |
31 | */ | ||
32 | 19 | ||
20 | // ################################################################################################## | ||
21 | // ################################################################################################## | ||
33 | #if THREADAPI == THREADAPI_WINDOWS | 22 | #if THREADAPI == THREADAPI_WINDOWS |
34 | #if defined( PLATFORM_XBOX) | 23 | |
35 | #include <xtl.h> | 24 | #if defined(PLATFORM_XBOX) |
36 | #else // !PLATFORM_XBOX | 25 | #include <xtl.h> |
37 | #define WIN32_LEAN_AND_MEAN | 26 | #else // !PLATFORM_XBOX |
38 | // CONDITION_VARIABLE needs version 0x0600+ | 27 | #define WIN32_LEAN_AND_MEAN |
39 | // _WIN32_WINNT value is already defined by MinGW, but not by MSVC | 28 | // CONDITION_VARIABLE needs version 0x0600+ |
40 | #ifndef _WIN32_WINNT | 29 | // _WIN32_WINNT value is already defined by MinGW, but not by MSVC |
41 | #define _WIN32_WINNT 0x0600 | 30 | #ifndef _WIN32_WINNT |
42 | #endif // _WIN32_WINNT | 31 | #define _WIN32_WINNT 0x0600 |
43 | #include <windows.h> | 32 | #endif // _WIN32_WINNT |
44 | #endif // !PLATFORM_XBOX | 33 | #include <windows.h> |
45 | #include <process.h> | 34 | #endif // !PLATFORM_XBOX |
35 | #include <process.h> | ||
46 | 36 | ||
47 | /* | 37 | /* |
48 | #define XSTR(x) STR(x) | 38 | #define XSTR(x) STR(x) |
@@ -50,202 +40,36 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; | |||
50 | #pragma message( "The value of _WIN32_WINNT: " XSTR(_WIN32_WINNT)) | 40 | #pragma message( "The value of _WIN32_WINNT: " XSTR(_WIN32_WINNT)) |
51 | */ | 41 | */ |
52 | 42 | ||
53 | // MSDN: http://msdn2.microsoft.com/en-us/library/ms684254.aspx | 43 | static constexpr int THREAD_PRIO_MIN{ -3 }; |
54 | // | 44 | static constexpr int THREAD_PRIO_MAX{ +3 }; |
55 | // CRITICAL_SECTION can be used for simple code protection. Mutexes are | ||
56 | // needed for use with the SIGNAL system. | ||
57 | // | ||
58 | |||
59 | #if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available, use a signal | ||
60 | |||
61 | struct SIGNAL_T | ||
62 | { | ||
63 | CRITICAL_SECTION signalCS; | ||
64 | CRITICAL_SECTION countCS; | ||
65 | HANDLE waitEvent; | ||
66 | HANDLE waitDoneEvent; | ||
67 | LONG waitersCount; | ||
68 | }; | ||
69 | |||
70 | |||
71 | #define MUTEX_T HANDLE | ||
72 | void MUTEX_INIT( MUTEX_T* ref); | ||
73 | void MUTEX_FREE( MUTEX_T* ref); | ||
74 | void MUTEX_LOCK( MUTEX_T* ref); | ||
75 | void MUTEX_UNLOCK( MUTEX_T* ref); | ||
76 | |||
77 | #else // CONDITION_VARIABLE are available, use them | ||
78 | |||
79 | #define SIGNAL_T CONDITION_VARIABLE | ||
80 | #define MUTEX_T CRITICAL_SECTION | ||
81 | #define MUTEX_INIT( ref) InitializeCriticalSection( ref) | ||
82 | #define MUTEX_FREE( ref) DeleteCriticalSection( ref) | ||
83 | #define MUTEX_LOCK( ref) EnterCriticalSection( ref) | ||
84 | #define MUTEX_UNLOCK( ref) LeaveCriticalSection( ref) | ||
85 | |||
86 | #endif // CONDITION_VARIABLE are available | ||
87 | |||
88 | #define MUTEX_RECURSIVE_INIT(ref) MUTEX_INIT(ref) /* always recursive in Win32 */ | ||
89 | |||
90 | using THREAD_RETURN_T = unsigned int; | ||
91 | 45 | ||
92 | #define YIELD() Sleep(0) | 46 | // ################################################################################################## |
93 | #define THREAD_CALLCONV __stdcall | 47 | // ################################################################################################## |
94 | #else // THREADAPI == THREADAPI_PTHREAD | 48 | #else // THREADAPI == THREADAPI_PTHREAD |
95 | // PThread (Linux, OS X, ...) | 49 | // ################################################################################################## |
96 | 50 | // ################################################################################################## | |
97 | // looks like some MinGW installations don't support PTW32_INCLUDE_WINDOWS_H, so let's include it ourselves, just in case | ||
98 | #if defined(PLATFORM_WIN32) | ||
99 | #include <windows.h> | ||
100 | #endif // PLATFORM_WIN32 | ||
101 | #include <pthread.h> | ||
102 | |||
103 | #ifdef PLATFORM_LINUX | ||
104 | #if defined(__GLIBC__) | ||
105 | # define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP | ||
106 | #else | ||
107 | # define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE | ||
108 | #endif | ||
109 | #else | ||
110 | /* OS X, ... */ | ||
111 | # define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE | ||
112 | #endif | ||
113 | |||
114 | #define MUTEX_T pthread_mutex_t | ||
115 | #define MUTEX_INIT(ref) pthread_mutex_init(ref, nullptr) | ||
116 | #define MUTEX_RECURSIVE_INIT(ref) \ | ||
117 | { pthread_mutexattr_t a; pthread_mutexattr_init( &a ); \ | ||
118 | pthread_mutexattr_settype( &a, _MUTEX_RECURSIVE ); \ | ||
119 | pthread_mutex_init(ref,&a); pthread_mutexattr_destroy( &a ); \ | ||
120 | } | ||
121 | #define MUTEX_FREE(ref) pthread_mutex_destroy(ref) | ||
122 | #define MUTEX_LOCK(ref) pthread_mutex_lock(ref) | ||
123 | #define MUTEX_UNLOCK(ref) pthread_mutex_unlock(ref) | ||
124 | |||
125 | using THREAD_RETURN_T = void *; | ||
126 | |||
127 | using SIGNAL_T = pthread_cond_t; | ||
128 | |||
129 | void SIGNAL_ONE( SIGNAL_T *ref ); | ||
130 | |||
131 | // Yield is non-portable: | ||
132 | // | ||
133 | // OS X 10.4.8/9 has pthread_yield_np() | ||
134 | // Linux 2.4 has pthread_yield() if _GNU_SOURCE is #defined | ||
135 | // FreeBSD 6.2 has pthread_yield() | ||
136 | // ... | ||
137 | // | ||
138 | #if defined( PLATFORM_OSX) | ||
139 | #define YIELD() pthread_yield_np() | ||
140 | #else | ||
141 | #define YIELD() sched_yield() | ||
142 | #endif | ||
143 | #define THREAD_CALLCONV | ||
144 | #endif //THREADAPI == THREADAPI_PTHREAD | ||
145 | |||
146 | void SIGNAL_INIT( SIGNAL_T *ref ); | ||
147 | void SIGNAL_FREE( SIGNAL_T *ref ); | ||
148 | void SIGNAL_ALL( SIGNAL_T *ref ); | ||
149 | 51 | ||
150 | /* | 52 | // PThread (Linux, OS X, ...) |
151 | * 'time_d': <0.0 for no timeout | ||
152 | * 0.0 for instant check | ||
153 | * >0.0 absolute timeout in secs + ms | ||
154 | */ | ||
155 | using time_d = double; | ||
156 | time_d now_secs(void); | ||
157 | |||
158 | time_d SIGNAL_TIMEOUT_PREPARE( double rel_secs ); | ||
159 | |||
160 | bool SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout ); | ||
161 | |||
162 | |||
163 | /*---=== Threading ===--- | ||
164 | */ | ||
165 | 53 | ||
166 | #define THREAD_PRIO_DEFAULT (-999) | 54 | // looks like some MinGW installations don't support PTW32_INCLUDE_WINDOWS_H, so let's include it ourselves, just in case |
55 | #if defined(PLATFORM_WIN32) | ||
56 | #include <windows.h> | ||
57 | #endif // PLATFORM_WIN32 | ||
58 | #include <pthread.h> | ||
167 | 59 | ||
168 | #if THREADAPI == THREADAPI_WINDOWS | 60 | #if defined(PLATFORM_LINUX) && !defined(LINUX_SCHED_RR) |
169 | 61 | static constexpr int THREAD_PRIO_MIN{ 0 }; | |
170 | using THREAD_T = HANDLE; | 62 | #else |
171 | # define THREAD_ISNULL( _h) (_h == 0) | 63 | static constexpr int THREAD_PRIO_MIN{ -3 }; |
172 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */); | 64 | #endif |
65 | static constexpr int THREAD_PRIO_MAX{ +3 }; | ||
173 | 66 | ||
174 | # define THREAD_PRIO_MIN (-3) | 67 | #endif // THREADAPI == THREADAPI_PTHREAD |
175 | # define THREAD_PRIO_MAX (+3) | 68 | // ################################################################################################## |
69 | // ################################################################################################## | ||
176 | 70 | ||
177 | # define THREAD_CLEANUP_PUSH( cb_, val_) | 71 | void THREAD_SETNAME(char const* _name); |
178 | # define THREAD_CLEANUP_POP( execute_) | 72 | void THREAD_SET_PRIORITY(int prio_, bool sudo_); |
179 | 73 | void THREAD_SET_AFFINITY(unsigned int aff); | |
180 | #else // THREADAPI == THREADAPI_PTHREAD | ||
181 | 74 | ||
182 | /* Platforms that have a timed 'pthread_join()' can get away with a simpler | 75 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, bool sudo_); |
183 | * implementation. Others will use a condition variable. | ||
184 | */ | ||
185 | # if defined __WINPTHREADS_VERSION | ||
186 | //# define USE_PTHREAD_TIMEDJOIN | ||
187 | # endif // __WINPTHREADS_VERSION | ||
188 | |||
189 | # ifdef USE_PTHREAD_TIMEDJOIN | ||
190 | # ifdef PLATFORM_OSX | ||
191 | # error "No 'pthread_timedjoin()' on this system" | ||
192 | # else | ||
193 | /* Linux, ... */ | ||
194 | # define PTHREAD_TIMEDJOIN pthread_timedjoin_np | ||
195 | # endif | ||
196 | # endif | ||
197 | |||
198 | using THREAD_T = pthread_t; | ||
199 | # define THREAD_ISNULL( _h) 0 // pthread_t may be a structure: never 'null' by itself | ||
200 | |||
201 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */); | ||
202 | |||
203 | # if defined(PLATFORM_LINUX) | ||
204 | extern volatile bool sudo; | ||
205 | # ifdef LINUX_SCHED_RR | ||
206 | # define THREAD_PRIO_MIN (sudo ? -3 : 0) | ||
207 | # else | ||
208 | # define THREAD_PRIO_MIN (0) | ||
209 | # endif | ||
210 | # define THREAD_PRIO_MAX (sudo ? +3 : 0) | ||
211 | # else | ||
212 | # define THREAD_PRIO_MIN (-3) | ||
213 | # define THREAD_PRIO_MAX (+3) | ||
214 | # endif | ||
215 | |||
216 | # if THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
217 | # define THREAD_CLEANUP_PUSH( cb_, val_) pthread_cleanup_push( cb_, val_) | ||
218 | # define THREAD_CLEANUP_POP( execute_) pthread_cleanup_pop( execute_) | ||
219 | # else | ||
220 | # define THREAD_CLEANUP_PUSH( cb_, val_) { | ||
221 | # define THREAD_CLEANUP_POP( execute_) } | ||
222 | # endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
223 | #endif // THREADAPI == THREADAPI_WINDOWS | ||
224 | |||
225 | /* | ||
226 | * Win32 and PTHREAD_TIMEDJOIN allow waiting for a thread with a timeout. | ||
227 | * Posix without PTHREAD_TIMEDJOIN needs to use a condition variable approach. | ||
228 | */ | ||
229 | #define THREADWAIT_TIMEOUT 1 | ||
230 | #define THREADWAIT_CONDVAR 2 | ||
231 | |||
232 | #if THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN) | ||
233 | #define THREADWAIT_METHOD THREADWAIT_TIMEOUT | ||
234 | #else // THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN) | ||
235 | #define THREADWAIT_METHOD THREADWAIT_CONDVAR | ||
236 | #endif // THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN) | ||
237 | |||
238 | |||
239 | #if THREADWAIT_METHOD == THREADWAIT_TIMEOUT | ||
240 | bool THREAD_WAIT_IMPL( THREAD_T *ref, double secs); | ||
241 | #define THREAD_WAIT( a, b, c, d, e) THREAD_WAIT_IMPL( a, b) | ||
242 | #else // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
243 | bool THREAD_WAIT_IMPL( THREAD_T *ref, double secs, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref); | ||
244 | #define THREAD_WAIT THREAD_WAIT_IMPL | ||
245 | #endif // // THREADWAIT_METHOD == THREADWAIT_CONDVAR | ||
246 | |||
247 | void THREAD_KILL( THREAD_T* ref); | ||
248 | void THREAD_SETNAME( char const* _name); | ||
249 | void THREAD_MAKE_ASYNCH_CANCELLABLE(); | ||
250 | void THREAD_SET_PRIORITY( int prio); | ||
251 | void THREAD_SET_AFFINITY( unsigned int aff); | ||