Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2012
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / common tools sub-project
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #ifndef GPAC_DISABLE_CORE_TOOLS
27 :
28 : #ifdef GPAC_CONFIG_ANDROID
29 : #include <jni.h>
30 : #endif
31 :
32 : #include <gpac/thread.h>
33 :
34 : #if defined(WIN32) || defined(_WIN32_WCE)
35 :
36 : /*win32 threads*/
37 : #include <windows.h>
38 : typedef HANDLE TH_HANDLE;
39 :
40 : #else
41 : /*pthreads*/
42 : #include <sched.h>
43 : #include <pthread.h>
44 : #include <semaphore.h>
45 : #include <errno.h>
46 : typedef pthread_t TH_HANDLE ;
47 :
48 : #endif
49 :
50 :
51 :
52 : /*********************************************************************
53 : OS-Specific Thread Object
54 : **********************************************************************/
55 : struct __tag_thread
56 : {
57 :
58 : u32 status;
59 : TH_HANDLE threadH;
60 : u32 stackSize;
61 : /* the thread procedure */
62 : u32 (*Run)(void *param);
63 : void *args;
64 : /* lock for signal */
65 : GF_Semaphore *_signal;
66 : #ifndef GPAC_DISABLE_LOG
67 : u32 id;
68 : char *log_name;
69 : #endif
70 : #ifdef GPAC_CONFIG_ANDROID
71 : u32 (*RunBeforeExit)(void *param);
72 : #endif
73 : };
74 :
75 :
76 : #ifndef GPAC_DISABLE_LOG
77 : #include <gpac/list.h>
78 : static GF_List *thread_bank = NULL;
79 :
80 225 : static void log_add_thread(GF_Thread *t)
81 : {
82 225 : if (!thread_bank) thread_bank = gf_list_new();
83 225 : gf_list_add(thread_bank, t);
84 225 : }
85 223 : static void log_del_thread(GF_Thread *t)
86 : {
87 223 : gf_list_del_item(thread_bank, t);
88 223 : if (!gf_list_count(thread_bank)) {
89 97 : gf_list_del(thread_bank);
90 97 : thread_bank = NULL;
91 : }
92 223 : }
93 48121 : static const char *log_th_name(u32 id)
94 : {
95 : u32 i, count;
96 :
97 48121 : if (!id) id = gf_th_id();
98 48121 : count = gf_list_count(thread_bank);
99 126780 : for (i=0; i<count; i++) {
100 97471 : GF_Thread *t = (GF_Thread*)gf_list_get(thread_bank, i);
101 97471 : if (t->id == id) return t->log_name;
102 : }
103 : return "Main Process";
104 : }
105 :
106 : #endif
107 :
108 :
109 : GF_EXPORT
110 225 : GF_Thread *gf_th_new(const char *name)
111 : {
112 225 : GF_Thread *tmp = (GF_Thread*)gf_malloc(sizeof(GF_Thread));
113 : memset(tmp, 0, sizeof(GF_Thread));
114 225 : tmp->status = GF_THREAD_STATUS_STOP;
115 :
116 : #ifndef GPAC_DISABLE_LOG
117 225 : if (name) {
118 225 : tmp->log_name = gf_strdup(name);
119 : } else {
120 : char szN[20];
121 : sprintf(szN, "%p", (void*)tmp);
122 0 : tmp->log_name = gf_strdup(szN);
123 : }
124 225 : log_add_thread(tmp);
125 : #endif
126 225 : return tmp;
127 : }
128 :
129 :
130 : #ifdef GPAC_CONFIG_ANDROID
131 : #include <pthread.h>
132 :
133 : static pthread_key_t currentThreadInfoKey = 0;
134 :
135 : /* Unique allocation of key */
136 : static pthread_once_t currentThreadInfoKey_once = PTHREAD_ONCE_INIT;
137 :
138 : GF_Err gf_register_before_exit_function(GF_Thread *t, u32 (*toRunBeforePthreadExit)(void *param) )
139 : {
140 : if (!t)
141 : return GF_BAD_PARAM;
142 : t->RunBeforeExit = toRunBeforePthreadExit;
143 : return GF_OK;
144 : }
145 :
146 : /* Unique key allocation */
147 : static void currentThreadInfoKey_alloc()
148 : {
149 : int err;
150 : /* We do not use any destructor */
151 : err = pthread_key_create(¤tThreadInfoKey, NULL);
152 : if (err) {
153 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] pthread_key_create() failed with error %d\n", err));
154 : }
155 : }
156 :
157 : GF_Thread * gf_th_current() {
158 : return pthread_getspecific(currentThreadInfoKey);
159 : }
160 :
161 : #endif /* GPAC_CONFIG_ANDROID */
162 :
163 :
164 : #ifdef WIN32
165 : DWORD WINAPI RunThread(void *ptr)
166 : {
167 : DWORD ret = 0;
168 : #else
169 225 : void * RunThread(void *ptr)
170 : {
171 : long int ret = 0;
172 : #endif
173 : GF_Thread *t = (GF_Thread *)ptr;
174 :
175 : /* Signal the caller */
176 225 : if (! t->_signal) goto exit;
177 : #ifdef GPAC_CONFIG_ANDROID
178 : if (pthread_once(¤tThreadInfoKey_once, ¤tThreadInfoKey_alloc) || pthread_setspecific(currentThreadInfoKey, t))
179 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't run thread %s, ID 0x%08x\n", t->log_name, t->id));
180 : #endif /* GPAC_CONFIG_ANDROID */
181 225 : t->status = GF_THREAD_STATUS_RUN;
182 225 : gf_sema_notify(t->_signal, 1);
183 :
184 : #ifndef GPAC_DISABLE_LOG
185 225 : t->id = gf_th_id();
186 225 : GF_LOG(GF_LOG_INFO, GF_LOG_MUTEX, ("[Thread %s] At %d Entering thread proc - thread ID 0x%08x\n", t->log_name, gf_sys_clock(), t->id));
187 : #endif
188 :
189 : /* Each thread has its own seed */
190 225 : gf_rand_init(GF_FALSE);
191 :
192 : /* Run our thread */
193 225 : ret = t->Run(t->args);
194 :
195 222 : exit:
196 : #ifndef GPAC_DISABLE_LOG
197 222 : GF_LOG(GF_LOG_INFO, GF_LOG_MUTEX, ("[Thread %s] At %d Exiting thread proc, return code %d\n", t->log_name, gf_sys_clock(), ret));
198 : #endif
199 223 : t->status = GF_THREAD_STATUS_DEAD;
200 223 : t->Run = NULL;
201 : #ifdef WIN32
202 : if (!CloseHandle(t->threadH)) {
203 : DWORD err = GetLastError();
204 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Thread %s] Couldn't close handle when exiting thread proc, error code: %d\n", t->log_name, err));
205 : }
206 : t->threadH = NULL;
207 : return ret;
208 : #else
209 :
210 : #ifdef GPAC_CONFIG_ANDROID
211 : #ifndef GPAC_DISABLE_LOG
212 : GF_LOG(GF_LOG_INFO, GF_LOG_MUTEX, ("[Thread %s] RunBeforeExit=%p\n", t->log_name, t->RunBeforeExit));
213 : #endif
214 : if (t->RunBeforeExit)
215 : t->RunBeforeExit(t->args);
216 : #endif /* GPAC_CONFIG_ANDROID */
217 223 : pthread_exit((void *)0);
218 : return (void *)ret;
219 : #endif
220 : }
221 :
222 : GF_EXPORT
223 225 : GF_Err gf_th_run(GF_Thread *t, u32 (*Run)(void *param), void *param)
224 : {
225 : #ifdef WIN32
226 : DWORD id;
227 : #else
228 : pthread_attr_t att;
229 : #endif
230 225 : if (!t || t->Run || t->_signal) return GF_BAD_PARAM;
231 225 : t->Run = Run;
232 225 : t->args = param;
233 225 : t->_signal = gf_sema_new(1, 0);
234 :
235 225 : if (!t->_signal)
236 : return GF_IO_ERR;
237 :
238 225 : GF_LOG(GF_LOG_INFO, GF_LOG_MUTEX, ("[Thread %s] Starting\n", t->log_name));
239 :
240 : #ifdef WIN32
241 : t->threadH = CreateThread(NULL, t->stackSize, &(RunThread), (void *)t, 0, &id);
242 : if (t->threadH != NULL) {
243 : #ifdef _MSC_VER
244 : /*add thread name for the msvc debugger*/
245 : #pragma pack(push,8)
246 : typedef struct {
247 : DWORD dwType;
248 : LPCSTR szName;
249 : DWORD dwThreadID;
250 : DWORD dwFlags;
251 : } THREADNAME_INFO;
252 : #pragma pack(pop)
253 : THREADNAME_INFO info;
254 : info.dwType = 0x1000;
255 : info.szName = t->log_name;
256 : info.dwThreadID = id;
257 : info.dwFlags = 0;
258 : __try {
259 : RaiseException(0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
260 : } __except (EXCEPTION_CONTINUE_EXECUTION) {
261 : }
262 : #endif
263 : } else {
264 : #else
265 225 : if ( pthread_attr_init(&att) != 0 ) return GF_IO_ERR;
266 225 : pthread_attr_setdetachstate(&att, PTHREAD_CREATE_JOINABLE);
267 225 : if ( pthread_create(&t->threadH, &att, RunThread, t) != 0 ) {
268 : #endif
269 0 : t->status = GF_THREAD_STATUS_DEAD;
270 0 : return GF_IO_ERR;
271 : }
272 :
273 : /*wait for the child function to call us - do NOT return before, otherwise the thread status would
274 : be unknown*/
275 225 : gf_sema_wait(t->_signal);
276 225 : gf_sema_del(t->_signal);
277 225 : t->_signal = NULL;
278 225 : GF_LOG(GF_LOG_INFO, GF_LOG_MUTEX, ("[Thread %s] Started\n", t->log_name));
279 : return GF_OK;
280 : }
281 :
282 :
283 : /* Stops a thread. If Destroy is not 0, thread is destroyed DANGEROUS as no cleanup */
284 223 : void Thread_Stop(GF_Thread *t, Bool Destroy)
285 : {
286 223 : if (gf_th_status(t) == GF_THREAD_STATUS_RUN) {
287 : #ifdef WIN32
288 : if (Destroy) {
289 : DWORD dw = 1;
290 : BOOL ret = TerminateThread(t->threadH, dw);
291 : if (!ret) {
292 : DWORD err = GetLastError();
293 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Thread %s] Couldn't stop thread ID 0x%08x, error code %d\n", t->log_name, t->id, err));
294 : }
295 : t->threadH = NULL;
296 : } else {
297 : WaitForSingleObject(t->threadH, INFINITE);
298 : }
299 : #else
300 0 : if (Destroy) {
301 : #if defined(GPAC_CONFIG_ANDROID) || defined(PTHREAD_HAS_NO_CANCEL)
302 : if (pthread_kill(t->threadH, SIGQUIT))
303 : #else
304 0 : if (pthread_cancel(t->threadH))
305 : #endif
306 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Thread %s] Couldn't kill thread ID 0x%08x\n", t->log_name, t->id));
307 0 : t->threadH = 0;
308 : } else {
309 : /*gracefully wait for Run to finish*/
310 0 : if (pthread_join(t->threadH, NULL))
311 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Thread %s] pthread_join() returned an error with thread ID 0x%08x\n", t->log_name, t->id));
312 : }
313 : #endif
314 : }
315 223 : t->status = GF_THREAD_STATUS_DEAD;
316 223 : }
317 :
318 : GF_EXPORT
319 1 : void gf_th_stop(GF_Thread *t)
320 : {
321 1 : if (t) Thread_Stop(t, GF_FALSE);
322 1 : }
323 :
324 : GF_EXPORT
325 223 : void gf_th_del(GF_Thread *t)
326 : {
327 223 : Thread_Stop(t, GF_FALSE);
328 : #ifdef WIN32
329 : // if (t->threadH) CloseHandle(t->threadH);
330 : #else
331 : /* It is necessary to free pthread handle */
332 223 : if (t->threadH)
333 223 : pthread_detach(t->threadH);
334 223 : t->threadH = 0;
335 : #endif
336 :
337 : #ifndef GPAC_DISABLE_LOG
338 223 : gf_free(t->log_name);
339 223 : log_del_thread(t);
340 : #endif
341 223 : gf_free(t);
342 223 : }
343 :
344 : GF_EXPORT
345 0 : void gf_th_set_priority(GF_Thread *t, s32 priority)
346 : {
347 : #ifdef WIN32
348 : /*!! in WinCE, changin thread priority is extremely dangerous, it may freeze threads randomly !!*/
349 : #ifndef _WIN32_WCE
350 : int _prio;
351 : BOOL ret;
352 : switch (priority) {
353 : case GF_THREAD_PRIORITY_IDLE:
354 : _prio = THREAD_PRIORITY_IDLE;
355 : break;
356 : case GF_THREAD_PRIORITY_LESS_IDLE:
357 : _prio = THREAD_PRIORITY_IDLE;
358 : break;
359 : case GF_THREAD_PRIORITY_LOWEST:
360 : _prio = THREAD_PRIORITY_LOWEST;
361 : break;
362 : case GF_THREAD_PRIORITY_LOW:
363 : _prio = THREAD_PRIORITY_BELOW_NORMAL;
364 : break;
365 : case GF_THREAD_PRIORITY_NORMAL:
366 : _prio = THREAD_PRIORITY_NORMAL;
367 : break;
368 : case GF_THREAD_PRIORITY_HIGH:
369 : _prio = THREAD_PRIORITY_ABOVE_NORMAL;
370 : break;
371 : case GF_THREAD_PRIORITY_HIGHEST:
372 : _prio = THREAD_PRIORITY_HIGHEST;
373 : break;
374 : default: /*GF_THREAD_PRIORITY_REALTIME -> GF_THREAD_PRIORITY_REALTIME_END*/
375 : _prio = THREAD_PRIORITY_TIME_CRITICAL;
376 : break;
377 : }
378 : ret = SetThreadPriority(t ? t->threadH : GetCurrentThread(), _prio);
379 : if (!ret) {
380 : DWORD err = GetLastError();
381 : GF_LOG(GF_LOG_WARNING, GF_LOG_MUTEX, ("[Thread %s] Couldn't set priority for thread ID 0x%08x, error %d\n", t->log_name, t->id, err));
382 : }
383 : #endif
384 :
385 : #else
386 :
387 : struct sched_param s_par;
388 0 : if (!t) return;
389 :
390 : /* consider this as real-time priority */
391 0 : if (priority > 200) {
392 0 : s_par.sched_priority = priority - 200;
393 0 : if (pthread_setschedparam(t->threadH, SCHED_RR, &s_par))
394 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MUTEX, ("[Thread %s] Couldn't set priority(1) for thread ID 0x%08x\n", t->log_name, t->id));
395 : } else {
396 0 : s_par.sched_priority = priority;
397 0 : if (pthread_setschedparam(t->threadH, SCHED_OTHER, &s_par))
398 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MUTEX, ("[Thread %s] Couldn't set priority(2) for thread ID 0x%08x\n", t->log_name, t->id));
399 : }
400 :
401 : #endif
402 : }
403 :
404 : GF_EXPORT
405 223 : u32 gf_th_status(GF_Thread *t)
406 : {
407 223 : if (!t) return 0;
408 223 : return t->status;
409 : }
410 :
411 :
412 : GF_EXPORT
413 748736017 : u32 gf_th_id()
414 : {
415 : #ifdef WIN32
416 : return ((u32) GetCurrentThreadId());
417 : #else
418 748736017 : return ((u32) (PTR_TO_U_CAST(pthread_self())));
419 : #endif
420 : }
421 :
422 :
423 : /*********************************************************************
424 : OS-Specific Mutex Object
425 : **********************************************************************/
426 : struct __tag_mutex
427 : {
428 : #ifdef WIN32
429 : HANDLE hMutex;
430 : #else
431 : pthread_mutex_t hMutex;
432 : #endif
433 : /* We filter recursive calls (1 thread calling Lock several times in a row only locks
434 : ONCE the mutex. Holder is the current ThreadID of the mutex holder*/
435 : u32 Holder, HolderCount;
436 : #ifndef GPAC_DISABLE_LOG
437 : char *log_name;
438 : #endif
439 : };
440 :
441 :
442 : GF_EXPORT
443 42100 : GF_Mutex *gf_mx_new(const char *name)
444 : {
445 : #ifndef WIN32
446 : pthread_mutexattr_t attr;
447 : #endif
448 42100 : GF_Mutex *tmp = (GF_Mutex*)gf_malloc(sizeof(GF_Mutex));
449 42100 : if (!tmp) return NULL;
450 : memset(tmp, 0, sizeof(GF_Mutex));
451 :
452 : #ifdef WIN32
453 : tmp->hMutex = CreateMutex(NULL, FALSE, NULL);
454 : if (!tmp->hMutex) {
455 : #else
456 42100 : pthread_mutexattr_init(&attr);
457 42100 : if ( pthread_mutex_init(&tmp->hMutex, &attr) != 0 ) {
458 : #endif
459 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't create mutex %s\n", strlen(name) ? name : ""));
460 0 : gf_free(tmp);
461 0 : return NULL;
462 : }
463 :
464 : #ifndef GPAC_DISABLE_LOG
465 42100 : if (name) {
466 42100 : if (stricmp(name, "Logs")) {
467 35859 : tmp->log_name = gf_strdup(name);
468 : }
469 : } else {
470 : char szN[20];
471 : sprintf(szN, "%p", (void*)tmp);
472 0 : tmp->log_name = gf_strdup(szN);
473 : }
474 : #endif
475 :
476 : return tmp;
477 : }
478 :
479 : GF_EXPORT
480 60711 : void gf_mx_del(GF_Mutex *mx)
481 : {
482 : #ifndef WIN32
483 : int err;
484 : #endif
485 60711 : if (!mx) return;
486 :
487 : #ifndef GPAC_DISABLE_LOG
488 35867 : if (mx->Holder && (gf_th_id() != mx->Holder) && mx->log_name) {
489 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MUTEX, ("[Mutex %s] Destroying mutex from thread %s but hold by thread %s\n", mx->log_name, log_th_name(gf_th_id() ), log_th_name(mx->Holder) ));
490 : }
491 : #endif
492 :
493 : #ifdef WIN32
494 : if (!CloseHandle(mx->hMutex)) {
495 : #ifndef GPAC_DISABLE_LOG
496 : if (mx->log_name) {
497 : DWORD err = GetLastError();
498 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex %s] CloseHandle when deleting mutex failed with error code %d\n", mx->log_name, err));
499 : }
500 : #endif
501 : }
502 : #else
503 35867 : err = pthread_mutex_destroy(&mx->hMutex);
504 35866 : if (err) {
505 : #ifndef GPAC_DISABLE_LOG
506 0 : if (mx->log_name) {
507 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex %s] pthread_mutex_destroy failed with error code %d\n", mx->log_name, err));
508 : }
509 : #endif
510 : }
511 :
512 : #endif
513 : #ifndef GPAC_DISABLE_LOG
514 35866 : if (mx->log_name) {
515 29626 : gf_free(mx->log_name);
516 29627 : mx->log_name = NULL;
517 : }
518 : #endif
519 35867 : gf_free(mx);
520 : }
521 :
522 : GF_EXPORT
523 357333075 : void gf_mx_v(GF_Mutex *mx)
524 : {
525 : u32 caller;
526 357333075 : if (!mx) return;
527 355985364 : caller = gf_th_id();
528 :
529 : /*only if we own*/
530 : assert(caller == mx->Holder);
531 355993869 : if (caller != mx->Holder) return;
532 : assert(mx->HolderCount > 0);
533 355995076 : mx->HolderCount -= 1;
534 :
535 355995076 : if (mx->HolderCount == 0) {
536 : #ifndef GPAC_DISABLE_LOG
537 261353859 : if (mx->log_name) {
538 261081100 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MUTEX, ("[Mutex %s] At %d Released by thread %s\n", mx->log_name, gf_sys_clock(), log_th_name(mx->Holder) ));
539 : }
540 : #endif
541 261341478 : mx->Holder = 0;
542 : #ifdef WIN32
543 : {
544 : BOOL ret = ReleaseMutex(mx->hMutex);
545 : if (!ret) {
546 : #ifndef GPAC_DISABLE_LOG
547 : if (mx->log_name) {
548 : DWORD err = GetLastError();
549 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't release mutex (thread %s, error %d)\n", log_th_name(mx->Holder), err));
550 : }
551 : #endif
552 :
553 : }
554 : }
555 : #else
556 261341478 : if (pthread_mutex_unlock(&mx->hMutex)) {
557 : #ifndef GPAC_DISABLE_LOG
558 0 : if (mx->log_name) {
559 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't release mutex (thread %s)\n", log_th_name(mx->Holder)));
560 : }
561 : #endif
562 : }
563 : #endif
564 : }
565 : }
566 :
567 : GF_EXPORT
568 357320483 : u32 gf_mx_p(GF_Mutex *mx)
569 : {
570 : #ifndef WIN32
571 : int retCode;
572 : #endif
573 : u32 caller;
574 : #ifndef GPAC_DISABLE_LOG
575 : const char *mx_holder_name;
576 : #endif
577 :
578 357320483 : if (!mx) return 1;
579 355974589 : caller = gf_th_id();
580 355993128 : if (caller == mx->Holder) {
581 94643722 : mx->HolderCount += 1;
582 94643722 : return 1;
583 : }
584 :
585 : #ifndef GPAC_DISABLE_LOG
586 261349406 : mx_holder_name = mx->Holder ? log_th_name(mx->Holder) : "none";
587 261349066 : if (mx->Holder && mx->log_name)
588 10252 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MUTEX, ("[Mutex %s] At %d Thread %s waiting a release from thread %s\n", mx->log_name, gf_sys_clock(), log_th_name(caller), mx_holder_name ));
589 : #endif
590 :
591 : #ifdef WIN32
592 : switch (WaitForSingleObject(mx->hMutex, INFINITE)) {
593 : case WAIT_ABANDONED:
594 : case WAIT_TIMEOUT:
595 : return 0;
596 : default:
597 : break;
598 : }
599 : #else
600 261349066 : retCode = pthread_mutex_lock(&mx->hMutex);
601 261355656 : if (retCode != 0 ) {
602 : #ifndef GPAC_DISABLE_LOG
603 0 : if (mx->log_name) {
604 0 : if (retCode == EINVAL)
605 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex %p=%s] Not properly initialized.\n", mx, mx->log_name));
606 0 : if (retCode == EDEADLK)
607 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex %p=%s] Deadlock detected.\n", mx, mx->log_name));
608 : }
609 : #endif /* GPAC_DISABLE_LOG */
610 : assert(0);
611 : return 0;
612 : }
613 : #endif /* NOT WIN32 */
614 261355656 : mx->HolderCount = 1;
615 261355656 : mx->Holder = caller;
616 : #ifndef GPAC_DISABLE_LOG
617 261355656 : if (mx->log_name) {
618 261082706 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MUTEX, ("[Mutex %s] At %d Grabbed by thread %s\n", mx->log_name, gf_sys_clock(), log_th_name(mx->Holder) ));
619 : }
620 : #endif
621 : return 1;
622 : }
623 :
624 :
625 : GF_EXPORT
626 1 : s32 gf_mx_get_num_locks(GF_Mutex *mx)
627 : {
628 : u32 caller;
629 1 : if (!mx) return 0;
630 0 : caller = gf_th_id();
631 0 : if (caller == mx->Holder) {
632 0 : return mx->HolderCount;
633 : }
634 : return -1;
635 : }
636 :
637 : GF_EXPORT
638 5270 : Bool gf_mx_try_lock(GF_Mutex *mx)
639 : {
640 : u32 caller;
641 5270 : if (!mx) return GF_FALSE;
642 5270 : caller = gf_th_id();
643 5270 : if (caller == mx->Holder) {
644 1659 : mx->HolderCount += 1;
645 1659 : return GF_TRUE;
646 : }
647 :
648 : #ifdef WIN32
649 : /*is the object signaled?*/
650 : switch (WaitForSingleObject(mx->hMutex, 0)) {
651 : case WAIT_OBJECT_0:
652 : break;
653 : case WAIT_ABANDONED:
654 : case WAIT_TIMEOUT:
655 : #ifndef GPAC_DISABLE_LOG
656 : if (mx->log_name) {
657 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MUTEX, ("[Mutex %s] At %d Couldn't be locked by thread %s (grabbed by thread %s)\n", mx->log_name, gf_sys_clock(), log_th_name(caller), log_th_name(mx->Holder) ));
658 : }
659 : #endif
660 : return GF_FALSE;
661 : case WAIT_FAILED:
662 : #ifndef GPAC_DISABLE_LOG
663 : if (mx->log_name) {
664 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex %s] At %d WaitForSingleObject failed\n", mx->log_name, gf_sys_clock()));
665 : return GF_FALSE;
666 : }
667 : #endif
668 : default:
669 : assert(0);
670 : return GF_FALSE;
671 : }
672 : #else
673 3611 : if (pthread_mutex_trylock(&mx->hMutex) != 0 ) {
674 : #ifndef GPAC_DISABLE_LOG
675 2 : if (mx->log_name) {
676 2 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MUTEX, ("[Mutex %s] At %d Couldn't release it for thread %s (grabbed by thread %s)\n", mx->log_name, gf_sys_clock(), log_th_name(caller), log_th_name(mx->Holder) ));
677 : }
678 : #endif
679 : return GF_FALSE;
680 : }
681 : #endif
682 3609 : mx->Holder = caller;
683 3609 : mx->HolderCount = 1;
684 : #ifndef GPAC_DISABLE_LOG
685 3609 : if (mx->log_name) {
686 3609 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MUTEX, ("[Mutex %s] At %d Grabbed by thread %s\n", mx->log_name, gf_sys_clock(), log_th_name(mx->Holder) ));
687 : }
688 : #endif
689 : return GF_TRUE;
690 : }
691 :
692 :
693 : /*********************************************************************
694 : OS-Specific Semaphore Object
695 : **********************************************************************/
696 : struct __tag_semaphore
697 : {
698 : #ifdef WIN32
699 : HANDLE hSemaphore;
700 : #else
701 : sem_t *hSemaphore;
702 : sem_t SemaData;
703 : #if defined(__DARWIN__) || defined(__APPLE__)
704 : char *SemName;
705 : #endif
706 : #endif
707 : };
708 :
709 : GF_EXPORT
710 436 : GF_Semaphore *gf_sema_new(u32 MaxCount, u32 InitCount)
711 : {
712 436 : GF_Semaphore *tmp = (GF_Semaphore*)gf_malloc(sizeof(GF_Semaphore));
713 436 : if (!tmp) {
714 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("Couldn't allocate semaphore\n"));
715 : return NULL;
716 : }
717 :
718 : #if defined(WIN32)
719 : MaxCount = MIN(MaxCount, GF_INT_MAX);
720 : tmp->hSemaphore = CreateSemaphore(NULL, InitCount, MIN(MaxCount, GF_INT_MAX), NULL);
721 : if (!tmp->hSemaphore) {
722 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("Couldn't create semaphore\n"));
723 : gf_free(tmp);
724 : return NULL;
725 : }
726 :
727 : #elif defined(__DARWIN__) || defined(__APPLE__)
728 : /* sem_init isn't supported on Mac OS X 10.3 & 10.4; it returns ENOSYS
729 : To get around this, a NAMED semaphore needs to be used
730 : sem_t *sem_open(const char *name, int oflag, ...);
731 : http://users.evitech.fi/~hannuvl/ke04/reaaliaika_ohj/memmap_named_semaphores.c
732 : */
733 : {
734 : char semaName[40];
735 : u64 add = (u64) tmp ^ gf_net_get_utc() ^ gf_rand();
736 : sprintf(semaName,"GPAC_SEM"LLU, add);
737 : tmp->SemName = gf_strdup(semaName);
738 : }
739 : sem_unlink(tmp->SemName);
740 : tmp->hSemaphore = sem_open(tmp->SemName, O_CREAT, S_IRUSR|S_IWUSR, InitCount);
741 : if (tmp->hSemaphore==SEM_FAILED) {
742 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("Couldn't init semaphore: error %d\n", errno));
743 : gf_free(tmp);
744 : return NULL;
745 : }
746 : #else
747 436 : if (sem_init(&tmp->SemaData, 0, InitCount) < 0 ) {
748 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("Couldn't init semaphore: error %d\n", errno));
749 0 : gf_free(tmp);
750 0 : return NULL;
751 : }
752 436 : tmp->hSemaphore = &tmp->SemaData;
753 : #endif
754 436 : return tmp;
755 : }
756 :
757 : GF_EXPORT
758 434 : void gf_sema_del(GF_Semaphore *sm)
759 : {
760 434 : if (!sm) return;
761 : #if defined(WIN32)
762 : if (!CloseHandle(sm->hSemaphore)) {
763 : DWORD err = GetLastError();
764 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] CloseHandle when deleting semaphore failed with error code %d\n", err));
765 : }
766 : #elif defined(__DARWIN__) || defined(__APPLE__)
767 : sem_close(sm->hSemaphore);
768 : sem_unlink(sm->SemName);
769 : gf_free(sm->SemName);
770 : #else
771 434 : sem_destroy(sm->hSemaphore);
772 : #endif
773 434 : gf_free(sm);
774 : }
775 :
776 : GF_EXPORT
777 1978559 : Bool gf_sema_notify(GF_Semaphore *sm, u32 NbRelease)
778 : {
779 : #ifndef WIN32
780 : sem_t *hSem;
781 : #else
782 : u32 prevCount=0;
783 : #endif
784 :
785 1978559 : if (!sm) return GF_FALSE;
786 :
787 : #if defined(WIN32)
788 : ReleaseSemaphore(sm->hSemaphore, NbRelease, (LPLONG) &prevCount);
789 : #else
790 :
791 1973187 : hSem = sm->hSemaphore;
792 :
793 7180992 : while (NbRelease) {
794 3237065 : if (sem_post(hSem) < 0) return GF_FALSE;
795 3234618 : NbRelease -= 1;
796 : }
797 : #endif
798 : return GF_TRUE;
799 : }
800 :
801 : GF_EXPORT
802 1397333 : Bool gf_sema_wait(GF_Semaphore *sm)
803 : {
804 1397333 : if (!sm) return GF_FALSE;
805 : #ifdef WIN32
806 : switch (WaitForSingleObject(sm->hSemaphore, INFINITE) ) {
807 : case WAIT_FAILED:
808 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Semaphore] failed to wait: %d\n", GetLastError() ));
809 : return GF_FALSE;
810 : case WAIT_ABANDONED:
811 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Semaphore] failed to wait: owner thread exit\n"));
812 : return GF_FALSE;
813 : default:
814 : break;
815 : }
816 :
817 : #else
818 1397362 : if (sem_wait(sm->hSemaphore) < 0) {
819 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Semaphore] failed to wait for semaphore: %d\n", errno));
820 : return GF_FALSE;
821 : }
822 : #endif
823 : return GF_TRUE;
824 : }
825 :
826 : GF_EXPORT
827 4306 : Bool gf_sema_wait_for(GF_Semaphore *sm, u32 TimeOut)
828 : {
829 : #ifdef WIN32
830 : if (!sm) return GF_FALSE;
831 : if (WaitForSingleObject(sm->hSemaphore, TimeOut) == WAIT_TIMEOUT) return GF_FALSE;
832 : return GF_TRUE;
833 : #else
834 : sem_t *hSem;
835 4306 : if (!sm) return GF_FALSE;
836 4344 : hSem = sm->hSemaphore;
837 :
838 4344 : if (!TimeOut) {
839 0 : if (!sem_trywait(hSem)) return GF_TRUE;
840 0 : return GF_FALSE;
841 : }
842 :
843 : #if defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_IOS)
844 :
845 : TimeOut += gf_sys_clock();
846 : do {
847 : if (!sem_trywait(hSem)) return GF_TRUE;
848 : //OSX/ios don't support sem_timedwait, so we sleep until the sem is notified or the timeout is done
849 : //don't be too greedy, use 5ms sleep
850 : //another approach would be to spawn a thread, use sem_wait and send an interrupt on the sema after the timeout ...
851 : gf_sleep(1);
852 : } while (gf_sys_clock() < TimeOut);
853 : return GF_FALSE;
854 : #else
855 : struct timespec tv;
856 : u32 secs;
857 4344 : if (clock_gettime(CLOCK_REALTIME, &tv) == -1) return GF_FALSE;
858 4315 : secs = TimeOut/1000;
859 4315 : tv.tv_sec += secs;
860 4315 : tv.tv_nsec += (u64) ((TimeOut-secs*1000)) * 1000000;
861 4315 : if (tv.tv_nsec>1000000000) {
862 190 : tv.tv_nsec -= 1000000000;
863 190 : tv.tv_sec += 1;
864 : }
865 4315 : if (!sem_timedwait(hSem, &tv)) return GF_TRUE;
866 :
867 0 : return GF_FALSE;
868 : #endif
869 :
870 : #endif
871 : }
872 :
873 : #endif
|