blob: 252f7fcc5ec3c69920cbcadc34f251fcb7c1f5db [file] [log] [blame]
/* $Id$ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Contributors:
* - Thanks for Zetron, Inc. (Phil Torre, ptorre@zetron.com) for donating
* the RTEMS port.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <pj/os.h>
#include <pj/assert.h>
#include <pj/pool.h>
#include <pj/log.h>
#include <pj/rand.h>
#include <pj/string.h>
#include <pj/guid.h>
#include <pj/except.h>
#include <pj/errno.h>
#if defined(PJ_HAS_SEMAPHORE_H) && PJ_HAS_SEMAPHORE_H != 0
# include <semaphore.h>
#endif
#include <unistd.h> // getpid()
#include <errno.h> // errno
#include <pthread.h>
#define THIS_FILE "os_core_unix.c"
#define SIGNATURE1 0xDEAFBEEF
#define SIGNATURE2 0xDEADC0DE
struct pj_thread_t
{
char obj_name[PJ_MAX_OBJ_NAME];
pthread_t thread;
pj_thread_proc *proc;
void *arg;
pj_uint32_t signature1;
pj_uint32_t signature2;
pj_mutex_t *suspended_mutex;
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
pj_uint32_t stk_size;
pj_uint32_t stk_max_usage;
char *stk_start;
const char *caller_file;
int caller_line;
#endif
};
struct pj_atomic_t
{
pj_mutex_t *mutex;
pj_atomic_value_t value;
};
struct pj_mutex_t
{
pthread_mutex_t mutex;
char obj_name[PJ_MAX_OBJ_NAME];
#if PJ_DEBUG
int nesting_level;
pj_thread_t *owner;
char owner_name[PJ_MAX_OBJ_NAME];
#endif
};
#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
struct pj_sem_t
{
sem_t *sem;
char obj_name[PJ_MAX_OBJ_NAME];
};
#endif /* PJ_HAS_SEMAPHORE */
#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
struct pj_event_t
{
enum event_state {
EV_STATE_OFF,
EV_STATE_SET,
EV_STATE_PULSED
} state;
pj_mutex_t mutex;
pthread_cond_t cond;
pj_bool_t auto_reset;
unsigned threads_waiting;
unsigned threads_to_release;
};
#endif /* PJ_HAS_EVENT_OBJ */
/*
* Flag and reference counter for PJLIB instance.
*/
static int initialized;
#if PJ_HAS_THREADS
static pj_thread_t main_thread;
static long thread_tls_id;
static pj_mutex_t critical_section;
#else
# define MAX_THREADS 32
static int tls_flag[MAX_THREADS];
static void *tls[MAX_THREADS];
#endif
static unsigned atexit_count;
static void (*atexit_func[32])(void);
static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type);
/*
* pj_init(void).
* Init PJLIB!
*/
PJ_DEF(pj_status_t) pj_init(void)
{
char dummy_guid[PJ_GUID_MAX_LENGTH];
pj_str_t guid;
pj_status_t rc;
/* Check if PJLIB have been initialized */
if (initialized) {
++initialized;
return PJ_SUCCESS;
}
#if PJ_HAS_THREADS
/* Init this thread's TLS. */
if ((rc=pj_thread_init()) != 0) {
return rc;
}
/* Critical section. */
if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_RECURSE)) != 0)
return rc;
#endif
/* Init logging */
pj_log_init();
/* Initialize exception ID for the pool.
* Must do so after critical section is configured.
*/
rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
if (rc != PJ_SUCCESS)
return rc;
/* Init random seed. */
/* Or probably not. Let application in charge of this */
/* pj_srand( clock() ); */
/* Startup GUID. */
guid.ptr = dummy_guid;
pj_generate_unique_string( &guid );
/* Startup timestamp */
#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
{
pj_timestamp dummy_ts;
if ((rc=pj_get_timestamp(&dummy_ts)) != 0) {
return rc;
}
}
#endif
/* Flag PJLIB as initialized */
++initialized;
pj_assert(initialized == 1);
PJ_LOG(4,(THIS_FILE, "pjlib %s for POSIX initialized",
PJ_VERSION));
return PJ_SUCCESS;
}
/*
* pj_atexit()
*/
PJ_DEF(pj_status_t) pj_atexit(void (*func)(void))
{
if (atexit_count >= PJ_ARRAY_SIZE(atexit_func))
return PJ_ETOOMANY;
atexit_func[atexit_count++] = func;
return PJ_SUCCESS;
}
/*
* pj_shutdown(void)
*/
PJ_DEF(void) pj_shutdown()
{
int i;
/* Only perform shutdown operation when 'initialized' reaches zero */
pj_assert(initialized > 0);
if (--initialized != 0)
return;
/* Call atexit() functions */
for (i=atexit_count-1; i>=0; --i) {
(*atexit_func[i])();
}
atexit_count = 0;
/* Free exception ID */
if (PJ_NO_MEMORY_EXCEPTION != -1) {
pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION);
PJ_NO_MEMORY_EXCEPTION = -1;
}
#if PJ_HAS_THREADS
/* Destroy PJLIB critical section */
pj_mutex_destroy(&critical_section);
/* Free PJLIB TLS */
if (thread_tls_id != -1) {
pj_thread_local_free(thread_tls_id);
thread_tls_id = -1;
}
/* Ticket #1132: Assertion when (re)starting PJLIB on different thread */
pj_bzero(&main_thread, sizeof(main_thread));
#endif
/* Clear static variables */
pj_errno_clear_handlers();
}
/*
* pj_getpid(void)
*/
PJ_DEF(pj_uint32_t) pj_getpid(void)
{
PJ_CHECK_STACK();
return getpid();
}
/*
* Check if this thread has been registered to PJLIB.
*/
PJ_DEF(pj_bool_t) pj_thread_is_registered(void)
{
#if PJ_HAS_THREADS
return pj_thread_local_get(thread_tls_id) != 0;
#else
pj_assert("pj_thread_is_registered() called in non-threading mode!");
return PJ_TRUE;
#endif
}
/*
* Get thread priority value for the thread.
*/
PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread)
{
#if PJ_HAS_THREADS
struct sched_param param;
int policy;
int rc;
rc = pthread_getschedparam (thread->thread, &policy, &param);
if (rc != 0)
return -1;
return param.sched_priority;
#else
PJ_UNUSED_ARG(thread);
return 1;
#endif
}
/*
* Set the thread priority.
*/
PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio)
{
#if PJ_HAS_THREADS
struct sched_param param;
int policy;
int rc;
rc = pthread_getschedparam (thread->thread, &policy, &param);
if (rc != 0)
return PJ_RETURN_OS_ERROR(rc);
param.sched_priority = prio;
rc = pthread_setschedparam(thread->thread, policy, &param);
if (rc != 0)
return PJ_RETURN_OS_ERROR(rc);
return PJ_SUCCESS;
#else
PJ_UNUSED_ARG(thread);
PJ_UNUSED_ARG(prio);
pj_assert("pj_thread_set_prio() called in non-threading mode!");
return 1;
#endif
}
/*
* Get the lowest priority value available on this system.
*/
PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread)
{
struct sched_param param;
int policy;
int rc;
rc = pthread_getschedparam(thread->thread, &policy, &param);
if (rc != 0)
return -1;
#if defined(_POSIX_PRIORITY_SCHEDULING)
return sched_get_priority_min(policy);
#elif defined __OpenBSD__
/* Thread prio min/max are declared in OpenBSD private hdr */
return 0;
#else
pj_assert("pj_thread_get_prio_min() not supported!");
return 0;
#endif
}
/*
* Get the highest priority value available on this system.
*/
PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread)
{
struct sched_param param;
int policy;
int rc;
rc = pthread_getschedparam(thread->thread, &policy, &param);
if (rc != 0)
return -1;
#if defined(_POSIX_PRIORITY_SCHEDULING)
return sched_get_priority_max(policy);
#elif defined __OpenBSD__
/* Thread prio min/max are declared in OpenBSD private hdr */
return 31;
#else
pj_assert("pj_thread_get_prio_max() not supported!");
return 0;
#endif
}
/*
* Get native thread handle
*/
PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread)
{
PJ_ASSERT_RETURN(thread, NULL);
#if PJ_HAS_THREADS
return &thread->thread;
#else
pj_assert("pj_thread_is_registered() called in non-threading mode!");
return NULL;
#endif
}
/*
* pj_thread_register(..)
*/
PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
pj_thread_desc desc,
pj_thread_t **ptr_thread)
{
#if PJ_HAS_THREADS
char stack_ptr;
pj_status_t rc;
pj_thread_t *thread = (pj_thread_t *)desc;
pj_str_t thread_name = pj_str((char*)cstr_thread_name);
/* Size sanity check. */
if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
pj_assert(!"Not enough pj_thread_desc size!");
return PJ_EBUG;
}
/* Warn if this thread has been registered before */
if (pj_thread_local_get (thread_tls_id) != 0) {
// 2006-02-26 bennylp:
// This wouldn't work in all cases!.
// If thread is created by external module (e.g. sound thread),
// thread may be reused while the pool used for the thread descriptor
// has been deleted by application.
//*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
//return PJ_SUCCESS;
PJ_LOG(4,(THIS_FILE, "Info: possibly re-registering existing "
"thread"));
}
/* On the other hand, also warn if the thread descriptor buffer seem to
* have been used to register other threads.
*/
pj_assert(thread->signature1 != SIGNATURE1 ||
thread->signature2 != SIGNATURE2 ||
(thread->thread == pthread_self()));
/* Initialize and set the thread entry. */
pj_bzero(desc, sizeof(struct pj_thread_t));
thread->thread = pthread_self();
thread->signature1 = SIGNATURE1;
thread->signature2 = SIGNATURE2;
if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name),
cstr_thread_name, thread->thread);
else
pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name),
"thr%p", (void*)thread->thread);
rc = pj_thread_local_set(thread_tls_id, thread);
if (rc != PJ_SUCCESS) {
pj_bzero(desc, sizeof(struct pj_thread_t));
return rc;
}
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
thread->stk_start = &stack_ptr;
thread->stk_size = 0xFFFFFFFFUL;
thread->stk_max_usage = 0;
#else
stack_ptr = '\0';
#endif
*ptr_thread = thread;
return PJ_SUCCESS;
#else
pj_thread_t *thread = (pj_thread_t*)desc;
*ptr_thread = thread;
return PJ_SUCCESS;
#endif
}
/*
* pj_thread_init(void)
*/
pj_status_t pj_thread_init(void)
{
#if PJ_HAS_THREADS
pj_status_t rc;
pj_thread_t *dummy;
rc = pj_thread_local_alloc(&thread_tls_id );
if (rc != PJ_SUCCESS) {
return rc;
}
return pj_thread_register("thr%p", (long*)&main_thread, &dummy);
#else
PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!"));
return PJ_EINVALIDOP;
#endif
}
#if PJ_HAS_THREADS
/*
* thread_main()
*
* This is the main entry for all threads.
*/
static void *thread_main(void *param)
{
pj_thread_t *rec = (pj_thread_t*)param;
void *result;
pj_status_t rc;
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
rec->stk_start = (char*)&rec;
#endif
/* Set current thread id. */
rc = pj_thread_local_set(thread_tls_id, rec);
if (rc != PJ_SUCCESS) {
pj_assert(!"Thread TLS ID is not set (pj_init() error?)");
}
/* Check if suspension is required. */
if (rec->suspended_mutex) {
pj_mutex_lock(rec->suspended_mutex);
pj_mutex_unlock(rec->suspended_mutex);
}
PJ_LOG(6,(rec->obj_name, "Thread started"));
/* Call user's entry! */
result = (void*)(long)(*rec->proc)(rec->arg);
/* Done. */
PJ_LOG(6,(rec->obj_name, "Thread quitting"));
return result;
}
#endif
/*
* pj_thread_create(...)
*/
PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool,
const char *thread_name,
pj_thread_proc *proc,
void *arg,
pj_size_t stack_size,
unsigned flags,
pj_thread_t **ptr_thread)
{
#if PJ_HAS_THREADS
pj_thread_t *rec;
pthread_attr_t thread_attr;
void *stack_addr;
int rc;
PJ_UNUSED_ARG(stack_addr);
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
/* Create thread record and assign name for the thread */
rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t));
PJ_ASSERT_RETURN(rec, PJ_ENOMEM);
/* Set name. */
if (!thread_name)
thread_name = "thr%p";
if (strchr(thread_name, '%')) {
pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
} else {
strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
}
/* Set default stack size */
if (stack_size == 0)
stack_size = PJ_THREAD_DEFAULT_STACK_SIZE;
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
rec->stk_size = stack_size;
rec->stk_max_usage = 0;
#endif
/* Emulate suspended thread with mutex. */
if (flags & PJ_THREAD_SUSPENDED) {
rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex);
if (rc != PJ_SUCCESS) {
return rc;
}
pj_mutex_lock(rec->suspended_mutex);
} else {
pj_assert(rec->suspended_mutex == NULL);
}
/* Init thread attributes */
pthread_attr_init(&thread_attr);
#if defined(PJ_THREAD_SET_STACK_SIZE) && PJ_THREAD_SET_STACK_SIZE!=0
/* Set thread's stack size */
rc = pthread_attr_setstacksize(&thread_attr, stack_size);
if (rc != 0)
return PJ_RETURN_OS_ERROR(rc);
#endif /* PJ_THREAD_SET_STACK_SIZE */
#if defined(PJ_THREAD_ALLOCATE_STACK) && PJ_THREAD_ALLOCATE_STACK!=0
/* Allocate memory for the stack */
stack_addr = pj_pool_alloc(pool, stack_size);
PJ_ASSERT_RETURN(stack_addr, PJ_ENOMEM);
rc = pthread_attr_setstackaddr(&thread_attr, stack_addr);
if (rc != 0)
return PJ_RETURN_OS_ERROR(rc);
#endif /* PJ_THREAD_ALLOCATE_STACK */
/* Create the thread. */
rec->proc = proc;
rec->arg = arg;
rc = pthread_create( &rec->thread, &thread_attr, &thread_main, rec);
if (rc != 0) {
return PJ_RETURN_OS_ERROR(rc);
}
*ptr_thread = rec;
PJ_LOG(6, (rec->obj_name, "Thread created"));
return PJ_SUCCESS;
#else
pj_assert(!"Threading is disabled!");
return PJ_EINVALIDOP;
#endif
}
/*
* pj_thread-get_name()
*/
PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
{
#if PJ_HAS_THREADS
pj_thread_t *rec = (pj_thread_t*)p;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(p, "");
return rec->obj_name;
#else
return "";
#endif
}
/*
* pj_thread_resume()
*/
PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
{
pj_status_t rc;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(p, PJ_EINVAL);
rc = pj_mutex_unlock(p->suspended_mutex);
return rc;
}
/*
* pj_thread_this()
*/
PJ_DEF(pj_thread_t*) pj_thread_this(void)
{
#if PJ_HAS_THREADS
pj_thread_t *rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id);
if (rec == NULL) {
pj_assert(!"Calling pjlib from unknown/external thread. You must "
"register external threads with pj_thread_register() "
"before calling any pjlib functions.");
}
/*
* MUST NOT check stack because this function is called
* by PJ_CHECK_STACK() itself!!!
*
*/
return rec;
#else
pj_assert(!"Threading is not enabled!");
return NULL;
#endif
}
/*
* pj_thread_join()
*/
PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
{
#if PJ_HAS_THREADS
pj_thread_t *rec = (pj_thread_t *)p;
void *ret;
int result;
PJ_CHECK_STACK();
PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
result = pthread_join( rec->thread, &ret);
if (result == 0)
return PJ_SUCCESS;
else {
/* Calling pthread_join() on a thread that no longer exists and
* getting back ESRCH isn't an error (in this context).
* Thanks Phil Torre <ptorre@zetron.com>.
*/
return result==ESRCH ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(result);
}
#else
PJ_CHECK_STACK();
pj_assert(!"No multithreading support!");
return PJ_EINVALIDOP;
#endif
}
/*
* pj_thread_destroy()
*/
PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
{
PJ_CHECK_STACK();
/* Destroy mutex used to suspend thread */
if (p->suspended_mutex) {
pj_mutex_destroy(p->suspended_mutex);
p->suspended_mutex = NULL;
}
return PJ_SUCCESS;
}
/*
* pj_thread_sleep()
*/
PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
{
/* TODO: should change this to something like PJ_OS_HAS_NANOSLEEP */
#if defined(PJ_RTEMS) && PJ_RTEMS!=0
enum { NANOSEC_PER_MSEC = 1000000 };
struct timespec req;
PJ_CHECK_STACK();
req.tv_sec = msec / 1000;
req.tv_nsec = (msec % 1000) * NANOSEC_PER_MSEC;
if (nanosleep(&req, NULL) == 0)
return PJ_SUCCESS;
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
#else
PJ_CHECK_STACK();
pj_set_os_error(0);
usleep(msec * 1000);
/* MacOS X (reported on 10.5) seems to always set errno to ETIMEDOUT.
* It does so because usleep() is declared to return int, and we're
* supposed to check for errno only when usleep() returns non-zero.
* Unfortunately, usleep() is declared to return void in other platforms
* so it's not possible to always check for the return value (unless
* we add a detection routine in autoconf).
*
* As a workaround, here we check if ETIMEDOUT is returned and
* return successfully if it is.
*/
if (pj_get_native_os_error() == ETIMEDOUT)
return PJ_SUCCESS;
return pj_get_os_error();
#endif /* PJ_RTEMS */
}
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
/*
* pj_thread_check_stack()
* Implementation for PJ_CHECK_STACK()
*/
PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
{
char stk_ptr;
pj_uint32_t usage;
pj_thread_t *thread = pj_thread_this();
/* Calculate current usage. */
usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
thread->stk_start - &stk_ptr;
/* Assert if stack usage is dangerously high. */
pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
/* Keep statistic. */
if (usage > thread->stk_max_usage) {
thread->stk_max_usage = usage;
thread->caller_file = file;
thread->caller_line = line;
}
}
/*
* pj_thread_get_stack_max_usage()
*/
PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
{
return thread->stk_max_usage;
}
/*
* pj_thread_get_stack_info()
*/
PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
const char **file,
int *line )
{
pj_assert(thread);
*file = thread->caller_file;
*line = thread->caller_line;
return 0;
}
#endif /* PJ_OS_HAS_CHECK_STACK */
///////////////////////////////////////////////////////////////////////////////
/*
* pj_atomic_create()
*/
PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
pj_atomic_value_t initial,
pj_atomic_t **ptr_atomic)
{
pj_status_t rc;
pj_atomic_t *atomic_var;
atomic_var = PJ_POOL_ZALLOC_T(pool, pj_atomic_t);
PJ_ASSERT_RETURN(atomic_var, PJ_ENOMEM);
#if PJ_HAS_THREADS
rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex);
if (rc != PJ_SUCCESS)
return rc;
#endif
atomic_var->value = initial;
*ptr_atomic = atomic_var;
return PJ_SUCCESS;
}
/*
* pj_atomic_destroy()
*/
PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
{
PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL);
#if PJ_HAS_THREADS
return pj_mutex_destroy( atomic_var->mutex );
#else
return 0;
#endif
}
/*
* pj_atomic_set()
*/
PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value)
{
PJ_CHECK_STACK();
#if PJ_HAS_THREADS
pj_mutex_lock( atomic_var->mutex );
#endif
atomic_var->value = value;
#if PJ_HAS_THREADS
pj_mutex_unlock( atomic_var->mutex);
#endif
}
/*
* pj_atomic_get()
*/
PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
{
pj_atomic_value_t oldval;
PJ_CHECK_STACK();
#if PJ_HAS_THREADS
pj_mutex_lock( atomic_var->mutex );
#endif
oldval = atomic_var->value;
#if PJ_HAS_THREADS
pj_mutex_unlock( atomic_var->mutex);
#endif
return oldval;
}
/*
* pj_atomic_inc_and_get()
*/
PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
{
pj_atomic_value_t new_value;
PJ_CHECK_STACK();
#if PJ_HAS_THREADS
pj_mutex_lock( atomic_var->mutex );
#endif
new_value = ++atomic_var->value;
#if PJ_HAS_THREADS
pj_mutex_unlock( atomic_var->mutex);
#endif
return new_value;
}
/*
* pj_atomic_inc()
*/
PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
{
pj_atomic_inc_and_get(atomic_var);
}
/*
* pj_atomic_dec_and_get()
*/
PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
{
pj_atomic_value_t new_value;
PJ_CHECK_STACK();
#if PJ_HAS_THREADS
pj_mutex_lock( atomic_var->mutex );
#endif
new_value = --atomic_var->value;
#if PJ_HAS_THREADS
pj_mutex_unlock( atomic_var->mutex);
#endif
return new_value;
}
/*
* pj_atomic_dec()
*/
PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
{
pj_atomic_dec_and_get(atomic_var);
}
/*
* pj_atomic_add_and_get()
*/
PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
pj_atomic_value_t value )
{
pj_atomic_value_t new_value;
#if PJ_HAS_THREADS
pj_mutex_lock(atomic_var->mutex);
#endif
atomic_var->value += value;
new_value = atomic_var->value;
#if PJ_HAS_THREADS
pj_mutex_unlock(atomic_var->mutex);
#endif
return new_value;
}
/*
* pj_atomic_add()
*/
PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
pj_atomic_value_t value )
{
pj_atomic_add_and_get(atomic_var, value);
}
///////////////////////////////////////////////////////////////////////////////
/*
* pj_thread_local_alloc()
*/
PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index)
{
#if PJ_HAS_THREADS
pthread_key_t key;
int rc;
PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL);
pj_assert( sizeof(pthread_key_t) <= sizeof(long));
if ((rc=pthread_key_create(&key, NULL)) != 0)
return PJ_RETURN_OS_ERROR(rc);
*p_index = key;
return PJ_SUCCESS;
#else
int i;
for (i=0; i<MAX_THREADS; ++i) {
if (tls_flag[i] == 0)
break;
}
if (i == MAX_THREADS)
return PJ_ETOOMANY;
tls_flag[i] = 1;
tls[i] = NULL;
*p_index = i;
return PJ_SUCCESS;
#endif
}
/*
* pj_thread_local_free()
*/
PJ_DEF(void) pj_thread_local_free(long index)
{
PJ_CHECK_STACK();
#if PJ_HAS_THREADS
pthread_key_delete(index);
#else
tls_flag[index] = 0;
#endif
}
/*
* pj_thread_local_set()
*/
PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
{
//Can't check stack because this function is called in the
//beginning before main thread is initialized.
//PJ_CHECK_STACK();
#if PJ_HAS_THREADS
int rc=pthread_setspecific(index, value);
return rc==0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc);
#else
pj_assert(index >= 0 && index < MAX_THREADS);
tls[index] = value;
return PJ_SUCCESS;
#endif
}
PJ_DEF(void*) pj_thread_local_get(long index)
{
//Can't check stack because this function is called
//by PJ_CHECK_STACK() itself!!!
//PJ_CHECK_STACK();
#if PJ_HAS_THREADS
return pthread_getspecific(index);
#else
pj_assert(index >= 0 && index < MAX_THREADS);
return tls[index];
#endif
}
///////////////////////////////////////////////////////////////////////////////
PJ_DEF(void) pj_enter_critical_section(void)
{
#if PJ_HAS_THREADS
pj_mutex_lock(&critical_section);
#endif
}
PJ_DEF(void) pj_leave_critical_section(void)
{
#if PJ_HAS_THREADS
pj_mutex_unlock(&critical_section);
#endif
}
///////////////////////////////////////////////////////////////////////////////
#if defined(PJ_LINUX) && PJ_LINUX!=0
PJ_BEGIN_DECL
PJ_DECL(int) pthread_mutexattr_settype(pthread_mutexattr_t*,int);
PJ_END_DECL
#endif
static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type)
{
#if PJ_HAS_THREADS
pthread_mutexattr_t attr;
int rc;
PJ_CHECK_STACK();
rc = pthread_mutexattr_init(&attr);
if (rc != 0)
return PJ_RETURN_OS_ERROR(rc);
if (type == PJ_MUTEX_SIMPLE) {
#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \
defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE)
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP);
#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \
defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE)
/* Nothing to do, default is simple */
#else
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
#endif
} else {
#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \
defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE)
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \
defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE)
// Phil Torre <ptorre@zetron.com>:
// The RTEMS implementation of POSIX mutexes doesn't include
// pthread_mutexattr_settype(), so what follows is a hack
// until I get RTEMS patched to support the set/get functions.
//
// More info:
// newlib's pthread also lacks pthread_mutexattr_settype(),
// but it seems to have mutexattr.recursive.
PJ_TODO(FIX_RTEMS_RECURSIVE_MUTEX_TYPE)
attr.recursive = 1;
#else
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#endif
}
if (rc != 0) {
return PJ_RETURN_OS_ERROR(rc);
}
rc = pthread_mutex_init(&mutex->mutex, &attr);
if (rc != 0) {
return PJ_RETURN_OS_ERROR(rc);
}
rc = pthread_mutexattr_destroy(&attr);
if (rc != 0) {
pj_status_t status = PJ_RETURN_OS_ERROR(rc);
pthread_mutex_destroy(&mutex->mutex);
return status;
}
#if PJ_DEBUG
/* Set owner. */
mutex->nesting_level = 0;
mutex->owner = NULL;
#endif
/* Set name. */
if (!name) {
name = "mtx%p";
}
if (strchr(name, '%')) {
pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
} else {
strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
}
PJ_LOG(6, (mutex->obj_name, "Mutex created"));
return PJ_SUCCESS;
#else /* PJ_HAS_THREADS */
return PJ_SUCCESS;
#endif
}
/*
* pj_mutex_create()
*/
PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool,
const char *name,
int type,
pj_mutex_t **ptr_mutex)
{
#if PJ_HAS_THREADS
pj_status_t rc;
pj_mutex_t *mutex;
PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL);
mutex = PJ_POOL_ALLOC_T(pool, pj_mutex_t);
PJ_ASSERT_RETURN(mutex, PJ_ENOMEM);
if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS)
return rc;
*ptr_mutex = mutex;
return PJ_SUCCESS;
#else /* PJ_HAS_THREADS */
*ptr_mutex = (pj_mutex_t*)1;
return PJ_SUCCESS;
#endif
}
/*
* pj_mutex_create_simple()
*/
PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool,
const char *name,
pj_mutex_t **mutex )
{
return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
}
/*
* pj_mutex_create_recursive()
*/
PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
const char *name,
pj_mutex_t **mutex )
{
return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
}
/*
* pj_mutex_lock()
*/
PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
{
#if PJ_HAS_THREADS
pj_status_t status;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
#if PJ_DEBUG
PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting (mutex owner=%s)",
pj_thread_this()->obj_name,
mutex->owner_name));
#else
PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting",
pj_thread_this()->obj_name));
#endif
status = pthread_mutex_lock( &mutex->mutex );
#if PJ_DEBUG
if (status == PJ_SUCCESS) {
mutex->owner = pj_thread_this();
pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name);
++mutex->nesting_level;
}
PJ_LOG(6,(mutex->obj_name,
(status==0 ?
"Mutex acquired by thread %s (level=%d)" :
"Mutex acquisition FAILED by %s (level=%d)"),
pj_thread_this()->obj_name,
mutex->nesting_level));
#else
PJ_LOG(6,(mutex->obj_name,
(status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"),
pj_thread_this()->obj_name));
#endif
if (status == 0)
return PJ_SUCCESS;
else
return PJ_RETURN_OS_ERROR(status);
#else /* PJ_HAS_THREADS */
pj_assert( mutex == (pj_mutex_t*)1 );
return PJ_SUCCESS;
#endif
}
/*
* pj_mutex_unlock()
*/
PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
{
#if PJ_HAS_THREADS
pj_status_t status;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
#if PJ_DEBUG
pj_assert(mutex->owner == pj_thread_this());
if (--mutex->nesting_level == 0) {
mutex->owner = NULL;
mutex->owner_name[0] = '\0';
}
PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s (level=%d)",
pj_thread_this()->obj_name,
mutex->nesting_level));
#else
PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s",
pj_thread_this()->obj_name));
#endif
status = pthread_mutex_unlock( &mutex->mutex );
if (status == 0)
return PJ_SUCCESS;
else
return PJ_RETURN_OS_ERROR(status);
#else /* PJ_HAS_THREADS */
pj_assert( mutex == (pj_mutex_t*)1 );
return PJ_SUCCESS;
#endif
}
/*
* pj_mutex_trylock()
*/
PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
{
#if PJ_HAS_THREADS
int status;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is trying",
pj_thread_this()->obj_name));
status = pthread_mutex_trylock( &mutex->mutex );
if (status==0) {
#if PJ_DEBUG
mutex->owner = pj_thread_this();
pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name);
++mutex->nesting_level;
PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s (level=%d)",
pj_thread_this()->obj_name,
mutex->nesting_level));
#else
PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s",
pj_thread_this()->obj_name));
#endif
} else {
PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s's trylock() failed",
pj_thread_this()->obj_name));
}
if (status==0)
return PJ_SUCCESS;
else
return PJ_RETURN_OS_ERROR(status);
#else /* PJ_HAS_THREADS */
pj_assert( mutex == (pj_mutex_t*)1);
return PJ_SUCCESS;
#endif
}
/*
* pj_mutex_destroy()
*/
PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
{
enum { RETRY = 4 };
int status = 0;
unsigned retry;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
#if PJ_HAS_THREADS
PJ_LOG(6,(mutex->obj_name, "Mutex destroyed by thread %s",
pj_thread_this()->obj_name));
for (retry=0; retry<RETRY; ++retry) {
status = pthread_mutex_destroy( &mutex->mutex );
if (status == PJ_SUCCESS)
break;
else if (retry<RETRY-1 && status == EBUSY)
pthread_mutex_unlock(&mutex->mutex);
}
if (status == 0)
return PJ_SUCCESS;
else {
return PJ_RETURN_OS_ERROR(status);
}
#else
pj_assert( mutex == (pj_mutex_t*)1 );
status = PJ_SUCCESS;
return status;
#endif
}
#if PJ_DEBUG
PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
{
#if PJ_HAS_THREADS
return mutex->owner == pj_thread_this();
#else
return 1;
#endif
}
#endif
///////////////////////////////////////////////////////////////////////////////
/*
* Include Read/Write mutex emulation for POSIX platforms that lack it (e.g.
* RTEMS). Otherwise use POSIX rwlock.
*/
#if defined(PJ_EMULATE_RWMUTEX) && PJ_EMULATE_RWMUTEX!=0
/* We need semaphore functionality to emulate rwmutex */
# if !defined(PJ_HAS_SEMAPHORE) || PJ_HAS_SEMAPHORE==0
# error "Semaphore support needs to be enabled to emulate rwmutex"
# endif
# include "os_rwmutex.c"
#else
struct pj_rwmutex_t
{
pthread_rwlock_t rwlock;
};
PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name,
pj_rwmutex_t **p_mutex)
{
pj_rwmutex_t *rwm;
pj_status_t status;
PJ_UNUSED_ARG(name);
rwm = PJ_POOL_ALLOC_T(pool, pj_rwmutex_t);
PJ_ASSERT_RETURN(rwm, PJ_ENOMEM);
status = pthread_rwlock_init(&rwm->rwlock, NULL);
if (status != 0)
return PJ_RETURN_OS_ERROR(status);
*p_mutex = rwm;
return PJ_SUCCESS;
}
/*
* Lock the mutex for reading.
*
*/
PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex)
{
pj_status_t status;
status = pthread_rwlock_rdlock(&mutex->rwlock);
if (status != 0)
return PJ_RETURN_OS_ERROR(status);
return PJ_SUCCESS;
}
/*
* Lock the mutex for writing.
*
*/
PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex)
{
pj_status_t status;
status = pthread_rwlock_wrlock(&mutex->rwlock);
if (status != 0)
return PJ_RETURN_OS_ERROR(status);
return PJ_SUCCESS;
}
/*
* Release read lock.
*
*/
PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex)
{
return pj_rwmutex_unlock_write(mutex);
}
/*
* Release write lock.
*
*/
PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex)
{
pj_status_t status;
status = pthread_rwlock_unlock(&mutex->rwlock);
if (status != 0)
return PJ_RETURN_OS_ERROR(status);
return PJ_SUCCESS;
}
/*
* Destroy reader/writer mutex.
*
*/
PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex)
{
pj_status_t status;
status = pthread_rwlock_destroy(&mutex->rwlock);
if (status != 0)
return PJ_RETURN_OS_ERROR(status);
return PJ_SUCCESS;
}
#endif /* PJ_EMULATE_RWMUTEX */
///////////////////////////////////////////////////////////////////////////////
#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
/*
* pj_sem_create()
*/
PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool,
const char *name,
unsigned initial,
unsigned max,
pj_sem_t **ptr_sem)
{
#if PJ_HAS_THREADS
pj_sem_t *sem;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL);
sem = PJ_POOL_ALLOC_T(pool, pj_sem_t);
PJ_ASSERT_RETURN(sem, PJ_ENOMEM);
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
/* MacOS X doesn't support anonymous semaphore */
{
char sem_name[PJ_GUID_MAX_LENGTH+1];
pj_str_t nam;
/* We should use SEM_NAME_LEN, but this doesn't seem to be
* declared anywhere? The value here is just from trial and error
* to get the longest name supported.
*/
# define MAX_SEM_NAME_LEN 23
/* Create a unique name for the semaphore. */
if (PJ_GUID_STRING_LENGTH <= MAX_SEM_NAME_LEN) {
nam.ptr = sem_name;
pj_generate_unique_string(&nam);
sem_name[nam.slen] = '\0';
} else {
pj_create_random_string(sem_name, MAX_SEM_NAME_LEN);
sem_name[MAX_SEM_NAME_LEN] = '\0';
}
/* Create semaphore */
sem->sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR,
initial);
if (sem->sem == SEM_FAILED)
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
/* And immediately release the name as we don't need it */
sem_unlink(sem_name);
}
#else
sem->sem = PJ_POOL_ALLOC_T(pool, sem_t);
if (sem_init( sem->sem, 0, initial) != 0)
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
#endif
/* Set name. */
if (!name) {
name = "sem%p";
}
if (strchr(name, '%')) {
pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
} else {
strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
}
PJ_LOG(6, (sem->obj_name, "Semaphore created"));
*ptr_sem = sem;
return PJ_SUCCESS;
#else
*ptr_sem = (pj_sem_t*)1;
return PJ_SUCCESS;
#endif
}
/*
* pj_sem_wait()
*/
PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
{
#if PJ_HAS_THREADS
int result;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(sem, PJ_EINVAL);
PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting",
pj_thread_this()->obj_name));
result = sem_wait( sem->sem );
if (result == 0) {
PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
pj_thread_this()->obj_name));
} else {
PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire",
pj_thread_this()->obj_name));
}
if (result == 0)
return PJ_SUCCESS;
else
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
#else
pj_assert( sem == (pj_sem_t*) 1 );
return PJ_SUCCESS;
#endif
}
/*
* pj_sem_trywait()
*/
PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
{
#if PJ_HAS_THREADS
int result;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(sem, PJ_EINVAL);
result = sem_trywait( sem->sem );
if (result == 0) {
PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s",
pj_thread_this()->obj_name));
}
if (result == 0)
return PJ_SUCCESS;
else
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
#else
pj_assert( sem == (pj_sem_t*)1 );
return PJ_SUCCESS;
#endif
}
/*
* pj_sem_post()
*/
PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
{
#if PJ_HAS_THREADS
int result;
PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
pj_thread_this()->obj_name));
result = sem_post( sem->sem );
if (result == 0)
return PJ_SUCCESS;
else
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
#else
pj_assert( sem == (pj_sem_t*) 1);
return PJ_SUCCESS;
#endif
}
/*
* pj_sem_destroy()
*/
PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
{
#if PJ_HAS_THREADS
int result;
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(sem, PJ_EINVAL);
PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
pj_thread_this()->obj_name));
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
result = sem_close( sem->sem );
#else
result = sem_destroy( sem->sem );
#endif
if (result == 0)
return PJ_SUCCESS;
else
return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
#else
pj_assert( sem == (pj_sem_t*) 1 );
return PJ_SUCCESS;
#endif
}
#endif /* PJ_HAS_SEMAPHORE */
///////////////////////////////////////////////////////////////////////////////
#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
/*
* pj_event_create()
*/
PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
pj_bool_t manual_reset, pj_bool_t initial,
pj_event_t **ptr_event)
{
pj_event_t *event;
event = PJ_POOL_ALLOC_T(pool, pj_event_t);
init_mutex(&event->mutex, name, PJ_MUTEX_SIMPLE);
pthread_cond_init(&event->cond, 0);
event->auto_reset = !manual_reset;
event->threads_waiting = 0;
if (initial) {
event->state = EV_STATE_SET;
event->threads_to_release = 1;
} else {
event->state = EV_STATE_OFF;
event->threads_to_release = 0;
}
*ptr_event = event;
return PJ_SUCCESS;
}
static void event_on_one_release(pj_event_t *event)
{
if (event->state == EV_STATE_SET) {
if (event->auto_reset) {
event->threads_to_release = 0;
event->state = EV_STATE_OFF;
} else {
/* Manual reset remains on */
}
} else {
if (event->auto_reset) {
/* Only release one */
event->threads_to_release = 0;
event->state = EV_STATE_OFF;
} else {
event->threads_to_release--;
pj_assert(event->threads_to_release >= 0);
if (event->threads_to_release==0)
event->state = EV_STATE_OFF;
}
}
}
/*
* pj_event_wait()
*/
PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
{
pthread_mutex_lock(&event->mutex.mutex);
event->threads_waiting++;
while (event->state == EV_STATE_OFF)
pthread_cond_wait(&event->cond, &event->mutex.mutex);
event->threads_waiting--;
event_on_one_release(event);
pthread_mutex_unlock(&event->mutex.mutex);
return PJ_SUCCESS;
}
/*
* pj_event_trywait()
*/
PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
{
pj_status_t status;
pthread_mutex_lock(&event->mutex.mutex);
status = event->state != EV_STATE_OFF ? PJ_SUCCESS : -1;
if (status==PJ_SUCCESS) {
event_on_one_release(event);
}
pthread_mutex_unlock(&event->mutex.mutex);
return status;
}
/*
* pj_event_set()
*/
PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
{
pthread_mutex_lock(&event->mutex.mutex);
event->threads_to_release = 1;
event->state = EV_STATE_SET;
if (event->auto_reset)
pthread_cond_signal(&event->cond);
else
pthread_cond_broadcast(&event->cond);
pthread_mutex_unlock(&event->mutex.mutex);
return PJ_SUCCESS;
}
/*
* pj_event_pulse()
*/
PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
{
pthread_mutex_lock(&event->mutex.mutex);
if (event->threads_waiting) {
event->threads_to_release = event->auto_reset ? 1 :
event->threads_waiting;
event->state = EV_STATE_PULSED;
if (event->threads_to_release==1)
pthread_cond_signal(&event->cond);
else
pthread_cond_broadcast(&event->cond);
}
pthread_mutex_unlock(&event->mutex.mutex);
return PJ_SUCCESS;
}
/*
* pj_event_reset()
*/
PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
{
pthread_mutex_lock(&event->mutex.mutex);
event->state = EV_STATE_OFF;
event->threads_to_release = 0;
pthread_mutex_unlock(&event->mutex.mutex);
return PJ_SUCCESS;
}
/*
* pj_event_destroy()
*/
PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
{
pj_mutex_destroy(&event->mutex);
pthread_cond_destroy(&event->cond);
return PJ_SUCCESS;
}
#endif /* PJ_HAS_EVENT_OBJ */
///////////////////////////////////////////////////////////////////////////////
#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
/*
* Terminal
*/
/**
* Set terminal color.
*/
PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
{
/* put bright prefix to ansi_color */
char ansi_color[12] = "\033[01;3";
if (color & PJ_TERM_COLOR_BRIGHT) {
color ^= PJ_TERM_COLOR_BRIGHT;
} else {
strcpy(ansi_color, "\033[00;3");
}
switch (color) {
case 0:
/* black color */
strcat(ansi_color, "0m");
break;
case PJ_TERM_COLOR_R:
/* red color */
strcat(ansi_color, "1m");
break;
case PJ_TERM_COLOR_G:
/* green color */
strcat(ansi_color, "2m");
break;
case PJ_TERM_COLOR_B:
/* blue color */
strcat(ansi_color, "4m");
break;
case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G:
/* yellow color */
strcat(ansi_color, "3m");
break;
case PJ_TERM_COLOR_R | PJ_TERM_COLOR_B:
/* magenta color */
strcat(ansi_color, "5m");
break;
case PJ_TERM_COLOR_G | PJ_TERM_COLOR_B:
/* cyan color */
strcat(ansi_color, "6m");
break;
case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B:
/* white color */
strcat(ansi_color, "7m");
break;
default:
/* default console color */
strcpy(ansi_color, "\033[00m");
break;
}
fputs(ansi_color, stdout);
return PJ_SUCCESS;
}
/**
* Get current terminal foreground color.
*/
PJ_DEF(pj_color_t) pj_term_get_color(void)
{
return 0;
}
#endif /* PJ_TERM_HAS_COLOR */
#if !defined(PJ_DARWINOS) || PJ_DARWINOS == 0
/*
* pj_run_app()
*/
PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
unsigned flags)
{
return (*main_func)(argc, argv);
}
#endif