1 #include "config.h"
2 
3 #include <razorback/debug.h>
4 #include <razorback/thread.h>
5 #include <razorback/ntlv.h>
6 #include <razorback/list.h>
7 #include <razorback/log.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <errno.h>
11 #include "runtime_config.h"
12 
13 
14 static struct List *sg_threadList;
15 
16 #ifdef _MSC_VER
17 static volatile int initialized = 0;
18 
19 static void initThreading_win32(void);
20 #else //_MSC_VER
21 
22 #include <pthread.h>
23 #include <signal.h>
24 
25 static pthread_attr_t g_attr;
26 static pthread_once_t g_once_control = PTHREAD_ONCE_INIT;
27 
28 static void initThreading_pthreads(void);
29 
30 
31 static void handler(int sig);
32 #endif //_MSC_VER
33 static void Thread_Lock(void *);
34 static void Thread_Unlock(void *);
35 
36 static void
initThreading(void)37 initThreading (void)
38 {
39 	sg_threadList = List_Create(LIST_MODE_GENERIC,
40             Thread_Cmp,
41             Thread_KeyCmp,
42             NULL, NULL,
43             Thread_Lock,
44             Thread_Unlock);
45 #ifdef _MSC_VER
46     initThreading_win32();
47 #else
48     initThreading_pthreads();
49 #endif
50 }
51 
52 #ifdef _MSC_VER
53 static DWORD WINAPI
Thread_MainWrapper(void * arg)54 Thread_MainWrapper (void *arg)
55 #else
56 static void *
57 Thread_MainWrapper (void *arg)
58 #endif
59 {
60     struct Thread *l_pThread = (struct Thread *) arg;
61     List_Lock(sg_threadList);
62     Thread_Lock (l_pThread);
63 
64     l_pThread->bRunning = true;
65 
66     Thread_Unlock (l_pThread);
67     List_Unlock(sg_threadList);
68 
69     l_pThread->mainFunction (l_pThread);
70 
71     List_Lock(sg_threadList);
72     Thread_Lock (l_pThread);
73 
74     l_pThread->bRunning = false;
75 
76     Thread_Unlock (l_pThread);
77     List_Unlock(sg_threadList);
78 
79     // Don't destroy the thread structure, it may still be referenced
80     // We should probably ref count this struct
81     Thread_Destroy(l_pThread);
82 
83 #ifdef _MSC_VER
84 	return 0;
85 #else //_MSC_VER
86     return NULL; // Implicit pthread_exit()
87 #endif //_MSC_VER
88 }
89 
90 SO_PUBLIC struct Thread *
Thread_Launch(void (* fpFunction)(struct Thread *),void * userData,char * name,struct RazorbackContext * context)91 Thread_Launch (void (*fpFunction) (struct Thread *), void *userData,
92         char *name, struct RazorbackContext *context)
93 {
94 	struct Thread *thread;
95 
96 	ASSERT (fpFunction != NULL);
97 	if (fpFunction == NULL)
98 		return NULL;
99 
100 #ifdef _MSC_VER
101     if (initialized == 0)
102 		initThreading();
103 
104 #else //_MSC_VER
105     pthread_once (&g_once_control, initThreading);
106 #endif //_MSC_VER
107 
108     // Racy
109     if (sg_threadList->length == Config_getThreadLimit ())
110         return NULL;
111 
112     // allocate memory for thread structure
113     thread = (struct Thread *)calloc (1, sizeof (struct Thread));
114     if (thread == NULL)
115     {
116         rzb_log (LOG_ERR,
117                  "%s: Failed to launch thread in Thread_Launch due to out of memory for Thread", __func__);
118         return NULL;
119     }
120 
121     // initialize running indicator
122     thread->bRunning = false;
123     thread->pContext = context;
124     thread->pUserData = userData;
125     thread->sName = name;
126     thread->bShutdown = false;
127     // Init ref count, once for the list, once for the caller.
128     thread->refs =2;
129 
130     thread->mainFunction = fpFunction;
131     // initialize running mutex
132     if ((thread->mMutex = Mutex_Create(MUTEX_MODE_RECURSIVE)) == NULL)
133     {
134         free(thread);
135         return NULL;
136     }
137 
138 #ifdef _MSC_VER
139 	thread->hThread = CreateThread(NULL, 0, Thread_MainWrapper, thread, 0, &thread->iThread);
140 #else //_MSC_VER
141     // start thread, check for error
142     if (pthread_create
143         (&thread->iThread, &g_attr, Thread_MainWrapper, thread) != 0)
144     {
145         free (thread);
146         rzb_log (LOG_ERR,
147                  "%s: Failed to launch thread in Thread_Launch due to pthread_create error (%i)", __func__,
148                  errno);
149         return NULL;
150     }
151 #endif //_MSC_VER
152     List_Push(sg_threadList, thread);
153 
154     // done
155     return thread;
156 }
157 
158 SO_PUBLIC void
Thread_Destroy(struct Thread * thread)159 Thread_Destroy (struct Thread *thread)
160 {
161 	ASSERT(thread != NULL);
162 	if (thread == NULL)
163 		return;
164 
165     List_Lock(sg_threadList);
166     Thread_Lock(thread);
167 
168     // Reference count should not drop below 1 as the list holds a ref.
169     ASSERT(thread->refs >= 1);
170 
171     if (thread->refs > 1)
172     {
173         thread->refs--;
174         Thread_Unlock(thread);
175         List_Unlock(sg_threadList);
176         return;
177     }
178 
179     List_Remove(sg_threadList, thread);
180     // destroy running mutex
181     Thread_Unlock(thread);
182     List_Unlock(sg_threadList);
183 
184     Mutex_Destroy (thread->mMutex);
185 
186     free (thread);
187 }
188 
189 SO_PUBLIC bool
Thread_IsRunning(struct Thread * thread)190 Thread_IsRunning (struct Thread *thread)
191 {
192     bool l_bRunning;
193 	ASSERT (thread != NULL);
194 	if (thread == NULL)
195 		return false;
196 
197     Thread_Lock (thread);
198 
199     l_bRunning = thread->bRunning;
200 
201     Thread_Unlock (thread);
202 
203     return l_bRunning;
204 }
205 
206 SO_PUBLIC bool
Thread_IsStopped(struct Thread * thread)207 Thread_IsStopped (struct Thread *thread)
208 {
209     bool l_bShutdown;
210 
211 	ASSERT (thread != NULL);
212 	if (thread == NULL)
213 		return false;
214 
215     Thread_Lock (thread);
216 
217     l_bShutdown = thread->bShutdown;
218 
219     Thread_Unlock (thread);
220 
221     return l_bShutdown;
222 }
223 
224 
225 SO_PUBLIC void
Thread_Stop(struct Thread * thread)226 Thread_Stop (struct Thread *thread)
227 {
228     ASSERT (thread != NULL);
229     if (thread == NULL)
230     	return;
231 
232     Thread_Lock (thread);
233 
234     thread->bShutdown = true;
235 
236     Thread_Unlock (thread);
237 
238 }
239 SO_PUBLIC void
Thread_Interrupt(struct Thread * thread)240 Thread_Interrupt(struct Thread *thread)
241 {
242     ASSERT (thread != NULL);
243     if (thread == NULL)
244     	return;
245 
246     Thread_Lock(thread);
247     Thread_Stop(thread);
248 #ifdef _MSC_VER
249     //UNIMPLEMENTED();
250 #else //_MSC_VER
251     pthread_kill(thread->iThread, SIGUSR1);
252 #endif //_MSC_VER
253     //thread->interrupt(thread);
254     Thread_Unlock(thread);
255 
256 }
257 
258 SO_PUBLIC void
Thread_Join(struct Thread * thread)259 Thread_Join(struct Thread *thread)
260 {
261 	ASSERT(thread != NULL);
262 	if (thread == NULL)
263 		return;
264 
265 #ifdef _MSC_VER
266 	WaitForSingleObject(thread->hThread,INFINITE);
267 #else //_MSC_VER
268     void *ret;
269     int res =0;
270     if ((res = pthread_join(thread->iThread, &ret)) != 0)
271         rzb_log(LOG_ERR, "%s: Failed to join: %i", __func__, res);
272 #endif //_MSC_VER
273 }
274 
275 SO_PUBLIC void
Thread_StopAndJoin(struct Thread * thread)276 Thread_StopAndJoin(struct Thread *thread)
277 {
278    ASSERT (thread != NULL);
279    if (thread == NULL)
280 	   return;
281 
282    Thread_Stop(thread);
283    Thread_Join(thread);
284 }
285 
286 SO_PUBLIC void
Thread_InterruptAndJoin(struct Thread * thread)287 Thread_InterruptAndJoin(struct Thread *thread)
288 {
289     ASSERT (thread != NULL);
290     if (thread == NULL)
291     	return;
292 
293     Thread_Stop(thread);
294     Thread_Interrupt(thread);
295     Thread_Join(thread);
296 }
297 
298 
299 SO_PUBLIC struct RazorbackContext *
Thread_GetContext(struct Thread * thread)300 Thread_GetContext(struct Thread *thread)
301 {
302     struct RazorbackContext *l_pOldContext;
303 
304     ASSERT(thread != NULL);
305     if (thread == NULL)
306     	return NULL;
307 
308     Thread_Lock (thread);
309 
310     l_pOldContext = thread->pContext;
311     Thread_Unlock (thread);
312 
313     return l_pOldContext;
314 }
315 
316 SO_PUBLIC struct RazorbackContext *
Thread_GetCurrentContext(void)317 Thread_GetCurrentContext(void)
318 {
319     struct Thread *thread;
320     struct RazorbackContext *cont;
321     thread = Thread_GetCurrent();
322     if (thread == NULL)
323     {
324         rzb_log(LOG_ERR, "%s: Failed to get current thread", __func__);
325         return NULL;
326     }
327     cont = Thread_GetContext(thread);
328     Thread_Destroy(thread);
329     return cont;
330 }
331 
332 SO_PUBLIC struct RazorbackContext *
Thread_ChangeContext(struct Thread * thread,struct RazorbackContext * context)333 Thread_ChangeContext(struct Thread *thread, struct RazorbackContext *context)
334 {
335     struct RazorbackContext *l_pOldContext;
336 
337     ASSERT(thread != NULL);
338     if (thread == NULL)
339     	return NULL;
340 
341     Thread_Lock (thread);
342 
343     l_pOldContext = thread->pContext;
344     thread->pContext = context;
345 
346     Thread_Unlock (thread);
347 
348     return l_pOldContext;
349 }
350 
351 SO_PUBLIC rzb_thread_t
Thread_GetCurrentId(void)352 Thread_GetCurrentId(void)
353 {
354 #ifdef _MSC_VER
355 	return GetCurrentThreadId();
356 #else
357 	return pthread_self();
358 #endif
359 }
360 
361 SO_PUBLIC struct Thread *
Thread_GetCurrent(void)362 Thread_GetCurrent(void)
363 {
364     struct Thread *l_pRet = NULL;
365 	rzb_thread_t l_tCurrent = Thread_GetCurrentId();
366     l_pRet = (struct Thread *)List_Find(sg_threadList, &l_tCurrent);
367     if (l_pRet == NULL)
368         return NULL;
369 
370     Thread_Lock(l_pRet);
371     l_pRet->refs++;
372     Thread_Unlock(l_pRet);
373     return l_pRet;
374 }
375 
376 SO_PUBLIC void
Thread_Yield(void)377 Thread_Yield(void)
378 {
379 #ifdef _MSC_VER
380     SwitchToThread();
381 #else
382     pthread_yield();
383 #endif
384 }
385 
386 SO_PUBLIC uint32_t
Thread_getCount(void)387 Thread_getCount (void)
388 {
389     uint32_t num;
390     List_Lock(sg_threadList);
391     num = sg_threadList->length;
392     List_Unlock(sg_threadList);
393     return num;
394 }
395 
396 SO_PUBLIC int
Thread_KeyCmp(void * a,void * id)397 Thread_KeyCmp(void *a, void *id)
398 {
399     struct Thread * cA = (struct Thread *)a;
400 	rzb_thread_t cId = *(rzb_thread_t *)id;
401 
402     if (cId == cA->iThread)
403         return 0;
404     else
405         return -1;
406 	return -1;
407 }
408 
409 SO_PUBLIC int
Thread_Cmp(void * a,void * b)410 Thread_Cmp(void *a, void *b)
411 {
412     struct Thread * cA = (struct Thread *)a;
413     struct Thread * cB = (struct Thread *)b;
414     if (a==b)
415         return 0;
416     if (cA->iThread == cB->iThread)
417         return 0;
418     else
419         return -1;
420 	return -1;
421 }
422 
423 static void
Thread_Lock(void * a)424 Thread_Lock(void *a)
425 {
426     struct Thread *t = (struct Thread *)a;
427     Mutex_Lock(t->mMutex);
428 }
429 
430 static void
Thread_Unlock(void * a)431 Thread_Unlock(void *a)
432 {
433     struct Thread *t = (struct Thread *)a;
434     Mutex_Unlock(t->mMutex);
435 }
436 
437 
438 #ifdef _MSC_VER
439 
440 static void
initThreading_win32(void)441 initThreading_win32(void)
442 {
443 	initialized=1;
444 }
445 
446 #else //_MSC_VER
447 
448 static void
initThreading_pthreads(void)449 initThreading_pthreads (void)
450 {
451     struct sigaction act;
452     sigset_t mask;
453 
454     pthread_attr_init (&g_attr);
455     pthread_attr_setdetachstate (&g_attr, PTHREAD_CREATE_JOINABLE);
456 
457     sigemptyset(&mask);
458     sigaddset(&mask, SIGUSR1);
459     act.sa_handler = handler;
460     act.sa_flags = 0;
461     sigemptyset(&act.sa_mask);
462     if (sigaction(SIGUSR1, &act, NULL) < 0)
463         rzb_log(LOG_ERR, "%s: Failed to install signal handler", __func__);
464 }
465 
handler(int sig)466 static void handler(int sig)
467 {
468     rzb_log(LOG_DEBUG, "Thread Got Signal");
469     return;
470 }
471 
472 SO_PUBLIC void
Thread_Interrupt_pthread(struct Thread * thread)473 Thread_Interrupt_pthread(struct Thread *thread)
474 {
475     pthread_kill(thread->iThread, SIGUSR1);
476 }
477 
478 #endif //_MSC_VER
479