Evo C++ Library v0.5.1
systhread.h
Go to the documentation of this file.
1 // Evo C++ Library
2 /* Copyright 2019 Justin Crowell
3 Distributed under the BSD 2-Clause License -- see included file LICENSE.txt for details.
4 */
6 
7 #pragma once
8 #ifndef INCL_evo_systhread_h
9 #define INCL_evo_systhread_h
10 
11 #include "sys.h"
12 
13 #if defined(_WIN32)
14  // Windows
15  #include "systime.h"
16 #else
17  // Linux/Unix
18  #include <pthread.h>
19  #if defined(__linux) && !defined(__CYGWIN__)
20  #include <sys/syscall.h>
21  #include <linux/version.h>
22  #if defined(LINUX_VERSION_CODE) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
23  #define EVO_LINUX_NPTL 1 // Linux 2.6+ uses native pthreads (NPTL)
24  #endif
25  #endif
26 #endif
27 
29 
30 // Thread Helpers
31 #if defined(_WIN32)
32  #define EVO_THREAD_RUN_DEFINE(NAME,PARAM_NAME) DWORD WINAPI NAME(LPVOID PARAM_NAME)
33  #define EVO_THREAD_RUN_RETURN return 0
34 #else
35 
40  #define EVO_THREAD_RUN_DEFINE(NAME,PARAM_NAME) void* NAME(void* PARAM_NAME)
41 
45  #define EVO_THREAD_RUN_RETURN return NULL
46 #endif
47 
48 namespace evo {
51 
53 
54 struct SysThread {
55 #if defined(_WIN32)
56  // Windows
57  typedef DWORD (WINAPI *RunFunc)(LPVOID);
58  typedef HANDLE Handle;
59 
60  SysThread()
61  { handle = NULL; }
62 
63  Error start(RunFunc run_func, void* run_ptr) {
64  handle = CreateThread(NULL, 0, run_func, run_ptr, 0, NULL);
65  if (handle != NULL)
66  return ENone;
67  return EUnknown;
68  }
69 
70  void detach() {
71  if (handle != NULL) {
72  CloseHandle(handle);
73  handle = NULL;
74  }
75  }
76 
77  bool join() {
78  if (handle != NULL) {
79  const bool done = (WaitForSingleObject(handle, INFINITE) == WAIT_OBJECT_0);
80  assert( done );
81  CloseHandle(handle);
82  handle = NULL;
83  return done;
84  }
85  return false;
86  }
87 
88  static void yield()
89  { YieldProcessor(); }
90 
91  static ulong id()
92  { return (ulong)GetCurrentThreadId(); }
93 
94 #else
95  // Linux/Unix
96  typedef void* (*RunFunc)(void*);
97  typedef pthread_t Handle;
98 
100  attached = false;
101  memset(&handle, 0, sizeof(Handle));
102  }
103 
104  Error start(RunFunc run_func, void* run_ptr) {
105  detach();
106  if (pthread_create(&handle, NULL, run_func, run_ptr) == 0) {
107  attached = true;
108  return ENone;
109  }
110  return EUnknown;
111  }
112 
113  void detach() {
114  if (attached) {
115  pthread_detach(handle);
116  memset(&handle, 0, sizeof(Handle));
117  attached = false;
118  }
119  }
120 
121  bool join() {
122  if (attached && pthread_join(handle, NULL) == 0) {
123  attached = false;
124  return true;
125  }
126  return false;
127  }
128 
129  static void yield() {
130  #if defined(__APPLE__)
131  pthread_yield_np();
132  #elif defined(__CYGWIN__)
133  __gthread_yield();
134  #else
135  pthread_yield();
136  #endif
137  }
138 
139  static ulong id() {
140  #if defined(SYS_gettid) && defined(EVO_LINUX_NPTL)
141  // Linux 2.6+ uses native pthreads (NPTL) so each pthread maps 1:1 to a kernel thread, and kernel TIDs are smaller numbers
142  return (ulong)syscall(SYS_gettid);
143  #else
144  // Converting pthread_t to ulong works on tested systems, though technically isn't portable
145  return (ulong)pthread_self();
146  #endif
147  }
148 
149  bool attached;
150 
151 #endif
153  { detach(); }
154 
155  Handle handle;
156 
157 private:
158  // Disable copying
159  SysThread(const SysThread&);
160  SysThread& operator=(const SysThread&);
161 };
162 
164 
165 struct SysMutex {
166 #if defined(_WIN32)
167  // Windows
168  typedef CRITICAL_SECTION Handle;
169 
170  SysMutex()
171  { InitializeCriticalSection(&handle); }
172 
173  ~SysMutex()
174  { DeleteCriticalSection(&handle); }
175 
176  bool trylock()
177  { return (TryEnterCriticalSection(&handle) != 0); }
178 
179  bool trylock(ulong timeout_ms) {
180  if (TryEnterCriticalSection(&handle) != 0)
181  return true;
182  // Spin wait -- Windows doesn't support timeout with critical section
183  SysTimestamp timeout_ts, ts;
184  timeout_ts.set_wall_timer();
185  timeout_ts.add_msec(timeout_ms);
186  for (;;) {
187  if (TryEnterCriticalSection(&handle) != 0)
188  return true;
189  ts.set_wall_timer();
190  if (ts.compare(timeout_ts) >= 0)
191  break;
192  }
193  return false;
194  }
195 
196  void lock()
197  { EnterCriticalSection(&handle); }
198 
199  void unlock()
200  { LeaveCriticalSection(&handle); }
201 
202 #else
203  // Linux/Unix
204  typedef pthread_mutex_t Handle;
205 
208  { pthread_mutex_init(&handle, NULL); }
209 
214  { pthread_mutex_destroy(&handle); }
215 
223  bool trylock() {
224  const int result = pthread_mutex_trylock(&handle);
225  if (result == 0)
226  return true;
227  assert( result == EBUSY );
228  return false;
229  }
230 
240  bool trylock(ulong timeout_ms) {
241  struct timespec ts;
242  #if defined(__APPLE__)
243  int result = pthread_mutex_trylock(&handle);
244  if (result == EBUSY) {
245  // Spin wait -- OSX doesn't have pthread_mutex_timedlock()
246  struct timespec timeout_ts;
247  SysLinux::set_timespec_now(timeout_ts);
248  SysLinux::add_timespec_ms(timeout_ts, timeout_ms);
249  for (;;) {
250  result = pthread_mutex_trylock(&handle);
251  if (result != EBUSY)
252  break;
254  if (SysLinux::compare_timespec(ts, timeout_ts) >= 0)
255  return false;
256  }
257  }
258  #else
259  // Always use CLOCK_REALTIME with pthread_mutex_timedlock()
260  #if defined(_POSIX_TIMERS) && defined(CLOCK_REALTIME) && !defined(EVO_USE_GETTIMEOFDAY)
261  ::clock_gettime(CLOCK_REALTIME, &ts);
262  #else
263  {
264  struct timeval tv;
265  ::gettimeofday(&tv, NULL);
267  }
268  #endif
269  SysLinux::add_timespec_ms(ts, timeout_ms);
270  const int result = pthread_mutex_timedlock(&handle, &ts);
271  #endif
272  if (result == 0)
273  return true;
274  assert( result == ETIMEDOUT );
275  return false;
276  }
277 
282  void lock()
283  { pthread_mutex_lock(&handle); }
284 
288  void unlock()
289  { pthread_mutex_unlock(&handle); }
290 
291 #endif
292 
293  Handle handle;
294 
295 private:
296  // Disable copying
297  SysMutex(const SysMutex&);
298  SysMutex& operator=(const SysMutex&);
299 };
300 
302 }
304 #endif
int compare(const SysTimestamp &oth) const
Compare to another timestamp.
Definition: systime.h:498
static ulong id()
Definition: systhread.h:139
Evo system time implementation helpers.
Definition: systhread.h:165
pthread_mutex_t Handle
Mutex handle type.
Definition: systhread.h:204
void set_wall_timer()
Set as current real (wall clock) time for use by timers.
Definition: systime.h:392
Evo implementation detail for system portability – this is included by most Evo headers, include this via: include <evo/type.h>.
void add_msec(ulong new_msec)
Add milliseconds to current time.
Definition: systime.h:467
Error start(RunFunc run_func, void *run_ptr)
Definition: systhread.h:104
Handle handle
Definition: systhread.h:155
bool join()
Definition: systhread.h:121
void *(* RunFunc)(void *)
Definition: systhread.h:96
No error.
Definition: sys.h:1115
~SysMutex()
Destructor.
Definition: systhread.h:213
Error
General Evo error code stored in exceptions, or used directly when exceptions are disabled...
Definition: sys.h:1113
~SysThread()
Definition: systhread.h:152
static void yield()
Definition: systhread.h:129
SysThread()
Definition: systhread.h:99
void detach()
Definition: systhread.h:113
static void set_timespec_tv(struct timespec &tm, struct timeval &tv)
Definition: sys.h:1533
static int compare_timespec(const struct timespec &a, const struct timespec &b)
Definition: sys.h:1576
static void add_timespec_ms(struct timespec &tm, ulong ms)
Definition: sys.h:1546
Unknown or unspecified error.
Definition: sys.h:1120
void lock()
Lock mutex.
Definition: systhread.h:282
Evo C++ Library namespace.
Definition: alg.h:11
pthread_t Handle
Definition: systhread.h:97
void unlock()
Unlock mutex.
Definition: systhread.h:288
bool trylock()
Try to lock mutex without blocking.
Definition: systhread.h:223
SysMutex()
Constructor.
Definition: systhread.h:207
bool trylock(ulong timeout_ms)
Try to lock mutex with a timeout.
Definition: systhread.h:240
static void set_timespec_now(struct timespec &tm)
Definition: sys.h:1560
Holds a system timestamp for storing date/time and measuring elapsed time.
Definition: systime.h:269
Definition: systhread.h:54
bool attached
Definition: systhread.h:149