blob: 531be6bf2fc635a6f5e79aa57e6c6ad16b808b5a [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pj/os.h>
21#include <pj/pool.h>
22#include <pj/log.h>
23#include <pj/string.h>
24#include <pj/guid.h>
25#include <pj/rand.h>
26#include <pj/assert.h>
27#include <pj/errno.h>
28#include <pj/except.h>
29#include <stddef.h>
30#include <stdlib.h>
31#include <stdio.h>
32
33#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
34# include <winsock.h>
35#endif
36
37#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
38# include <winsock2.h>
39#endif
40
41/* Activate mutex related logging if PJ_DEBUG_MUTEX is set, otherwise
42 * use default level 6 logging.
43 */
44#if defined(PJ_DEBUG_MUTEX) && PJ_DEBUG_MUTEX
45# undef PJ_DEBUG
46# define PJ_DEBUG 1
47# define LOG_MUTEX(expr) PJ_LOG(5,expr)
48#else
49# define LOG_MUTEX(expr) PJ_LOG(6,expr)
50#endif
51
52#define THIS_FILE "os_core_win32.c"
53
54/*
55 * Implementation of pj_thread_t.
56 */
57struct pj_thread_t
58{
59 char obj_name[PJ_MAX_OBJ_NAME];
60 HANDLE hthread;
61 DWORD idthread;
62 pj_thread_proc *proc;
63 void *arg;
64
65#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
66 pj_uint32_t stk_size;
67 pj_uint32_t stk_max_usage;
68 char *stk_start;
69 const char *caller_file;
70 int caller_line;
71#endif
72};
73
74
75/*
76 * Implementation of pj_mutex_t.
77 */
78struct pj_mutex_t
79{
80#if PJ_WIN32_WINNT >= 0x0400
81 CRITICAL_SECTION crit;
82#else
83 HANDLE hMutex;
84#endif
85 char obj_name[PJ_MAX_OBJ_NAME];
86#if PJ_DEBUG
87 int nesting_level;
88 pj_thread_t *owner;
89#endif
90};
91
92/*
93 * Implementation of pj_sem_t.
94 */
95typedef struct pj_sem_t
96{
97 HANDLE hSemaphore;
98 char obj_name[PJ_MAX_OBJ_NAME];
99} pj_mem_t;
100
101
102/*
103 * Implementation of pj_event_t.
104 */
105struct pj_event_t
106{
107 HANDLE hEvent;
108 char obj_name[PJ_MAX_OBJ_NAME];
109};
110
111/*
112 * Implementation of pj_atomic_t.
113 */
114struct pj_atomic_t
115{
116 long value;
117};
118
119/*
120 * Flag and reference counter for PJLIB instance.
121 */
122static int initialized;
123
124/*
125 * Static global variables.
126 */
127static pj_thread_desc main_thread;
128static long thread_tls_id = -1;
129static pj_mutex_t critical_section_mutex;
130static unsigned atexit_count;
131static void (*atexit_func[32])(void);
132
133/*
134 * Some static prototypes.
135 */
136static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name);
137
138
139/*
140 * pj_init(void).
141 * Init PJLIB!
142 */
143PJ_DEF(pj_status_t) pj_init(void)
144{
145 WSADATA wsa;
146 char dummy_guid[32]; /* use maximum GUID length */
147 pj_str_t guid;
148 pj_status_t rc;
149
150 /* Check if PJLIB have been initialized */
151 if (initialized) {
152 ++initialized;
153 return PJ_SUCCESS;
154 }
155
156 /* Init Winsock.. */
157 if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
158 return PJ_RETURN_OS_ERROR(WSAGetLastError());
159 }
160
161 /* Init this thread's TLS. */
162 if ((rc=pj_thread_init()) != PJ_SUCCESS) {
163 return rc;
164 }
165
166 /* Init logging */
167 pj_log_init();
168
169 /* Init random seed. */
170 /* Or probably not. Let application in charge of this */
171 /* pj_srand( GetCurrentProcessId() ); */
172
173 /* Initialize critical section. */
174 if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS)
175 return rc;
176
177 /* Startup GUID. */
178 guid.ptr = dummy_guid;
179 pj_generate_unique_string( &guid );
180
181 /* Initialize exception ID for the pool.
182 * Must do so after critical section is configured.
183 */
184 rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
185 if (rc != PJ_SUCCESS)
186 return rc;
187
188 /* Startup timestamp */
189#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
190 {
191 pj_timestamp dummy_ts;
192 if ((rc=pj_get_timestamp_freq(&dummy_ts)) != PJ_SUCCESS) {
193 return rc;
194 }
195 if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) {
196 return rc;
197 }
198 }
199#endif
200
201 /* Flag PJLIB as initialized */
202 ++initialized;
203 pj_assert(initialized == 1);
204
205 PJ_LOG(4,(THIS_FILE, "pjlib %s for win32 initialized",
206 PJ_VERSION));
207
208 return PJ_SUCCESS;
209}
210
211/*
212 * pj_atexit()
213 */
214PJ_DEF(pj_status_t) pj_atexit(void (*func)(void))
215{
216 if (atexit_count >= PJ_ARRAY_SIZE(atexit_func))
217 return PJ_ETOOMANY;
218
219 atexit_func[atexit_count++] = func;
220 return PJ_SUCCESS;
221}
222
223
224/*
225 * pj_shutdown(void)
226 */
227PJ_DEF(void) pj_shutdown()
228{
229 int i;
230
231 /* Only perform shutdown operation when 'initialized' reaches zero */
232 pj_assert(initialized > 0);
233 if (--initialized != 0)
234 return;
235
236 /* Display stack usage */
237#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
238 {
239 pj_thread_t *rec = (pj_thread_t*)main_thread;
240 PJ_LOG(5,(rec->obj_name, "Main thread stack max usage=%u by %s:%d",
241 rec->stk_max_usage, rec->caller_file, rec->caller_line));
242 }
243#endif
244
245 /* Call atexit() functions */
246 for (i=atexit_count-1; i>=0; --i) {
247 (*atexit_func[i])();
248 }
249 atexit_count = 0;
250
251 /* Free exception ID */
252 if (PJ_NO_MEMORY_EXCEPTION != -1) {
253 pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION);
254 PJ_NO_MEMORY_EXCEPTION = -1;
255 }
256
257 /* Destroy PJLIB critical section */
258 pj_mutex_destroy(&critical_section_mutex);
259
260 /* Free PJLIB TLS */
261 if (thread_tls_id != -1) {
262 pj_thread_local_free(thread_tls_id);
263 thread_tls_id = -1;
264 }
265
266 /* Clear static variables */
267 pj_errno_clear_handlers();
268
269 /* Ticket #1132: Assertion when (re)starting PJLIB on different thread */
270 pj_bzero(main_thread, sizeof(main_thread));
271
272 /* Shutdown Winsock */
273 WSACleanup();
274}
275
276
277/*
278 * pj_getpid(void)
279 */
280PJ_DEF(pj_uint32_t) pj_getpid(void)
281{
282 PJ_CHECK_STACK();
283 return GetCurrentProcessId();
284}
285
286/*
287 * Check if this thread has been registered to PJLIB.
288 */
289PJ_DEF(pj_bool_t) pj_thread_is_registered(void)
290{
291 return pj_thread_local_get(thread_tls_id) != 0;
292}
293
294
295/*
296 * Get thread priority value for the thread.
297 */
298PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread)
299{
300 return GetThreadPriority(thread->hthread);
301}
302
303
304/*
305 * Set the thread priority.
306 */
307PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio)
308{
309#if PJ_HAS_THREADS
310 PJ_ASSERT_RETURN(thread, PJ_EINVAL);
311 PJ_ASSERT_RETURN(prio>=THREAD_PRIORITY_IDLE &&
312 prio<=THREAD_PRIORITY_TIME_CRITICAL,
313 PJ_EINVAL);
314
315 if (SetThreadPriority(thread->hthread, prio) == FALSE)
316 return PJ_RETURN_OS_ERROR(GetLastError());
317
318 return PJ_SUCCESS;
319
320#else
321 PJ_UNUSED_ARG(thread);
322 PJ_UNUSED_ARG(prio);
323 pj_assert("pj_thread_set_prio() called in non-threading mode!");
324 return PJ_EINVALIDOP;
325#endif
326}
327
328
329/*
330 * Get the lowest priority value available on this system.
331 */
332PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread)
333{
334 PJ_UNUSED_ARG(thread);
335 return THREAD_PRIORITY_IDLE;
336}
337
338
339/*
340 * Get the highest priority value available on this system.
341 */
342PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread)
343{
344 PJ_UNUSED_ARG(thread);
345 return THREAD_PRIORITY_TIME_CRITICAL;
346}
347
348
349/*
350 * Get native thread handle
351 */
352PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread)
353{
354 PJ_ASSERT_RETURN(thread, NULL);
355
356#if PJ_HAS_THREADS
357 return thread->hthread;
358#else
359 pj_assert("pj_thread_is_registered() called in non-threading mode!");
360 return NULL;
361#endif
362}
363
364/*
365 * pj_thread_register(..)
366 */
367PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
368 pj_thread_desc desc,
369 pj_thread_t **thread_ptr)
370{
371 char stack_ptr;
372 pj_status_t rc;
373 pj_thread_t *thread = (pj_thread_t *)desc;
374 pj_str_t thread_name = pj_str((char*)cstr_thread_name);
375
376 /* Size sanity check. */
377 if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
378 pj_assert(!"Not enough pj_thread_desc size!");
379 return PJ_EBUG;
380 }
381
382 /* If a thread descriptor has been registered before, just return it. */
383 if (pj_thread_local_get (thread_tls_id) != 0) {
384 // 2006-02-26 bennylp:
385 // This wouldn't work in all cases!.
386 // If thread is created by external module (e.g. sound thread),
387 // thread may be reused while the pool used for the thread descriptor
388 // has been deleted by application.
389 //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
390 //return PJ_SUCCESS;
391 }
392
393 /* Initialize and set the thread entry. */
394 pj_bzero(desc, sizeof(struct pj_thread_t));
395 thread->hthread = GetCurrentThread();
396 thread->idthread = GetCurrentThreadId();
397
398#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
399 thread->stk_start = &stack_ptr;
400 thread->stk_size = 0xFFFFFFFFUL;
401 thread->stk_max_usage = 0;
402#else
403 stack_ptr = '\0';
404#endif
405
406 if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
407 pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name),
408 cstr_thread_name, thread->idthread);
409 else
410 pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name),
411 "thr%p", (void*)(pj_ssize_t)thread->idthread);
412
413 rc = pj_thread_local_set(thread_tls_id, thread);
414 if (rc != PJ_SUCCESS)
415 return rc;
416
417 *thread_ptr = thread;
418 return PJ_SUCCESS;
419}
420
421/*
422 * pj_thread_init(void)
423 */
424pj_status_t pj_thread_init(void)
425{
426 pj_status_t rc;
427 pj_thread_t *thread;
428
429 rc = pj_thread_local_alloc(&thread_tls_id);
430 if (rc != PJ_SUCCESS)
431 return rc;
432
433 return pj_thread_register("thr%p", main_thread, &thread);
434}
435
436static DWORD WINAPI thread_main(void *param)
437{
438 pj_thread_t *rec = param;
439 DWORD result;
440
441#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
442 rec->stk_start = (char*)&rec;
443#endif
444
445 if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) {
446 pj_assert(!"TLS is not set (pj_init() error?)");
447 }
448
449 PJ_LOG(6,(rec->obj_name, "Thread started"));
450
451 result = (*rec->proc)(rec->arg);
452
453 PJ_LOG(6,(rec->obj_name, "Thread quitting"));
454#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
455 PJ_LOG(5,(rec->obj_name, "Thread stack max usage=%u by %s:%d",
456 rec->stk_max_usage, rec->caller_file, rec->caller_line));
457#endif
458
459 return (DWORD)result;
460}
461
462/*
463 * pj_thread_create(...)
464 */
465PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
466 const char *thread_name,
467 pj_thread_proc *proc,
468 void *arg,
469 pj_size_t stack_size,
470 unsigned flags,
471 pj_thread_t **thread_ptr)
472{
473 DWORD dwflags = 0;
474 pj_thread_t *rec;
475
476 PJ_CHECK_STACK();
477 PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL);
478
479 /* Set flags */
480 if (flags & PJ_THREAD_SUSPENDED)
481 dwflags |= CREATE_SUSPENDED;
482
483 /* Create thread record and assign name for the thread */
484 rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
485 if (!rec)
486 return PJ_ENOMEM;
487
488 /* Set name. */
489 if (!thread_name)
490 thread_name = "thr%p";
491
492 if (strchr(thread_name, '%')) {
493 pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
494 } else {
495 pj_ansi_strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
496 rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
497 }
498
499 PJ_LOG(6, (rec->obj_name, "Thread created"));
500
501#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
502 rec->stk_size = stack_size ? (pj_uint32_t)stack_size : 0xFFFFFFFFUL;
503 rec->stk_max_usage = 0;
504#endif
505
506 /* Create the thread. */
507 rec->proc = proc;
508 rec->arg = arg;
509 rec->hthread = CreateThread(NULL, stack_size,
510 thread_main, rec,
511 dwflags, &rec->idthread);
512 if (rec->hthread == NULL)
513 return PJ_RETURN_OS_ERROR(GetLastError());
514
515 /* Success! */
516 *thread_ptr = rec;
517 return PJ_SUCCESS;
518}
519
520/*
521 * pj_thread-get_name()
522 */
523PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
524{
525 pj_thread_t *rec = (pj_thread_t*)p;
526
527 PJ_CHECK_STACK();
528 PJ_ASSERT_RETURN(p, "");
529
530 return rec->obj_name;
531}
532
533/*
534 * pj_thread_resume()
535 */
536PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
537{
538 pj_thread_t *rec = (pj_thread_t*)p;
539
540 PJ_CHECK_STACK();
541 PJ_ASSERT_RETURN(p, PJ_EINVAL);
542
543 if (ResumeThread(rec->hthread) == (DWORD)-1)
544 return PJ_RETURN_OS_ERROR(GetLastError());
545 else
546 return PJ_SUCCESS;
547}
548
549/*
550 * pj_thread_this()
551 */
552PJ_DEF(pj_thread_t*) pj_thread_this(void)
553{
554 pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
555
556 if (rec == NULL) {
557 pj_assert(!"Calling pjlib from unknown/external thread. You must "
558 "register external threads with pj_thread_register() "
559 "before calling any pjlib functions.");
560 }
561
562 /*
563 * MUST NOT check stack because this function is called
564 * by PJ_CHECK_STACK() itself!!!
565 *
566 */
567
568 return rec;
569}
570
571/*
572 * pj_thread_join()
573 */
574PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
575{
576 pj_thread_t *rec = (pj_thread_t *)p;
577 DWORD rc;
578
579 PJ_CHECK_STACK();
580 PJ_ASSERT_RETURN(p, PJ_EINVAL);
581
582 if (p == pj_thread_this())
583 return PJ_ECANCELLED;
584
585 PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
586
587 rc = WaitForSingleObject(rec->hthread, INFINITE);
588
589 if (rc==WAIT_OBJECT_0)
590 return PJ_SUCCESS;
591 else if (rc==WAIT_TIMEOUT)
592 return PJ_ETIMEDOUT;
593 else
594 return PJ_RETURN_OS_ERROR(GetLastError());
595}
596
597/*
598 * pj_thread_destroy()
599 */
600PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
601{
602 pj_thread_t *rec = (pj_thread_t *)p;
603
604 PJ_CHECK_STACK();
605 PJ_ASSERT_RETURN(p, PJ_EINVAL);
606
607 if (CloseHandle(rec->hthread) == TRUE)
608 return PJ_SUCCESS;
609 else
610 return PJ_RETURN_OS_ERROR(GetLastError());
611}
612
613/*
614 * pj_thread_sleep()
615 */
616PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
617{
618 PJ_CHECK_STACK();
619 Sleep(msec);
620 return PJ_SUCCESS;
621}
622
623#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
624/*
625 * pj_thread_check_stack()
626 * Implementation for PJ_CHECK_STACK()
627 */
628PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
629{
630 char stk_ptr;
631 pj_uint32_t usage;
632 pj_thread_t *thread = pj_thread_this();
633
634 pj_assert(thread);
635
636 /* Calculate current usage. */
637 usage = (&stk_ptr > thread->stk_start) ?
638 (pj_uint32_t)(&stk_ptr - thread->stk_start) :
639 (pj_uint32_t)(thread->stk_start - &stk_ptr);
640
641 /* Assert if stack usage is dangerously high. */
642 pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
643
644 /* Keep statistic. */
645 if (usage > thread->stk_max_usage) {
646 thread->stk_max_usage = usage;
647 thread->caller_file = file;
648 thread->caller_line = line;
649 }
650
651}
652
653/*
654 * pj_thread_get_stack_max_usage()
655 */
656PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
657{
658 return thread->stk_max_usage;
659}
660
661/*
662 * pj_thread_get_stack_info()
663 */
664PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
665 const char **file,
666 int *line )
667{
668 pj_assert(thread);
669
670 *file = thread->caller_file;
671 *line = thread->caller_line;
672 return 0;
673}
674
675#endif /* PJ_OS_HAS_CHECK_STACK */
676
677
678///////////////////////////////////////////////////////////////////////////////
679
680/*
681 * pj_atomic_create()
682 */
683PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
684 pj_atomic_value_t initial,
685 pj_atomic_t **atomic_ptr)
686{
687 pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t));
688 if (!atomic_var)
689 return PJ_ENOMEM;
690
691 atomic_var->value = initial;
692 *atomic_ptr = atomic_var;
693
694 return PJ_SUCCESS;
695}
696
697/*
698 * pj_atomic_destroy()
699 */
700PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
701{
702 PJ_UNUSED_ARG(var);
703 PJ_ASSERT_RETURN(var, PJ_EINVAL);
704
705 return 0;
706}
707
708/*
709 * pj_atomic_set()
710 */
711PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value)
712{
713 PJ_CHECK_STACK();
714
715 InterlockedExchange(&atomic_var->value, value);
716}
717
718/*
719 * pj_atomic_get()
720 */
721PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
722{
723 PJ_CHECK_STACK();
724 PJ_ASSERT_RETURN(atomic_var, 0);
725
726 return atomic_var->value;
727}
728
729/*
730 * pj_atomic_inc_and_get()
731 */
732PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
733{
734 PJ_CHECK_STACK();
735
736#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
737 return InterlockedIncrement(&atomic_var->value);
738#else
739 return InterlockedIncrement(&atomic_var->value);
740#endif
741}
742
743/*
744 * pj_atomic_inc()
745 */
746PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
747{
748 pj_atomic_inc_and_get(atomic_var);
749}
750
751/*
752 * pj_atomic_dec_and_get()
753 */
754PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
755{
756 PJ_CHECK_STACK();
757
758#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
759 return InterlockedDecrement(&atomic_var->value);
760#else
761 return InterlockedDecrement(&atomic_var->value);
762#endif
763}
764
765/*
766 * pj_atomic_dec()
767 */
768PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
769{
770 pj_atomic_dec_and_get(atomic_var);
771}
772
773/*
774 * pj_atomic_add()
775 */
776PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
777 pj_atomic_value_t value )
778{
779#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
780 InterlockedExchangeAdd( &atomic_var->value, value );
781#else
782 InterlockedExchangeAdd( &atomic_var->value, value );
783#endif
784}
785
786/*
787 * pj_atomic_add_and_get()
788 */
789PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
790 pj_atomic_value_t value)
791{
792#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
793 long oldValue = InterlockedExchangeAdd( &atomic_var->value, value);
794 return oldValue + value;
795#else
796 long oldValue = InterlockedExchangeAdd( &atomic_var->value, value);
797 return oldValue + value;
798#endif
799}
800
801///////////////////////////////////////////////////////////////////////////////
802/*
803 * pj_thread_local_alloc()
804 */
805PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
806{
807 PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL);
808
809 //Can't check stack because this function is called in the
810 //beginning before main thread is initialized.
811 //PJ_CHECK_STACK();
812
813 *index = TlsAlloc();
814
815 if (*index == TLS_OUT_OF_INDEXES)
816 return PJ_RETURN_OS_ERROR(GetLastError());
817 else
818 return PJ_SUCCESS;
819}
820
821/*
822 * pj_thread_local_free()
823 */
824PJ_DEF(void) pj_thread_local_free(long index)
825{
826 PJ_CHECK_STACK();
827 TlsFree(index);
828}
829
830/*
831 * pj_thread_local_set()
832 */
833PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
834{
835 BOOL rc;
836
837 //Can't check stack because this function is called in the
838 //beginning before main thread is initialized.
839 //PJ_CHECK_STACK();
840 rc = TlsSetValue(index, value);
841 return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
842}
843
844/*
845 * pj_thread_local_get()
846 */
847PJ_DEF(void*) pj_thread_local_get(long index)
848{
849 //Can't check stack because this function is called
850 //by PJ_CHECK_STACK() itself!!!
851 //PJ_CHECK_STACK();
852 return TlsGetValue(index);
853}
854
855///////////////////////////////////////////////////////////////////////////////
856static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name)
857{
858
859 PJ_CHECK_STACK();
860
861#if PJ_WIN32_WINNT >= 0x0400
862 InitializeCriticalSection(&mutex->crit);
863#else
864 mutex->hMutex = CreateMutex(NULL, FALSE, NULL);
865 if (!mutex->hMutex) {
866 return PJ_RETURN_OS_ERROR(GetLastError());
867 }
868#endif
869
870#if PJ_DEBUG
871 /* Set owner. */
872 mutex->nesting_level = 0;
873 mutex->owner = NULL;
874#endif
875
876 /* Set name. */
877 if (!name) {
878 name = "mtx%p";
879 }
880 if (strchr(name, '%')) {
881 pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
882 } else {
883 pj_ansi_strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
884 mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
885 }
886
887 PJ_LOG(6, (mutex->obj_name, "Mutex created"));
888 return PJ_SUCCESS;
889}
890
891/*
892 * pj_mutex_create()
893 */
894PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
895 const char *name,
896 int type,
897 pj_mutex_t **mutex_ptr)
898{
899 pj_status_t rc;
900 pj_mutex_t *mutex;
901
902 PJ_UNUSED_ARG(type);
903 PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL);
904
905 mutex = pj_pool_alloc(pool, sizeof(*mutex));
906 if (!mutex)
907 return PJ_ENOMEM;
908
909 rc = init_mutex(mutex, name);
910 if (rc != PJ_SUCCESS)
911 return rc;
912
913 *mutex_ptr = mutex;
914
915 return PJ_SUCCESS;
916}
917
918/*
919 * pj_mutex_create_simple()
920 */
921PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
922 const char *name,
923 pj_mutex_t **mutex )
924{
925 return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
926}
927
928/*
929 * pj_mutex_create_recursive()
930 */
931PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
932 const char *name,
933 pj_mutex_t **mutex )
934{
935 return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
936}
937
938/*
939 * pj_mutex_lock()
940 */
941PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
942{
943 pj_status_t status;
944
945 PJ_CHECK_STACK();
946 PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
947
948 LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is waiting",
949 pj_thread_this()->obj_name));
950
951#if PJ_WIN32_WINNT >= 0x0400
952 EnterCriticalSection(&mutex->crit);
953 status=PJ_SUCCESS;
954#else
955 if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0)
956 status = PJ_SUCCESS;
957 else
958 status = PJ_STATUS_FROM_OS(GetLastError());
959
960#endif
961 LOG_MUTEX((mutex->obj_name,
962 (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"),
963 pj_thread_this()->obj_name));
964
965#if PJ_DEBUG
966 if (status == PJ_SUCCESS) {
967 mutex->owner = pj_thread_this();
968 ++mutex->nesting_level;
969 }
970#endif
971
972 return status;
973}
974
975/*
976 * pj_mutex_unlock()
977 */
978PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
979{
980 pj_status_t status;
981
982 PJ_CHECK_STACK();
983 PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
984
985#if PJ_DEBUG
986 pj_assert(mutex->owner == pj_thread_this());
987 if (--mutex->nesting_level == 0) {
988 mutex->owner = NULL;
989 }
990#endif
991
992 LOG_MUTEX((mutex->obj_name, "Mutex released by thread %s",
993 pj_thread_this()->obj_name));
994
995#if PJ_WIN32_WINNT >= 0x0400
996 LeaveCriticalSection(&mutex->crit);
997 status=PJ_SUCCESS;
998#else
999 status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS :
1000 PJ_STATUS_FROM_OS(GetLastError());
1001#endif
1002 return status;
1003}
1004
1005/*
1006 * pj_mutex_trylock()
1007 */
1008PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
1009{
1010 pj_status_t status;
1011
1012 PJ_CHECK_STACK();
1013 PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
1014
1015 LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is trying",
1016 pj_thread_this()->obj_name));
1017
1018#if PJ_WIN32_WINNT >= 0x0400
1019 status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN;
1020#else
1021 status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ?
1022 PJ_SUCCESS : PJ_ETIMEDOUT;
1023#endif
1024 if (status==PJ_SUCCESS) {
1025 LOG_MUTEX((mutex->obj_name, "Mutex acquired by thread %s",
1026 pj_thread_this()->obj_name));
1027
1028#if PJ_DEBUG
1029 mutex->owner = pj_thread_this();
1030 ++mutex->nesting_level;
1031#endif
1032 } else {
1033 LOG_MUTEX((mutex->obj_name, "Mutex: thread %s's trylock() failed",
1034 pj_thread_this()->obj_name));
1035 }
1036
1037 return status;
1038}
1039
1040/*
1041 * pj_mutex_destroy()
1042 */
1043PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
1044{
1045 PJ_CHECK_STACK();
1046 PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
1047
1048 LOG_MUTEX((mutex->obj_name, "Mutex destroyed"));
1049
1050#if PJ_WIN32_WINNT >= 0x0400
1051 DeleteCriticalSection(&mutex->crit);
1052 return PJ_SUCCESS;
1053#else
1054 return CloseHandle(mutex->hMutex) ? PJ_SUCCESS :
1055 PJ_RETURN_OS_ERROR(GetLastError());
1056#endif
1057}
1058
1059/*
1060 * pj_mutex_is_locked()
1061 */
1062PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
1063{
1064#if PJ_DEBUG
1065 return mutex->owner == pj_thread_this();
1066#else
1067 PJ_UNUSED_ARG(mutex);
1068 pj_assert(!"PJ_DEBUG is not set!");
1069 return 1;
1070#endif
1071}
1072
1073///////////////////////////////////////////////////////////////////////////////
1074/*
1075 * Win32 lacks Read/Write mutex, so include the emulation.
1076 */
1077#include "os_rwmutex.c"
1078
1079///////////////////////////////////////////////////////////////////////////////
1080/*
1081 * pj_enter_critical_section()
1082 */
1083PJ_DEF(void) pj_enter_critical_section(void)
1084{
1085 pj_mutex_lock(&critical_section_mutex);
1086}
1087
1088
1089/*
1090 * pj_leave_critical_section()
1091 */
1092PJ_DEF(void) pj_leave_critical_section(void)
1093{
1094 pj_mutex_unlock(&critical_section_mutex);
1095}
1096
1097///////////////////////////////////////////////////////////////////////////////
1098#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
1099
1100/*
1101 * pj_sem_create()
1102 */
1103PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
1104 const char *name,
1105 unsigned initial,
1106 unsigned max,
1107 pj_sem_t **sem_ptr)
1108{
1109 pj_sem_t *sem;
1110
1111 PJ_CHECK_STACK();
1112 PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL);
1113
1114 sem = pj_pool_alloc(pool, sizeof(*sem));
1115 sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL);
1116 if (!sem->hSemaphore)
1117 return PJ_RETURN_OS_ERROR(GetLastError());
1118
1119 /* Set name. */
1120 if (!name) {
1121 name = "sem%p";
1122 }
1123 if (strchr(name, '%')) {
1124 pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
1125 } else {
1126 pj_ansi_strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
1127 sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
1128 }
1129
1130 LOG_MUTEX((sem->obj_name, "Semaphore created"));
1131
1132 *sem_ptr = sem;
1133 return PJ_SUCCESS;
1134}
1135
1136static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout)
1137{
1138 DWORD result;
1139
1140 PJ_CHECK_STACK();
1141 PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1142
1143 LOG_MUTEX((sem->obj_name, "Semaphore: thread %s is waiting",
1144 pj_thread_this()->obj_name));
1145
1146 result = WaitForSingleObject(sem->hSemaphore, timeout);
1147 if (result == WAIT_OBJECT_0) {
1148 LOG_MUTEX((sem->obj_name, "Semaphore acquired by thread %s",
1149 pj_thread_this()->obj_name));
1150 } else {
1151 LOG_MUTEX((sem->obj_name, "Semaphore: thread %s FAILED to acquire",
1152 pj_thread_this()->obj_name));
1153 }
1154
1155 if (result==WAIT_OBJECT_0)
1156 return PJ_SUCCESS;
1157 else if (result==WAIT_TIMEOUT)
1158 return PJ_ETIMEDOUT;
1159 else
1160 return PJ_RETURN_OS_ERROR(GetLastError());
1161}
1162
1163/*
1164 * pj_sem_wait()
1165 */
1166PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
1167{
1168 PJ_CHECK_STACK();
1169 PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1170
1171 return pj_sem_wait_for(sem, INFINITE);
1172}
1173
1174/*
1175 * pj_sem_trywait()
1176 */
1177PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
1178{
1179 PJ_CHECK_STACK();
1180 PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1181
1182 return pj_sem_wait_for(sem, 0);
1183}
1184
1185/*
1186 * pj_sem_post()
1187 */
1188PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
1189{
1190 PJ_CHECK_STACK();
1191 PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1192
1193 LOG_MUTEX((sem->obj_name, "Semaphore released by thread %s",
1194 pj_thread_this()->obj_name));
1195
1196 if (ReleaseSemaphore(sem->hSemaphore, 1, NULL))
1197 return PJ_SUCCESS;
1198 else
1199 return PJ_RETURN_OS_ERROR(GetLastError());
1200}
1201
1202/*
1203 * pj_sem_destroy()
1204 */
1205PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
1206{
1207 PJ_CHECK_STACK();
1208 PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1209
1210 LOG_MUTEX((sem->obj_name, "Semaphore destroyed by thread %s",
1211 pj_thread_this()->obj_name));
1212
1213 if (CloseHandle(sem->hSemaphore))
1214 return PJ_SUCCESS;
1215 else
1216 return PJ_RETURN_OS_ERROR(GetLastError());
1217}
1218
1219#endif /* PJ_HAS_SEMAPHORE */
1220///////////////////////////////////////////////////////////////////////////////
1221
1222
1223#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
1224
1225/*
1226 * pj_event_create()
1227 */
1228PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool,
1229 const char *name,
1230 pj_bool_t manual_reset,
1231 pj_bool_t initial,
1232 pj_event_t **event_ptr)
1233{
1234 pj_event_t *event;
1235
1236 PJ_CHECK_STACK();
1237 PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL);
1238
1239 event = pj_pool_alloc(pool, sizeof(*event));
1240 if (!event)
1241 return PJ_ENOMEM;
1242
1243 event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE,
1244 initial?TRUE:FALSE, NULL);
1245
1246 if (!event->hEvent)
1247 return PJ_RETURN_OS_ERROR(GetLastError());
1248
1249 /* Set name. */
1250 if (!name) {
1251 name = "evt%p";
1252 }
1253 if (strchr(name, '%')) {
1254 pj_ansi_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event);
1255 } else {
1256 pj_ansi_strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME);
1257 event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
1258 }
1259
1260 PJ_LOG(6, (event->obj_name, "Event created"));
1261
1262 *event_ptr = event;
1263 return PJ_SUCCESS;
1264}
1265
1266static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout)
1267{
1268 DWORD result;
1269
1270 PJ_CHECK_STACK();
1271 PJ_ASSERT_RETURN(event, PJ_EINVAL);
1272
1273 PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting",
1274 pj_thread_this()->obj_name));
1275
1276 result = WaitForSingleObject(event->hEvent, timeout);
1277 if (result == WAIT_OBJECT_0) {
1278 PJ_LOG(6, (event->obj_name, "Event: thread %s is released",
1279 pj_thread_this()->obj_name));
1280 } else {
1281 PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire",
1282 pj_thread_this()->obj_name));
1283 }
1284
1285 if (result==WAIT_OBJECT_0)
1286 return PJ_SUCCESS;
1287 else if (result==WAIT_TIMEOUT)
1288 return PJ_ETIMEDOUT;
1289 else
1290 return PJ_RETURN_OS_ERROR(GetLastError());
1291}
1292
1293/*
1294 * pj_event_wait()
1295 */
1296PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
1297{
1298 PJ_ASSERT_RETURN(event, PJ_EINVAL);
1299
1300 return pj_event_wait_for(event, INFINITE);
1301}
1302
1303/*
1304 * pj_event_trywait()
1305 */
1306PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
1307{
1308 PJ_ASSERT_RETURN(event, PJ_EINVAL);
1309
1310 return pj_event_wait_for(event, 0);
1311}
1312
1313/*
1314 * pj_event_set()
1315 */
1316PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
1317{
1318 PJ_CHECK_STACK();
1319 PJ_ASSERT_RETURN(event, PJ_EINVAL);
1320
1321 PJ_LOG(6, (event->obj_name, "Setting event"));
1322
1323 if (SetEvent(event->hEvent))
1324 return PJ_SUCCESS;
1325 else
1326 return PJ_RETURN_OS_ERROR(GetLastError());
1327}
1328
1329/*
1330 * pj_event_pulse()
1331 */
1332PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
1333{
1334 PJ_CHECK_STACK();
1335 PJ_ASSERT_RETURN(event, PJ_EINVAL);
1336
1337 PJ_LOG(6, (event->obj_name, "Pulsing event"));
1338
1339 if (PulseEvent(event->hEvent))
1340 return PJ_SUCCESS;
1341 else
1342 return PJ_RETURN_OS_ERROR(GetLastError());
1343}
1344
1345/*
1346 * pj_event_reset()
1347 */
1348PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
1349{
1350 PJ_CHECK_STACK();
1351 PJ_ASSERT_RETURN(event, PJ_EINVAL);
1352
1353 PJ_LOG(6, (event->obj_name, "Event is reset"));
1354
1355 if (ResetEvent(event->hEvent))
1356 return PJ_SUCCESS;
1357 else
1358 return PJ_RETURN_OS_ERROR(GetLastError());
1359}
1360
1361/*
1362 * pj_event_destroy()
1363 */
1364PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
1365{
1366 PJ_CHECK_STACK();
1367 PJ_ASSERT_RETURN(event, PJ_EINVAL);
1368
1369 PJ_LOG(6, (event->obj_name, "Event is destroying"));
1370
1371 if (CloseHandle(event->hEvent))
1372 return PJ_SUCCESS;
1373 else
1374 return PJ_RETURN_OS_ERROR(GetLastError());
1375}
1376
1377#endif /* PJ_HAS_EVENT_OBJ */
1378
1379///////////////////////////////////////////////////////////////////////////////
1380#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
1381/*
1382 * Terminal color
1383 */
1384
1385static WORD pj_color_to_os_attr(pj_color_t color)
1386{
1387 WORD attr = 0;
1388
1389 if (color & PJ_TERM_COLOR_R)
1390 attr |= FOREGROUND_RED;
1391 if (color & PJ_TERM_COLOR_G)
1392 attr |= FOREGROUND_GREEN;
1393 if (color & PJ_TERM_COLOR_B)
1394 attr |= FOREGROUND_BLUE;
1395 if (color & PJ_TERM_COLOR_BRIGHT)
1396 attr |= FOREGROUND_INTENSITY;
1397
1398 return attr;
1399}
1400
1401static pj_color_t os_attr_to_pj_color(WORD attr)
1402{
1403 int color = 0;
1404
1405 if (attr & FOREGROUND_RED)
1406 color |= PJ_TERM_COLOR_R;
1407 if (attr & FOREGROUND_GREEN)
1408 color |= PJ_TERM_COLOR_G;
1409 if (attr & FOREGROUND_BLUE)
1410 color |= PJ_TERM_COLOR_B;
1411 if (attr & FOREGROUND_INTENSITY)
1412 color |= PJ_TERM_COLOR_BRIGHT;
1413
1414 return color;
1415}
1416
1417
1418/*
1419 * pj_term_set_color()
1420 */
1421PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
1422{
1423 BOOL rc;
1424 WORD attr = 0;
1425
1426 PJ_CHECK_STACK();
1427
1428 attr = pj_color_to_os_attr(color);
1429 rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr);
1430 return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
1431}
1432
1433/*
1434 * pj_term_get_color()
1435 * Get current terminal foreground color.
1436 */
1437PJ_DEF(pj_color_t) pj_term_get_color(void)
1438{
1439 CONSOLE_SCREEN_BUFFER_INFO info;
1440
1441 PJ_CHECK_STACK();
1442
1443 GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info);
1444 return os_attr_to_pj_color(info.wAttributes);
1445}
1446
1447#endif /* PJ_TERM_HAS_COLOR */
1448
1449/*
1450 * pj_run_app()
1451 */
1452PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
1453 unsigned flags)
1454{
1455 PJ_UNUSED_ARG(flags);
1456 return (*main_func)(argc, argv);
1457}