Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjlib / src / pj / os_core_win32.c
1 /* $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  */
57 struct 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  */
78 struct 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  */
95 typedef 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  */
105 struct 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  */
114 struct pj_atomic_t
115 {
116     long value;
117 };
118
119 /*
120  * Flag and reference counter for PJLIB instance.
121  */
122 static int initialized;
123
124 /*
125  * Static global variables.
126  */
127 static pj_thread_desc main_thread;
128 static long thread_tls_id = -1;
129 static pj_mutex_t critical_section_mutex;
130 static unsigned atexit_count;
131 static void (*atexit_func[32])(void);
132
133 /*
134  * Some static prototypes.
135  */
136 static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name);
137
138
139 /*
140  * pj_init(void).
141  * Init PJLIB!
142  */
143 PJ_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  */
214 PJ_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  */
227 PJ_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  */
280 PJ_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  */
289 PJ_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  */
298 PJ_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  */
307 PJ_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  */
332 PJ_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  */
342 PJ_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  */
352 PJ_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  */
367 PJ_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*)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  */
424 pj_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
436 static 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  */
465 PJ_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 ? 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  */
523 PJ_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  */
536 PJ_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  */
552 PJ_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  */
574 PJ_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  */
600 PJ_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  */
616 PJ_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  */
628 PJ_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) ? &stk_ptr - thread->stk_start :
638                 thread->stk_start - &stk_ptr;
639
640     /* Assert if stack usage is dangerously high. */
641     pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
642
643     /* Keep statistic. */
644     if (usage > thread->stk_max_usage) {
645         thread->stk_max_usage = usage;
646         thread->caller_file = file;
647         thread->caller_line = line;
648     }
649
650 }
651
652 /*
653  * pj_thread_get_stack_max_usage()
654  */
655 PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
656 {
657     return thread->stk_max_usage;
658 }
659
660 /*
661  * pj_thread_get_stack_info()
662  */
663 PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
664                                               const char **file,
665                                               int *line )
666 {
667     pj_assert(thread);
668
669     *file = thread->caller_file;
670     *line = thread->caller_line;
671     return 0;
672 }
673
674 #endif  /* PJ_OS_HAS_CHECK_STACK */
675
676
677 ///////////////////////////////////////////////////////////////////////////////
678
679 /*
680  * pj_atomic_create()
681  */
682 PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, 
683                                       pj_atomic_value_t initial,
684                                       pj_atomic_t **atomic_ptr)
685 {
686     pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t));
687     if (!atomic_var)
688         return PJ_ENOMEM;
689
690     atomic_var->value = initial;
691     *atomic_ptr = atomic_var;
692
693     return PJ_SUCCESS;
694 }
695
696 /*
697  * pj_atomic_destroy()
698  */
699 PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
700 {
701     PJ_UNUSED_ARG(var);
702     PJ_ASSERT_RETURN(var, PJ_EINVAL);
703
704     return 0;
705 }
706
707 /*
708  * pj_atomic_set()
709  */
710 PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value)
711 {
712     PJ_CHECK_STACK();
713
714     InterlockedExchange(&atomic_var->value, value);
715 }
716
717 /*
718  * pj_atomic_get()
719  */
720 PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
721 {
722     PJ_CHECK_STACK();
723     PJ_ASSERT_RETURN(atomic_var, 0);
724
725     return atomic_var->value;
726 }
727
728 /*
729  * pj_atomic_inc_and_get()
730  */
731 PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
732 {
733     PJ_CHECK_STACK();
734
735 #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
736     return InterlockedIncrement(&atomic_var->value);
737 #else
738     return InterlockedIncrement(&atomic_var->value);
739 #endif
740 }
741
742 /*
743  * pj_atomic_inc()
744  */
745 PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
746 {
747     pj_atomic_inc_and_get(atomic_var);
748 }
749
750 /*
751  * pj_atomic_dec_and_get()
752  */
753 PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
754 {
755     PJ_CHECK_STACK();
756
757 #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
758     return InterlockedDecrement(&atomic_var->value);
759 #else
760     return InterlockedDecrement(&atomic_var->value);
761 #endif
762 }
763
764 /*
765  * pj_atomic_dec()
766  */
767 PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
768 {
769     pj_atomic_dec_and_get(atomic_var);
770 }
771
772 /*
773  * pj_atomic_add()
774  */
775 PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
776                             pj_atomic_value_t value )
777 {
778 #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
779     InterlockedExchangeAdd( &atomic_var->value, value );
780 #else
781     InterlockedExchangeAdd( &atomic_var->value, value );
782 #endif
783 }
784
785 /*
786  * pj_atomic_add_and_get()
787  */
788 PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
789                                                  pj_atomic_value_t value)
790 {
791 #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
792     long oldValue = InterlockedExchangeAdd( &atomic_var->value, value);
793     return oldValue + value;
794 #else
795     long oldValue = InterlockedExchangeAdd( &atomic_var->value, value);
796     return oldValue + value;
797 #endif
798 }
799
800 ///////////////////////////////////////////////////////////////////////////////
801 /*
802  * pj_thread_local_alloc()
803  */
804 PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
805 {
806     PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL);
807
808     //Can't check stack because this function is called in the
809     //beginning before main thread is initialized.
810     //PJ_CHECK_STACK();
811
812     *index = TlsAlloc();
813
814     if (*index == TLS_OUT_OF_INDEXES)
815         return PJ_RETURN_OS_ERROR(GetLastError());
816     else
817         return PJ_SUCCESS;
818 }
819
820 /*
821  * pj_thread_local_free()
822  */
823 PJ_DEF(void) pj_thread_local_free(long index)
824 {
825     PJ_CHECK_STACK();
826     TlsFree(index);
827 }
828
829 /*
830  * pj_thread_local_set()
831  */
832 PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
833 {
834     BOOL rc;
835
836     //Can't check stack because this function is called in the
837     //beginning before main thread is initialized.
838     //PJ_CHECK_STACK();
839     rc = TlsSetValue(index, value);
840     return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
841 }
842
843 /*
844  * pj_thread_local_get()
845  */
846 PJ_DEF(void*) pj_thread_local_get(long index)
847 {
848     //Can't check stack because this function is called
849     //by PJ_CHECK_STACK() itself!!!
850     //PJ_CHECK_STACK();
851     return TlsGetValue(index);
852 }
853
854 ///////////////////////////////////////////////////////////////////////////////
855 static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name)
856 {
857
858     PJ_CHECK_STACK();
859
860 #if PJ_WIN32_WINNT >= 0x0400
861     InitializeCriticalSection(&mutex->crit);
862 #else
863     mutex->hMutex = CreateMutex(NULL, FALSE, NULL);
864     if (!mutex->hMutex) {
865         return PJ_RETURN_OS_ERROR(GetLastError());
866     }
867 #endif
868
869 #if PJ_DEBUG
870     /* Set owner. */
871     mutex->nesting_level = 0;
872     mutex->owner = NULL;
873 #endif
874
875     /* Set name. */
876     if (!name) {
877         name = "mtx%p";
878     }
879     if (strchr(name, '%')) {
880         pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
881     } else {
882         pj_ansi_strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
883         mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
884     }
885
886     PJ_LOG(6, (mutex->obj_name, "Mutex created"));
887     return PJ_SUCCESS;
888 }
889
890 /*
891  * pj_mutex_create()
892  */
893 PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, 
894                                     const char *name, 
895                                     int type,
896                                     pj_mutex_t **mutex_ptr)
897 {
898     pj_status_t rc;
899     pj_mutex_t *mutex;
900
901     PJ_UNUSED_ARG(type);
902     PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL);
903
904     mutex = pj_pool_alloc(pool, sizeof(*mutex));
905     if (!mutex)
906         return PJ_ENOMEM;
907
908     rc = init_mutex(mutex, name);
909     if (rc != PJ_SUCCESS)
910         return rc;
911
912     *mutex_ptr = mutex;
913
914     return PJ_SUCCESS;
915 }
916
917 /*
918  * pj_mutex_create_simple()
919  */
920 PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, 
921                                             const char *name,
922                                             pj_mutex_t **mutex )
923 {
924     return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
925 }
926
927 /*
928  * pj_mutex_create_recursive()
929  */
930 PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
931                                                const char *name,
932                                                pj_mutex_t **mutex )
933 {
934     return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
935 }
936
937 /*
938  * pj_mutex_lock()
939  */
940 PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
941 {
942     pj_status_t status;
943
944     PJ_CHECK_STACK();
945     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
946
947     LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is waiting", 
948                                 pj_thread_this()->obj_name));
949
950 #if PJ_WIN32_WINNT >= 0x0400
951     EnterCriticalSection(&mutex->crit);
952     status=PJ_SUCCESS;
953 #else
954     if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0)
955         status = PJ_SUCCESS;
956     else
957         status = PJ_STATUS_FROM_OS(GetLastError());
958
959 #endif
960     LOG_MUTEX((mutex->obj_name, 
961               (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"),
962               pj_thread_this()->obj_name));
963
964 #if PJ_DEBUG
965     if (status == PJ_SUCCESS) {
966         mutex->owner = pj_thread_this();
967         ++mutex->nesting_level;
968     }
969 #endif
970
971     return status;
972 }
973
974 /*
975  * pj_mutex_unlock()
976  */
977 PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
978 {
979     pj_status_t status;
980
981     PJ_CHECK_STACK();
982     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
983
984 #if PJ_DEBUG
985     pj_assert(mutex->owner == pj_thread_this());
986     if (--mutex->nesting_level == 0) {
987         mutex->owner = NULL;
988     }
989 #endif
990
991     LOG_MUTEX((mutex->obj_name, "Mutex released by thread %s", 
992                                 pj_thread_this()->obj_name));
993
994 #if PJ_WIN32_WINNT >= 0x0400
995     LeaveCriticalSection(&mutex->crit);
996     status=PJ_SUCCESS;
997 #else
998     status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS : 
999                 PJ_STATUS_FROM_OS(GetLastError());
1000 #endif
1001     return status;
1002 }
1003
1004 /*
1005  * pj_mutex_trylock()
1006  */
1007 PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
1008 {
1009     pj_status_t status;
1010
1011     PJ_CHECK_STACK();
1012     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
1013
1014     LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is trying", 
1015                                 pj_thread_this()->obj_name));
1016
1017 #if PJ_WIN32_WINNT >= 0x0400
1018     status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN;
1019 #else
1020     status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ? 
1021                 PJ_SUCCESS : PJ_ETIMEDOUT;
1022 #endif
1023     if (status==PJ_SUCCESS) {
1024         LOG_MUTEX((mutex->obj_name, "Mutex acquired by thread %s", 
1025                                   pj_thread_this()->obj_name));
1026
1027 #if PJ_DEBUG
1028         mutex->owner = pj_thread_this();
1029         ++mutex->nesting_level;
1030 #endif
1031     } else {
1032         LOG_MUTEX((mutex->obj_name, "Mutex: thread %s's trylock() failed", 
1033                                     pj_thread_this()->obj_name));
1034     }
1035
1036     return status;
1037 }
1038
1039 /*
1040  * pj_mutex_destroy()
1041  */
1042 PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
1043 {
1044     PJ_CHECK_STACK();
1045     PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
1046
1047     LOG_MUTEX((mutex->obj_name, "Mutex destroyed"));
1048
1049 #if PJ_WIN32_WINNT >= 0x0400
1050     DeleteCriticalSection(&mutex->crit);
1051     return PJ_SUCCESS;
1052 #else
1053     return CloseHandle(mutex->hMutex) ? PJ_SUCCESS : 
1054             PJ_RETURN_OS_ERROR(GetLastError());
1055 #endif
1056 }
1057
1058 /*
1059  * pj_mutex_is_locked()
1060  */
1061 PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
1062 {
1063 #if PJ_DEBUG
1064     return mutex->owner == pj_thread_this();
1065 #else
1066     PJ_UNUSED_ARG(mutex);
1067     pj_assert(!"PJ_DEBUG is not set!");
1068     return 1;
1069 #endif
1070 }
1071
1072 ///////////////////////////////////////////////////////////////////////////////
1073 /*
1074  * Win32 lacks Read/Write mutex, so include the emulation.
1075  */
1076 #include "os_rwmutex.c"
1077
1078 ///////////////////////////////////////////////////////////////////////////////
1079 /*
1080  * pj_enter_critical_section()
1081  */
1082 PJ_DEF(void) pj_enter_critical_section(void)
1083 {
1084     pj_mutex_lock(&critical_section_mutex);
1085 }
1086
1087
1088 /*
1089  * pj_leave_critical_section()
1090  */
1091 PJ_DEF(void) pj_leave_critical_section(void)
1092 {
1093     pj_mutex_unlock(&critical_section_mutex);
1094 }
1095
1096 ///////////////////////////////////////////////////////////////////////////////
1097 #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
1098
1099 /*
1100  * pj_sem_create()
1101  */
1102 PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, 
1103                                    const char *name,
1104                                    unsigned initial, 
1105                                    unsigned max,
1106                                    pj_sem_t **sem_ptr)
1107 {
1108     pj_sem_t *sem;
1109
1110     PJ_CHECK_STACK();
1111     PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL);
1112
1113     sem = pj_pool_alloc(pool, sizeof(*sem));    
1114     sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL);
1115     if (!sem->hSemaphore)
1116         return PJ_RETURN_OS_ERROR(GetLastError());
1117
1118     /* Set name. */
1119     if (!name) {
1120         name = "sem%p";
1121     }
1122     if (strchr(name, '%')) {
1123         pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
1124     } else {
1125         pj_ansi_strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
1126         sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
1127     }
1128
1129     LOG_MUTEX((sem->obj_name, "Semaphore created"));
1130
1131     *sem_ptr = sem;
1132     return PJ_SUCCESS;
1133 }
1134
1135 static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout)
1136 {
1137     DWORD result;
1138
1139     PJ_CHECK_STACK();
1140     PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1141
1142     LOG_MUTEX((sem->obj_name, "Semaphore: thread %s is waiting", 
1143                               pj_thread_this()->obj_name));
1144
1145     result = WaitForSingleObject(sem->hSemaphore, timeout);
1146     if (result == WAIT_OBJECT_0) {
1147         LOG_MUTEX((sem->obj_name, "Semaphore acquired by thread %s", 
1148                                   pj_thread_this()->obj_name));
1149     } else {
1150         LOG_MUTEX((sem->obj_name, "Semaphore: thread %s FAILED to acquire", 
1151                                   pj_thread_this()->obj_name));
1152     }
1153
1154     if (result==WAIT_OBJECT_0)
1155         return PJ_SUCCESS;
1156     else if (result==WAIT_TIMEOUT)
1157         return PJ_ETIMEDOUT;
1158     else
1159         return PJ_RETURN_OS_ERROR(GetLastError());
1160 }
1161
1162 /*
1163  * pj_sem_wait()
1164  */
1165 PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
1166 {
1167     PJ_CHECK_STACK();
1168     PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1169
1170     return pj_sem_wait_for(sem, INFINITE);
1171 }
1172
1173 /*
1174  * pj_sem_trywait()
1175  */
1176 PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
1177 {
1178     PJ_CHECK_STACK();
1179     PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1180
1181     return pj_sem_wait_for(sem, 0);
1182 }
1183
1184 /*
1185  * pj_sem_post()
1186  */
1187 PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
1188 {
1189     PJ_CHECK_STACK();
1190     PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1191
1192     LOG_MUTEX((sem->obj_name, "Semaphore released by thread %s",
1193                               pj_thread_this()->obj_name));
1194
1195     if (ReleaseSemaphore(sem->hSemaphore, 1, NULL))
1196         return PJ_SUCCESS;
1197     else
1198         return PJ_RETURN_OS_ERROR(GetLastError());
1199 }
1200
1201 /*
1202  * pj_sem_destroy()
1203  */
1204 PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
1205 {
1206     PJ_CHECK_STACK();
1207     PJ_ASSERT_RETURN(sem, PJ_EINVAL);
1208
1209     LOG_MUTEX((sem->obj_name, "Semaphore destroyed by thread %s",
1210                               pj_thread_this()->obj_name));
1211
1212     if (CloseHandle(sem->hSemaphore))
1213         return PJ_SUCCESS;
1214     else
1215         return PJ_RETURN_OS_ERROR(GetLastError());
1216 }
1217
1218 #endif  /* PJ_HAS_SEMAPHORE */
1219 ///////////////////////////////////////////////////////////////////////////////
1220
1221
1222 #if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
1223
1224 /*
1225  * pj_event_create()
1226  */
1227 PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, 
1228                                      const char *name,
1229                                      pj_bool_t manual_reset, 
1230                                      pj_bool_t initial,
1231                                      pj_event_t **event_ptr)
1232 {
1233     pj_event_t *event;
1234
1235     PJ_CHECK_STACK();
1236     PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL);
1237
1238     event = pj_pool_alloc(pool, sizeof(*event));
1239     if (!event)
1240         return PJ_ENOMEM;
1241
1242     event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, 
1243                                 initial?TRUE:FALSE, NULL);
1244
1245     if (!event->hEvent)
1246         return PJ_RETURN_OS_ERROR(GetLastError());
1247
1248     /* Set name. */
1249     if (!name) {
1250         name = "evt%p";
1251     }
1252     if (strchr(name, '%')) {
1253         pj_ansi_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event);
1254     } else {
1255         pj_ansi_strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME);
1256         event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
1257     }
1258
1259     PJ_LOG(6, (event->obj_name, "Event created"));
1260
1261     *event_ptr = event;
1262     return PJ_SUCCESS;
1263 }
1264
1265 static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout)
1266 {
1267     DWORD result;
1268
1269     PJ_CHECK_STACK();
1270     PJ_ASSERT_RETURN(event, PJ_EINVAL);
1271
1272     PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", 
1273                                 pj_thread_this()->obj_name));
1274
1275     result = WaitForSingleObject(event->hEvent, timeout);
1276     if (result == WAIT_OBJECT_0) {
1277         PJ_LOG(6, (event->obj_name, "Event: thread %s is released", 
1278                                     pj_thread_this()->obj_name));
1279     } else {
1280         PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire", 
1281                                     pj_thread_this()->obj_name));
1282     }
1283
1284     if (result==WAIT_OBJECT_0)
1285         return PJ_SUCCESS;
1286     else if (result==WAIT_TIMEOUT)
1287         return PJ_ETIMEDOUT;
1288     else
1289         return PJ_RETURN_OS_ERROR(GetLastError());
1290 }
1291
1292 /*
1293  * pj_event_wait()
1294  */
1295 PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
1296 {
1297     PJ_ASSERT_RETURN(event, PJ_EINVAL);
1298
1299     return pj_event_wait_for(event, INFINITE);
1300 }
1301
1302 /*
1303  * pj_event_trywait()
1304  */
1305 PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
1306 {
1307     PJ_ASSERT_RETURN(event, PJ_EINVAL);
1308
1309     return pj_event_wait_for(event, 0);
1310 }
1311
1312 /*
1313  * pj_event_set()
1314  */
1315 PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
1316 {
1317     PJ_CHECK_STACK();
1318     PJ_ASSERT_RETURN(event, PJ_EINVAL);
1319
1320     PJ_LOG(6, (event->obj_name, "Setting event"));
1321
1322     if (SetEvent(event->hEvent))
1323         return PJ_SUCCESS;
1324     else
1325         return PJ_RETURN_OS_ERROR(GetLastError());
1326 }
1327
1328 /*
1329  * pj_event_pulse()
1330  */
1331 PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
1332 {
1333     PJ_CHECK_STACK();
1334     PJ_ASSERT_RETURN(event, PJ_EINVAL);
1335
1336     PJ_LOG(6, (event->obj_name, "Pulsing event"));
1337
1338     if (PulseEvent(event->hEvent))
1339         return PJ_SUCCESS;
1340     else
1341         return PJ_RETURN_OS_ERROR(GetLastError());
1342 }
1343
1344 /*
1345  * pj_event_reset()
1346  */
1347 PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
1348 {
1349     PJ_CHECK_STACK();
1350     PJ_ASSERT_RETURN(event, PJ_EINVAL);
1351
1352     PJ_LOG(6, (event->obj_name, "Event is reset"));
1353
1354     if (ResetEvent(event->hEvent))
1355         return PJ_SUCCESS;
1356     else
1357         return PJ_RETURN_OS_ERROR(GetLastError());
1358 }
1359
1360 /*
1361  * pj_event_destroy()
1362  */
1363 PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
1364 {
1365     PJ_CHECK_STACK();
1366     PJ_ASSERT_RETURN(event, PJ_EINVAL);
1367
1368     PJ_LOG(6, (event->obj_name, "Event is destroying"));
1369
1370     if (CloseHandle(event->hEvent))
1371         return PJ_SUCCESS;
1372     else
1373         return PJ_RETURN_OS_ERROR(GetLastError());
1374 }
1375
1376 #endif  /* PJ_HAS_EVENT_OBJ */
1377
1378 ///////////////////////////////////////////////////////////////////////////////
1379 #if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
1380 /*
1381  * Terminal color
1382  */
1383
1384 static WORD pj_color_to_os_attr(pj_color_t color)
1385 {
1386     WORD attr = 0;
1387
1388     if (color & PJ_TERM_COLOR_R)
1389         attr |= FOREGROUND_RED;
1390     if (color & PJ_TERM_COLOR_G)
1391         attr |= FOREGROUND_GREEN;
1392     if (color & PJ_TERM_COLOR_B)
1393         attr |= FOREGROUND_BLUE;
1394     if (color & PJ_TERM_COLOR_BRIGHT)
1395         attr |= FOREGROUND_INTENSITY;
1396
1397     return attr;
1398 }
1399
1400 static pj_color_t os_attr_to_pj_color(WORD attr)
1401 {
1402     int color = 0;
1403
1404     if (attr & FOREGROUND_RED)
1405         color |= PJ_TERM_COLOR_R;
1406     if (attr & FOREGROUND_GREEN)
1407         color |= PJ_TERM_COLOR_G;
1408     if (attr & FOREGROUND_BLUE)
1409         color |= PJ_TERM_COLOR_B;
1410     if (attr & FOREGROUND_INTENSITY)
1411         color |= PJ_TERM_COLOR_BRIGHT;
1412
1413     return color;
1414 }
1415
1416
1417 /*
1418  * pj_term_set_color()
1419  */
1420 PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
1421 {
1422     BOOL rc;
1423     WORD attr = 0;
1424
1425     PJ_CHECK_STACK();
1426
1427     attr = pj_color_to_os_attr(color);
1428     rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr);
1429     return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
1430 }
1431
1432 /*
1433  * pj_term_get_color()
1434  * Get current terminal foreground color.
1435  */
1436 PJ_DEF(pj_color_t) pj_term_get_color(void)
1437 {
1438     CONSOLE_SCREEN_BUFFER_INFO info;
1439
1440     PJ_CHECK_STACK();
1441
1442     GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info);
1443     return os_attr_to_pj_color(info.wAttributes);
1444 }
1445
1446 #endif  /* PJ_TERM_HAS_COLOR */
1447
1448 /*
1449  * pj_run_app()
1450  */
1451 PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
1452                        unsigned flags)
1453 {
1454     PJ_UNUSED_ARG(flags);
1455     return (*main_func)(argc, argv);
1456 }