/* * Cross Platform Thread/Mutex abstraction * Copyright(C) 2007 Michael Jerris * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so. * * This work is provided under this license on an "as is" basis, without warranty of any kind, * either expressed or implied, including, without limitation, warranties that the covered code * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire * risk as to the quality and performance of the covered code is with you. Should any covered * code prove defective in any respect, you (not the initial developer or any other contributor) * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty * constitutes an essential part of this license. No use of any covered code is authorized hereunder * except under this disclaimer. * */ #include "ks.h" size_t thread_default_stacksize = 240 * 1024; #ifndef WIN32 pthread_once_t init_priority = PTHREAD_ONCE_INIT; #endif KS_DECLARE(ks_thread_os_handle_t) ks_thread_os_handle(ks_thread_t *thread) { return thread->handle; } KS_DECLARE(ks_thread_os_handle_t) ks_thread_self(void) { #ifdef WIN32 return GetCurrentThread(); #else return pthread_self(); #endif } static void ks_thread_init_priority(void) { #ifdef WIN32 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); #else #ifdef USE_SCHED_SETSCHEDULER /* * Try to use a round-robin scheduler * with a fallback if that does not work */ struct sched_param sched = { 0 }; sched.sched_priority = KS_PRI_LOW; if (sched_setscheduler(0, SCHED_FIFO, &sched)) { sched.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, &sched)) { return; } } #endif #endif return; } void ks_thread_override_default_stacksize(size_t size) { thread_default_stacksize = size; } static void ks_thread_cleanup(ks_pool_t *mpool, void *ptr, void *arg, int type, ks_pool_cleanup_action_t action, ks_pool_cleanup_type_t ctype) { ks_thread_t *thread = (ks_thread_t *) ptr; switch(action) { case KS_MPCL_ANNOUNCE: thread->running = 0; break; case KS_MPCL_TEARDOWN: if (!(thread->flags & KS_THREAD_FLAG_DETATCHED)) { ks_thread_join(thread); } break; case KS_MPCL_DESTROY: #ifdef WIN32 if (!(thread->flags & KS_THREAD_FLAG_DETATCHED)) { CloseHandle(thread->handle); } #endif break; } } static void *KS_THREAD_CALLING_CONVENTION thread_launch(void *args) { ks_thread_t *thread = (ks_thread_t *) args; #ifdef HAVE_PTHREAD_SETSCHEDPARAM if (thread->priority) { int policy = SCHED_FIFO; struct sched_param param = { 0 }; pthread_t tt = pthread_self(); pthread_once(&init_priority, ks_thread_init_priority); pthread_getschedparam(tt, &policy, ¶m); param.sched_priority = thread->priority; pthread_setschedparam(tt, policy, ¶m); } #endif thread->return_data = thread->function(thread, thread->private_data); #ifndef WIN32 pthread_attr_destroy(&thread->attribute); #endif return thread->return_data; } KS_DECLARE(int) ks_thread_set_priority(int nice_val) { #ifdef WIN32 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); #else #ifdef USE_SCHED_SETSCHEDULER /* * Try to use a round-robin scheduler * with a fallback if that does not work */ struct sched_param sched = { 0 }; sched.sched_priority = KS_PRI_LOW; if (sched_setscheduler(0, SCHED_FIFO, &sched)) { sched.sched_priority = 0; if (sched_setscheduler(0, SCHED_OTHER, &sched)) { return -1; } } #endif if (nice_val) { #ifdef HAVE_SETPRIORITY /* * setpriority() works on FreeBSD (6.2), nice() doesn't */ if (setpriority(PRIO_PROCESS, getpid(), nice_val) < 0) { ks_log(KS_LOG_CRIT, "Could not set nice level\n"); return -1; } #else if (nice(nice_val) != nice_val) { ks_log(KS_LOG_CRIT, "Could not set nice level\n"); return -1; } #endif } #endif return 0; } KS_DECLARE(uint8_t) ks_thread_priority(ks_thread_t *thread) { uint8_t priority = 0; #ifdef WIN32 DWORD pri = GetThreadPriority(thread->handle); if (pri >= THREAD_PRIORITY_TIME_CRITICAL) { priority = 99; } else if (pri >= THREAD_PRIORITY_ABOVE_NORMAL) { priority = 50; } else { priority = 10; } #else int policy; struct sched_param param = { 0 }; pthread_getschedparam(thread->handle, &policy, ¶m); priority = param.sched_priority; #endif return priority; } KS_DECLARE(ks_status_t) ks_thread_join(ks_thread_t *thread) { #ifdef WIN32 WaitForSingleObject(thread->handle, INFINITE); #else void *ret; pthread_join(thread->handle, &ret); #endif return KS_STATUS_SUCCESS; } KS_DECLARE(ks_status_t) ks_thread_create_ex(ks_thread_t **rthread, ks_thread_function_t func, void *data, uint32_t flags, size_t stack_size, ks_thread_priority_t priority, ks_pool_t *pool) { ks_thread_t *thread = NULL; ks_status_t status = KS_STATUS_FAIL; if (!rthread) goto done; *rthread = NULL; if (!func || !pool) goto done; thread = (ks_thread_t *) ks_pool_alloc(pool, sizeof(ks_thread_t)); if (!thread) goto done; thread->private_data = data; thread->function = func; thread->stack_size = stack_size; thread->running = 1; thread->flags = flags; thread->priority = priority; thread->pool = pool; #if defined(WIN32) thread->handle = (void *) _beginthreadex(NULL, (unsigned) thread->stack_size, (unsigned int (__stdcall *) (void *)) thread_launch, thread, 0, NULL); if (!thread->handle) { goto fail; } if (priority >= 99) { SetThreadPriority(thread->handle, THREAD_PRIORITY_TIME_CRITICAL); } else if (priority >= 50) { SetThreadPriority(thread->handle, THREAD_PRIORITY_ABOVE_NORMAL); } else if (priority >= 10) { SetThreadPriority(thread->handle, THREAD_PRIORITY_NORMAL); } else if (priority >= 1) { SetThreadPriority(thread->handle, THREAD_PRIORITY_LOWEST); } if (flags & KS_THREAD_FLAG_DETATCHED) { CloseHandle(thread->handle); } status = KS_STATUS_SUCCESS; goto done; #else if (pthread_attr_init(&thread->attribute) != 0) goto fail; if ((flags & KS_THREAD_FLAG_DETATCHED) && pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0) goto failpthread; if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0) goto failpthread; if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) goto failpthread; status = KS_STATUS_SUCCESS; goto done; failpthread: pthread_attr_destroy(&thread->attribute); #endif fail: if (thread) { thread->running = 0; if (pool) { ks_pool_free(pool, &thread); } } done: if (status == KS_STATUS_SUCCESS) { *rthread = thread; ks_pool_set_cleanup(pool, thread, NULL, 0, ks_thread_cleanup); } return status; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */