diff options
Diffstat (limited to '3rdparty/plibsys/src/puthread.c')
-rw-r--r-- | 3rdparty/plibsys/src/puthread.c | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/3rdparty/plibsys/src/puthread.c b/3rdparty/plibsys/src/puthread.c new file mode 100644 index 0000000..ad72e78 --- /dev/null +++ b/3rdparty/plibsys/src/puthread.c @@ -0,0 +1,558 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "patomic.h" +#ifndef P_OS_WIN +# include "perror.h" +#endif +#include "pmem.h" +#include "pspinlock.h" +#include "pstring.h" +#include "puthread.h" +#include "puthread-private.h" + +#ifdef P_OS_OS2 +# define INCL_DOSPROCESS +# define INCL_DOSERRORS +# define INCL_DOSMISC +# include <os2.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifndef P_OS_WIN +# include <unistd.h> +#endif + +#ifdef P_OS_WIN +typedef void (WINAPI * SystemInfoFunc) (LPSYSTEM_INFO); +#endif + +#ifdef P_OS_HPUX +# include <sys/pstat.h> +#endif + +#ifdef P_OS_BSD4 +# include <sys/param.h> +# include <sys/types.h> +# include <sys/sysctl.h> +#endif + +#ifdef P_OS_VMS +# define __NEW_STARLET 1 +# include <starlet.h> +# include <ssdef.h> +# include <stsdef.h> +# include <efndef.h> +# include <iledef.h> +# include <iosbdef.h> +# include <syidef.h> +# include <tis.h> +# include <lib$routines.h> +#endif + +#ifdef P_OS_QNX6 +# include <sys/syspage.h> +#endif + +#ifdef P_OS_BEOS +# include <kernel/OS.h> +#endif + +#ifdef P_OS_SYLLABLE +# include <atheos/sysinfo.h> +#endif + +#if defined (P_OS_SCO) && !defined (_SC_NPROCESSORS_ONLN) +# include <sys/utsname.h> +#endif + +#ifdef P_OS_AMIGA +# include <clib/alib_protos.h> +# include <proto/dos.h> +# include <proto/exec.h> +#endif + +extern void p_uthread_init_internal (void); +extern void p_uthread_shutdown_internal (void); +extern void p_uthread_exit_internal (void); +extern void p_uthread_wait_internal (PUThread *thread); +extern void p_uthread_free_internal (PUThread *thread); +extern void p_uthread_set_name_internal (PUThread *thread); +extern PUThread * p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size); + +static void pp_uthread_cleanup (ppointer data); +static ppointer pp_uthread_proxy (ppointer data); + +#ifndef P_OS_WIN +# if !defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (PLIBSYS_HAS_NANOSLEEP) +static pint pp_uthread_nanosleep (puint32 msec); +# endif +#endif + +static PUThreadKey * pp_uthread_specific_data = NULL; +static PSpinLock * pp_uthread_new_spin = NULL; + +static void +pp_uthread_cleanup (ppointer data) +{ + p_uthread_unref (data); +} + +static ppointer +pp_uthread_proxy (ppointer data) +{ + PUThreadBase *base_thread = data; + + p_uthread_set_local (pp_uthread_specific_data, data); + + p_spinlock_lock (pp_uthread_new_spin); + p_spinlock_unlock (pp_uthread_new_spin); + + if (base_thread->name != NULL) + p_uthread_set_name_internal ((PUThread *) base_thread); + + base_thread->func (base_thread->data); + + return NULL; +} + +void +p_uthread_init (void) +{ + if (P_LIKELY (pp_uthread_specific_data == NULL)) + pp_uthread_specific_data = p_uthread_local_new ((PDestroyFunc) pp_uthread_cleanup); + + if (P_LIKELY (pp_uthread_new_spin == NULL)) + pp_uthread_new_spin = p_spinlock_new (); + + p_uthread_init_internal (); +} + +void +p_uthread_shutdown (void) +{ + PUThread *cur_thread; + + if (P_LIKELY (pp_uthread_specific_data != NULL)) { + cur_thread = p_uthread_get_local (pp_uthread_specific_data); + + if (P_UNLIKELY (cur_thread != NULL)) { + p_uthread_unref (cur_thread); + p_uthread_set_local (pp_uthread_specific_data, NULL); + } + + p_uthread_local_free (pp_uthread_specific_data); + pp_uthread_specific_data = NULL; + } + + if (P_LIKELY (pp_uthread_new_spin != NULL)) { + p_spinlock_free (pp_uthread_new_spin); + pp_uthread_new_spin = NULL; + } + + p_uthread_shutdown_internal (); +} + +P_LIB_API PUThread * +p_uthread_create_full (PUThreadFunc func, + ppointer data, + pboolean joinable, + PUThreadPriority prio, + psize stack_size, + const pchar *name) +{ + PUThreadBase *base_thread; + + if (P_UNLIKELY (func == NULL)) + return NULL; + + p_spinlock_lock (pp_uthread_new_spin); + + base_thread = (PUThreadBase *) p_uthread_create_internal (pp_uthread_proxy, + joinable, + prio, + stack_size); + + if (P_LIKELY (base_thread != NULL)) { + base_thread->ref_count = 2; + base_thread->ours = TRUE; + base_thread->joinable = joinable; + base_thread->func = func; + base_thread->data = data; + base_thread->name = p_strdup (name); + } + + p_spinlock_unlock (pp_uthread_new_spin); + + return (PUThread *) base_thread; +} + +P_LIB_API PUThread * +p_uthread_create (PUThreadFunc func, + ppointer data, + pboolean joinable, + const pchar *name) +{ + /* All checks will be inside */ + return p_uthread_create_full (func, data, joinable, P_UTHREAD_PRIORITY_INHERIT, 0, name); +} + +P_LIB_API void +p_uthread_exit (pint code) +{ + PUThreadBase *base_thread = (PUThreadBase *) p_uthread_current (); + + if (P_UNLIKELY (base_thread == NULL)) + return; + + if (P_UNLIKELY (base_thread->ours == FALSE)) { + P_WARNING ("PUThread::p_uthread_exit: p_uthread_exit() cannot be called from an unknown thread"); + return; + } + + base_thread->ret_code = code; + + p_uthread_exit_internal (); +} + +P_LIB_API pint +p_uthread_join (PUThread *thread) +{ + PUThreadBase *base_thread; + + if (P_UNLIKELY (thread == NULL)) + return -1; + + base_thread = (PUThreadBase *) thread; + + if (base_thread->joinable == FALSE) + return -1; + + p_uthread_wait_internal (thread); + + return base_thread->ret_code; +} + +P_LIB_API PUThread * +p_uthread_current (void) +{ + PUThreadBase *base_thread = p_uthread_get_local (pp_uthread_specific_data); + + if (P_UNLIKELY (base_thread == NULL)) { + if (P_UNLIKELY ((base_thread = p_malloc0 (sizeof (PUThreadBase))) == NULL)) { + P_ERROR ("PUThread::p_uthread_current: failed to allocate memory"); + return NULL; + } + + base_thread->ref_count = 1; + + p_uthread_set_local (pp_uthread_specific_data, base_thread); + } + + return (PUThread *) base_thread; +} + +P_LIB_API pint +p_uthread_ideal_count (void) +{ +#if defined (P_OS_WIN) + SYSTEM_INFO sys_info; + SystemInfoFunc sys_info_func; + + sys_info_func = (SystemInfoFunc) GetProcAddress (GetModuleHandleA ("kernel32.dll"), + "GetNativeSystemInfo"); + + if (P_UNLIKELY (sys_info_func == NULL)) + sys_info_func = (SystemInfoFunc) GetProcAddress (GetModuleHandleA ("kernel32.dll"), + "GetSystemInfo"); + + if (P_UNLIKELY (sys_info_func == NULL)) { + P_ERROR ("PUThread::p_uthread_ideal_count: failed to get address of system info procedure"); + return 1; + } + + sys_info_func (&sys_info); + + return (pint) sys_info.dwNumberOfProcessors; +#elif defined (P_OS_HPUX) + struct pst_dynamic psd; + + if (P_LIKELY (pstat_getdynamic (&psd, sizeof (psd), 1, 0) != -1)) + return (pint) psd.psd_proc_cnt; + else { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call pstat_getdynamic()"); + return 1; + } +#elif defined (P_OS_IRIX) + pint cores; + + cores = sysconf (_SC_NPROC_ONLN); + + if (P_UNLIKELY (cores < 0)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysconf(_SC_NPROC_ONLN)"); + cores = 1; + } + + return cores; +#elif defined (P_OS_BSD4) + pint cores; + pint mib[2]; + size_t len = sizeof (cores); + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + + if (P_UNLIKELY (sysctl (mib, 2, &cores, &len, NULL, 0) == -1)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysctl()"); + return 1; + } + + return (pint) cores; +#elif defined (P_OS_VMS) + pint cores; + pint status; + puint efn; + IOSB iosb; +# if (PLIBSYS_SIZEOF_VOID_P == 4) + ILE3 itmlst[] = { { sizeof (cores), SYI$_AVAILCPU_CNT, &cores, NULL}, + { 0, 0, NULL, NULL} + }; +# else + ILEB_64 itmlst[] = { { 1, SYI$_AVAILCPU_CNT, -1, sizeof (cores), &cores, NULL}, + { 0, 0, 0, 0, NULL, NULL} + }; +# endif + + status = lib$get_ef (&efn); + + if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call lib$get_ef()"); + return 1; + } + + status = sys$getsyi (efn, NULL, NULL, itmlst, &iosb, tis_io_complete, 0); + + if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sys$getsyiw()"); + lib$free_ef (&efn); + return 1; + } + + status = tis_synch (efn, &iosb); + + if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call tis_synch()"); + lib$free_ef (&efn); + return 1; + } + + if (P_UNLIKELY (iosb.iosb$l_getxxi_status != SS$_NORMAL)) { + P_WARNING ("PUThread::p_uthread_ideal_count: l_getxxi_status is not normal"); + lib$free_ef (&efn); + return 1; + } + + lib$free_ef (&efn); + + return cores; +#elif defined (P_OS_OS2) + APIRET ulrc; + ULONG cores; + + if (P_UNLIKELY (DosQuerySysInfo (QSV_NUMPROCESSORS, + QSV_NUMPROCESSORS, + &cores, + sizeof (cores)) != NO_ERROR)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call DosQuerySysInfo()"); + return 1; + } + + return (pint) cores; +#elif defined (P_OS_QNX6) + return (pint) _syspage_ptr->num_cpu; +#elif defined (P_OS_BEOS) + system_info sys_info; + + get_system_info (&sys_info); + + return (pint) sys_info.cpu_count; +#elif defined (P_OS_SYLLABLE) + system_info sys_info; + + if (P_UNLIKELY (get_system_info_v (&sys_info, SYS_INFO_VERSION) != 0)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call get_system_info_v()"); + return 1; + } + + return (pint) sys_info.nCPUCount; +#elif defined (P_OS_AMIGA) + puint32 cores; + + IExec->GetCPUInfoTags (GCIT_NumberOfCPUs, &cores, TAG_END); + + return (pint) cores; +#elif defined (P_OS_SCO) && !defined (_SC_NPROCESSORS_ONLN) + struct scoutsname utsn; + + if (P_UNLIKELY (__scoinfo (&utsn, sizeof (utsn)) == -1)) { + P_ERROR ("PUThread::p_uthread_ideal_count: failed to call __scoinfo()"); + return 1; + } + + return (pint) utsn.numcpu; +#elif defined (_SC_NPROCESSORS_ONLN) + pint cores; + + cores = (pint) sysconf (_SC_NPROCESSORS_ONLN); + + if (P_UNLIKELY (cores == -1)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysconf(_SC_NPROCESSORS_ONLN)"); + return 1; + } + + return cores; +#else + return 1; +#endif +} + +P_LIB_API void +p_uthread_ref (PUThread *thread) +{ + if (P_UNLIKELY (thread == NULL)) + return; + + p_atomic_int_inc (&((PUThreadBase *) thread)->ref_count); +} + +P_LIB_API void +p_uthread_unref (PUThread *thread) +{ + PUThreadBase *base_thread; + + if (P_UNLIKELY (thread == NULL)) + return; + + base_thread = (PUThreadBase *) thread; + + if (p_atomic_int_dec_and_test (&base_thread->ref_count) == TRUE) { + p_free (base_thread->name); + + if (base_thread->ours == TRUE) + p_uthread_free_internal (thread); + else + p_free (thread); + } +} + +#ifndef P_OS_WIN +# include <errno.h> +# if !defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (PLIBSYS_HAS_NANOSLEEP) +# include <sys/select.h> +# include <sys/time.h> +static pint pp_uthread_nanosleep (puint32 msec) +{ + pint rc; + struct timeval tstart, tstop, tremain, time2wait; + + time2wait.tv_sec = msec / 1000; + time2wait.tv_usec = (msec % 1000) * 1000; + + if (P_UNLIKELY (gettimeofday (&tstart, NULL) != 0)) + return -1; + + rc = -1; + + while (rc != 0) { + if (P_UNLIKELY ((rc = select (0, NULL, NULL, NULL, &time2wait)) != 0)) { + if (p_error_get_last_system () == EINTR) { + if (gettimeofday (&tstop, NULL) != 0) + return -1; + + tremain.tv_sec = time2wait.tv_sec - + (tstop.tv_sec - tstart.tv_sec); + tremain.tv_usec = time2wait.tv_usec - + (tstop.tv_usec - tstart.tv_usec); + tremain.tv_sec += tremain.tv_usec / 1000000L; + tremain.tv_usec %= 1000000L; + } else + return -1; + } + } + + return 0; +} +# endif +#endif + +P_LIB_API pint +p_uthread_sleep (puint32 msec) +{ +#if defined (P_OS_WIN) + Sleep (msec); + return 0; +#elif defined (P_OS_OS2) + return (DosSleep (msec) == NO_ERROR) ? 0 : -1; +#elif defined (P_OS_AMIGA) + return TimeDelay (0, msec / 1000, (msec % 1000) * 1000) == 0 ? 0 : -1; +#elif defined (PLIBSYS_HAS_CLOCKNANOSLEEP) || defined (PLIBSYS_HAS_NANOSLEEP) + pint result; + struct timespec time_req; + struct timespec time_rem; + + memset (&time_rem, 0, sizeof (struct timespec)); + + time_req.tv_nsec = (msec % 1000) * 1000000L; + time_req.tv_sec = (time_t) (msec / 1000); + + result = -1; + while (result != 0) { + /* Syllable has unimplemented clock_nanocleep() call */ +# if defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (P_OS_SYLLABLE) + if (P_UNLIKELY ((result = clock_nanosleep (CLOCK_MONOTONIC, + 0, + &time_req, + &time_rem)) != 0)) { +# else + if (P_UNLIKELY ((result = nanosleep (&time_req, &time_rem)) != 0)) { +# endif + if (p_error_get_last_system () == EINTR) + time_req = time_rem; + else + return -1; + } + } + + return 0; +#else + return pp_uthread_nanosleep (msec); +#endif +} |