📄 os_core_unix.c
字号:
/* $Id: os_core_unix.c 1075 2007-03-16 18:41:07Z bennylp $ */
/*
* Copyright (C)2003-2007 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.
*/
#define _GNU_SOURCE
#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) && PJ_HAS_SEMAPHORE != 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
{
char obj_name[PJ_MAX_OBJ_NAME];
};
#endif /* PJ_HAS_EVENT_OBJ */
#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;
#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
/* 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. */
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
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;
/* 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;
}
#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
}
/*
* 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 = 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_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_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!!!
*
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -