diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a73b7f7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +file(GLOB_RECURSE WIN_PTHREAD_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/*.c") +file(GLOB_RECURSE WIN_PTHREAD_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/*.h") +add_library(winpthreads STATIC ${WIN_PTHREAD_SOURCE} ${WIN_PTHREAD_HEADER}) + +add_definitions(-D_DEBUG) +add_definitions(-D_CONSOLE) +add_definitions(-D_MBCS) diff --git a/include/pthread.h b/include/pthread.h new file mode 100644 index 0000000..b1b5c81 --- /dev/null +++ b/include/pthread.h @@ -0,0 +1,701 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +/* + * Parts of this library are derived by: + * + * Posix Threads library for Microsoft Windows + * + * Use at own risk, there is no implied warranty to this code. + * It uses undocumented features of Microsoft Windows that can change + * at any time in the future. + * + * (C) 2010 Lockless Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Lockless Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef WIN_PTHREADS_H +#define WIN_PTHREADS_H + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "pthread_compat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define __WINPTHREADS_VERSION_MAJOR 0 +#define __WINPTHREADS_VERSION_MINOR 5 +#define __WINPTHREADS_VERSION_PATCHLEVEL 0 + +/* MSB 8-bit major version, 8-bit minor version, 16-bit patch level. */ +#define __WINPTHREADS_VERSION 0x00050000 + +#if defined(IN_WINPTHREAD) +# if defined(DLL_EXPORT) +# define WINPTHREAD_API __declspec(dllexport) /* building the DLL */ +# else +# define WINPTHREAD_API /* building the static library */ +# endif +#else +# if defined(WINPTHREADS_USE_DLLIMPORT) +# define WINPTHREAD_API __declspec(dllimport) /* user wants explicit `dllimport` */ +# else +# define WINPTHREAD_API /* the default; auto imported in case of DLL */ +# endif +#endif + +/* #define WINPTHREAD_DBG 1 */ + +/* Compatibility stuff: */ +#define RWLS_PER_THREAD 8 + +/* Error-codes. */ +#ifndef ETIMEDOUT +#define ETIMEDOUT 138 +#endif +#ifndef ENOTSUP +#define ENOTSUP 129 +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK 140 +#endif + +/* pthread specific defines. */ + +#define PTHREAD_CANCEL_DISABLE 0 +#define PTHREAD_CANCEL_ENABLE 0x01 + +#define PTHREAD_CANCEL_DEFERRED 0 +#define PTHREAD_CANCEL_ASYNCHRONOUS 0x02 + +#define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_CREATE_DETACHED 0x04 + +#define PTHREAD_EXPLICIT_SCHED 0 +#define PTHREAD_INHERIT_SCHED 0x08 + +#define PTHREAD_SCOPE_PROCESS 0 +#define PTHREAD_SCOPE_SYSTEM 0x10 + +#define PTHREAD_DEFAULT_ATTR (PTHREAD_CANCEL_ENABLE) + +#define PTHREAD_CANCELED ((void *) (intptr_t) 0xDEADBEEF) + +#define _PTHREAD_NULL_THREAD ((pthread_t) 0) + +#define PTHREAD_ONCE_INIT 0 + +#define PTHREAD_DESTRUCTOR_ITERATIONS 256 +#define PTHREAD_KEYS_MAX (1<<20) + +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_ERRORCHECK 1 +#define PTHREAD_MUTEX_RECURSIVE 2 +#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL + +#define PTHREAD_MUTEX_SHARED 1 +#define PTHREAD_MUTEX_PRIVATE 0 + +#define PTHREAD_PRIO_NONE 0 +#define PTHREAD_PRIO_INHERIT 8 +#define PTHREAD_PRIO_PROTECT 16 +#define PTHREAD_PRIO_MULT 32 +#define PTHREAD_PROCESS_SHARED 1 +#define PTHREAD_PROCESS_PRIVATE 0 + +#define PTHREAD_MUTEX_FAST_NP PTHREAD_MUTEX_NORMAL +#define PTHREAD_MUTEX_TIMED_NP PTHREAD_MUTEX_FAST_NP +#define PTHREAD_MUTEX_ADAPTIVE_NP PTHREAD_MUTEX_FAST_NP +#define PTHREAD_MUTEX_ERRORCHECK_NP PTHREAD_MUTEX_ERRORCHECK +#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE + +WINPTHREAD_API void * pthread_timechange_handler_np(void * dummy); +WINPTHREAD_API int pthread_delay_np (const struct timespec *interval); +WINPTHREAD_API int pthread_num_processors_np(void); +WINPTHREAD_API int pthread_set_num_processors_np(int n); + +#define PTHREAD_BARRIER_SERIAL_THREAD 1 + +/* maximum number of times a read lock may be obtained */ +#define MAX_READ_LOCKS (INT_MAX - 1) + +/* No fork() in windows - so ignore this */ +#define pthread_atfork(F1,F2,F3) 0 + +/* unsupported stuff: */ +#define pthread_mutex_getprioceiling(M, P) ENOTSUP +#define pthread_mutex_setprioceiling(M, P) ENOTSUP +#define pthread_getcpuclockid(T, C) ENOTSUP +#define pthread_attr_getguardsize(A, S) ENOTSUP +#define pthread_attr_setgaurdsize(A, S) ENOTSUP + +typedef long pthread_once_t; +typedef unsigned pthread_mutexattr_t; +typedef unsigned pthread_key_t; +typedef void *pthread_barrierattr_t; +typedef int pthread_condattr_t; +typedef int pthread_rwlockattr_t; + +/* +struct _pthread_v; + +typedef struct pthread_t { + struct _pthread_v *p; + int x; +} pthread_t; +*/ + +typedef uintptr_t pthread_t; + +typedef struct _pthread_cleanup _pthread_cleanup; +struct _pthread_cleanup +{ + void (*func)(void *); + void *arg; + _pthread_cleanup *next; +}; + +/* Using MemoryBarrier() requires including Windows headers. User code + * may want to use pthread_cleanup_push without including Windows headers + * first, thus prefer GCC specific intrinsics where possible. */ +#ifdef __GNUC__ +#define __pthread_MemoryBarrier() __sync_synchronize() +#else +#define __pthread_MemoryBarrier() MemoryBarrier() +#endif + +#define pthread_cleanup_push(F, A) \ + do { \ + const _pthread_cleanup _pthread_cup = \ + { (F), (A), *pthread_getclean() }; \ + __pthread_MemoryBarrier(); \ + *pthread_getclean() = (_pthread_cleanup *) &_pthread_cup; \ + __pthread_MemoryBarrier(); \ + do { \ + do {} while (0) + +/* Note that if async cancelling is used, then there is a race here */ +#define pthread_cleanup_pop(E) \ + } while (0); \ + *pthread_getclean() = _pthread_cup.next; \ + if ((E)) _pthread_cup.func((pthread_once_t *)_pthread_cup.arg); \ + } while (0) + +#ifndef SCHED_OTHER +/* Some POSIX realtime extensions, mostly stubbed */ +#define SCHED_OTHER 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 +#define SCHED_MIN SCHED_OTHER +#define SCHED_MAX SCHED_RR + +struct sched_param { + int sched_priority; +}; + +WINPTHREAD_API int sched_yield(void); +WINPTHREAD_API int sched_get_priority_min(int pol); +WINPTHREAD_API int sched_get_priority_max(int pol); +WINPTHREAD_API int sched_getscheduler(pid_t pid); +WINPTHREAD_API int sched_setscheduler(pid_t pid, int pol, const struct sched_param *param); + +#endif + +typedef struct pthread_attr_t pthread_attr_t; +struct pthread_attr_t +{ + unsigned p_state; + void *stack; + size_t s_size; + struct sched_param param; +}; + +WINPTHREAD_API int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); +WINPTHREAD_API int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param); +WINPTHREAD_API int pthread_getschedparam(pthread_t thread, int *pol, struct sched_param *param); +WINPTHREAD_API int pthread_setschedparam(pthread_t thread, int pol, const struct sched_param *param); +WINPTHREAD_API int pthread_attr_setschedpolicy (pthread_attr_t *attr, int pol); +WINPTHREAD_API int pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *pol); + +/* synchronization objects */ +typedef intptr_t pthread_spinlock_t; +typedef intptr_t pthread_mutex_t; +typedef intptr_t pthread_cond_t; +typedef intptr_t pthread_rwlock_t; +typedef void *pthread_barrier_t; + +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_ERRORCHECK 1 +#define PTHREAD_MUTEX_RECURSIVE 2 + +#define GENERIC_INITIALIZER -1 +#define GENERIC_ERRORCHECK_INITIALIZER -2 +#define GENERIC_RECURSIVE_INITIALIZER -3 +#define GENERIC_NORMAL_INITIALIZER -1 +#define PTHREAD_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_INITIALIZER +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_RECURSIVE_INITIALIZER +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_ERRORCHECK_INITIALIZER +#define PTHREAD_NORMAL_MUTEX_INITIALIZER (pthread_mutex_t)GENERIC_NORMAL_INITIALIZER +#define PTHREAD_DEFAULT_MUTEX_INITIALIZER PTHREAD_NORMAL_MUTEX_INITIALIZER +#define PTHREAD_COND_INITIALIZER (pthread_cond_t)GENERIC_INITIALIZER +#define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t)GENERIC_INITIALIZER +#define PTHREAD_SPINLOCK_INITIALIZER (pthread_spinlock_t)GENERIC_INITIALIZER + +WINPTHREAD_API extern void (**_pthread_key_dest)(void *); +WINPTHREAD_API int pthread_key_create(pthread_key_t *key, void (* dest)(void *)); +WINPTHREAD_API int pthread_key_delete(pthread_key_t key); +WINPTHREAD_API void * pthread_getspecific(pthread_key_t key); +WINPTHREAD_API int pthread_setspecific(pthread_key_t key, const void *value); + +WINPTHREAD_API pthread_t pthread_self(void); +WINPTHREAD_API int pthread_once(pthread_once_t *o, void (*func)(void)); +WINPTHREAD_API void pthread_testcancel(void); +WINPTHREAD_API int pthread_equal(pthread_t t1, pthread_t t2); +WINPTHREAD_API void pthread_tls_init(void); +WINPTHREAD_API void _pthread_cleanup_dest(pthread_t t); +WINPTHREAD_API int pthread_get_concurrency(int *val); +WINPTHREAD_API int pthread_set_concurrency(int val); +WINPTHREAD_API void pthread_exit(void *res); +WINPTHREAD_API void _pthread_invoke_cancel(void); +WINPTHREAD_API int pthread_cancel(pthread_t t); +WINPTHREAD_API int pthread_kill(pthread_t t, int sig); +WINPTHREAD_API unsigned _pthread_get_state(const pthread_attr_t *attr, unsigned flag); +WINPTHREAD_API int _pthread_set_state(pthread_attr_t *attr, unsigned flag, unsigned val); +WINPTHREAD_API int pthread_setcancelstate(int state, int *oldstate); +WINPTHREAD_API int pthread_setcanceltype(int type, int *oldtype); +WINPTHREAD_API unsigned __stdcall pthread_create_wrapper(void *args); +WINPTHREAD_API int pthread_create(pthread_t *th, const pthread_attr_t *attr, void *(* func)(void *), void *arg); +WINPTHREAD_API int pthread_join(pthread_t t, void **res); +WINPTHREAD_API int pthread_detach(pthread_t t); +WINPTHREAD_API int pthread_setname_np(pthread_t thread, const char *name); +WINPTHREAD_API int pthread_getname_np(pthread_t thread, char *name, size_t len); + + +WINPTHREAD_API int pthread_rwlock_init(pthread_rwlock_t *rwlock_, const pthread_rwlockattr_t *attr); +WINPTHREAD_API int pthread_rwlock_wrlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *ts); +WINPTHREAD_API int pthread_rwlock_rdlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_timedrdlock(pthread_rwlock_t *l, const struct timespec *ts); +WINPTHREAD_API int pthread_rwlock_unlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_tryrdlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_trywrlock(pthread_rwlock_t *l); +WINPTHREAD_API int pthread_rwlock_destroy (pthread_rwlock_t *l); + +WINPTHREAD_API int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *a); +WINPTHREAD_API int pthread_cond_destroy(pthread_cond_t *cv); +WINPTHREAD_API int pthread_cond_signal (pthread_cond_t *cv); +WINPTHREAD_API int pthread_cond_broadcast (pthread_cond_t *cv); +WINPTHREAD_API int pthread_cond_wait (pthread_cond_t *cv, pthread_mutex_t *external_mutex); +WINPTHREAD_API int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *t); +WINPTHREAD_API int pthread_cond_timedwait_relative_np(pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *t); + +WINPTHREAD_API int pthread_mutex_lock(pthread_mutex_t *m); +WINPTHREAD_API int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *ts); +WINPTHREAD_API int pthread_mutex_unlock(pthread_mutex_t *m); +WINPTHREAD_API int pthread_mutex_trylock(pthread_mutex_t *m); +WINPTHREAD_API int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a); +WINPTHREAD_API int pthread_mutex_destroy(pthread_mutex_t *m); + +WINPTHREAD_API int pthread_barrier_destroy(pthread_barrier_t *b); +WINPTHREAD_API int pthread_barrier_init(pthread_barrier_t *b, const void *attr, unsigned int count); +WINPTHREAD_API int pthread_barrier_wait(pthread_barrier_t *b); + +WINPTHREAD_API int pthread_spin_init(pthread_spinlock_t *l, int pshared); +WINPTHREAD_API int pthread_spin_destroy(pthread_spinlock_t *l); +/* No-fair spinlock due to lack of knowledge of thread number. */ +WINPTHREAD_API int pthread_spin_lock(pthread_spinlock_t *l); +WINPTHREAD_API int pthread_spin_trylock(pthread_spinlock_t *l); +WINPTHREAD_API int pthread_spin_unlock(pthread_spinlock_t *l); + +WINPTHREAD_API int pthread_attr_init(pthread_attr_t *attr); +WINPTHREAD_API int pthread_attr_destroy(pthread_attr_t *attr); +WINPTHREAD_API int pthread_attr_setdetachstate(pthread_attr_t *a, int flag); +WINPTHREAD_API int pthread_attr_getdetachstate(const pthread_attr_t *a, int *flag); +WINPTHREAD_API int pthread_attr_setinheritsched(pthread_attr_t *a, int flag); +WINPTHREAD_API int pthread_attr_getinheritsched(const pthread_attr_t *a, int *flag); +WINPTHREAD_API int pthread_attr_setscope(pthread_attr_t *a, int flag); +WINPTHREAD_API int pthread_attr_getscope(const pthread_attr_t *a, int *flag); +WINPTHREAD_API int pthread_attr_getstack(const pthread_attr_t *attr, void **stack, size_t *size); +WINPTHREAD_API int pthread_attr_setstack(pthread_attr_t *attr, void *stack, size_t size); +WINPTHREAD_API int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stack); +WINPTHREAD_API int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stack); +WINPTHREAD_API int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *size); +WINPTHREAD_API int pthread_attr_setstacksize(pthread_attr_t *attr, size_t size); + +WINPTHREAD_API int pthread_mutexattr_init(pthread_mutexattr_t *a); +WINPTHREAD_API int pthread_mutexattr_destroy(pthread_mutexattr_t *a); +WINPTHREAD_API int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type); +WINPTHREAD_API int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type); +WINPTHREAD_API int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type); +WINPTHREAD_API int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type); +WINPTHREAD_API int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type); +WINPTHREAD_API int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type); +WINPTHREAD_API int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio); +WINPTHREAD_API int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio); +WINPTHREAD_API int pthread_getconcurrency(void); +WINPTHREAD_API int pthread_setconcurrency(int new_level); + +WINPTHREAD_API int pthread_condattr_destroy(pthread_condattr_t *a); +WINPTHREAD_API int pthread_condattr_init(pthread_condattr_t *a); +WINPTHREAD_API int pthread_condattr_getpshared(const pthread_condattr_t *a, int *s); +WINPTHREAD_API int pthread_condattr_setpshared(pthread_condattr_t *a, int s); + +#ifndef __clockid_t_defined +typedef int clockid_t; +#define __clockid_t_defined 1 +#endif /* __clockid_t_defined */ + +WINPTHREAD_API int pthread_condattr_getclock (const pthread_condattr_t *attr, + clockid_t *clock_id); +WINPTHREAD_API int pthread_condattr_setclock(pthread_condattr_t *attr, + clockid_t clock_id); +WINPTHREAD_API int __pthread_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp); + +WINPTHREAD_API int pthread_barrierattr_init(void **attr); +WINPTHREAD_API int pthread_barrierattr_destroy(void **attr); +WINPTHREAD_API int pthread_barrierattr_setpshared(void **attr, int s); +WINPTHREAD_API int pthread_barrierattr_getpshared(void **attr, int *s); + +/* Private extensions for analysis and internal use. */ +WINPTHREAD_API struct _pthread_cleanup ** pthread_getclean (void); +WINPTHREAD_API void * pthread_gethandle (pthread_t t); +WINPTHREAD_API void * pthread_getevent (void); + +WINPTHREAD_API unsigned long long _pthread_rel_time_in_ms(const struct timespec *ts); +WINPTHREAD_API unsigned long long _pthread_time_in_ms(void); +WINPTHREAD_API unsigned long long _pthread_time_in_ms_from_timespec(const struct timespec *ts); +WINPTHREAD_API int _pthread_tryjoin (pthread_t t, void **res); +WINPTHREAD_API int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a); +WINPTHREAD_API int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s); +WINPTHREAD_API int pthread_rwlockattr_init(pthread_rwlockattr_t *a); +WINPTHREAD_API int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s); + +#ifndef SIG_BLOCK +#define SIG_BLOCK 0 +#endif +#ifndef SIG_UNBLOCK +#define SIG_UNBLOCK 1 +#endif +#ifndef SIG_SETMASK +#define SIG_SETMASK 2 +#endif + +#include + +#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS + +#undef _POSIX_THREAD_KEYS_MAX +#define _POSIX_THREAD_KEYS_MAX PTHREAD_KEYS_MAX + +#undef PTHREAD_THREADS_MAX +#define PTHREAD_THREADS_MAX 2019 + +#undef _POSIX_SEM_NSEMS_MAX +#define _POSIX_SEM_NSEMS_MAX 256 + +#undef SEM_NSEMS_MAX +#define SEM_NSEMS_MAX 1024 + +/* Wrap cancellation points. */ +#if defined(__WINPTHREAD_ENABLE_WRAP_API) \ + || defined(__WINPTRHEAD_ENABLE_WRAP_API) /* historical typo */ +#define accept(...) (pthread_testcancel(), accept(__VA_ARGS__)) +#define aio_suspend(...) (pthread_testcancel(), aio_suspend(__VA_ARGS__)) +#define clock_nanosleep(...) (pthread_testcancel(), clock_nanosleep(__VA_ARGS__)) +#define close(...) (pthread_testcancel(), close(__VA_ARGS__)) +#define connect(...) (pthread_testcancel(), connect(__VA_ARGS__)) +#define creat(...) (pthread_testcancel(), creat(__VA_ARGS__)) +#define fcntl(...) (pthread_testcancel(), fcntl(__VA_ARGS__)) +#define fdatasync(...) (pthread_testcancel(), fdatasync(__VA_ARGS__)) +#define fsync(...) (pthread_testcancel(), fsync(__VA_ARGS__)) +#define getmsg(...) (pthread_testcancel(), getmsg(__VA_ARGS__)) +#define getpmsg(...) (pthread_testcancel(), getpmsg(__VA_ARGS__)) +#define lockf(...) (pthread_testcancel(), lockf(__VA_ARGS__)) +#define mg_receive(...) (pthread_testcancel(), mg_receive(__VA_ARGS__)) +#define mg_send(...) (pthread_testcancel(), mg_send(__VA_ARGS__)) +#define mg_timedreceive(...) (pthread_testcancel(), mg_timedreceive(__VA_ARGS__)) +#define mg_timessend(...) (pthread_testcancel(), mg_timedsend(__VA_ARGS__)) +#define msgrcv(...) (pthread_testcancel(), msgrecv(__VA_ARGS__)) +#define msgsnd(...) (pthread_testcancel(), msgsnd(__VA_ARGS__)) +#define msync(...) (pthread_testcancel(), msync(__VA_ARGS__)) +#define nanosleep(...) (pthread_testcancel(), nanosleep(__VA_ARGS__)) +#define open(...) (pthread_testcancel(), open(__VA_ARGS__)) +#define pause(...) (pthread_testcancel(), pause(__VA_ARGS__)) +#define poll(...) (pthread_testcancel(), poll(__VA_ARGS__)) +#define pread(...) (pthread_testcancel(), pread(__VA_ARGS__)) +#define pselect(...) (pthread_testcancel(), pselect(__VA_ARGS__)) +#define putmsg(...) (pthread_testcancel(), putmsg(__VA_ARGS__)) +#define putpmsg(...) (pthread_testcancel(), putpmsg(__VA_ARGS__)) +#define pwrite(...) (pthread_testcancel(), pwrite(__VA_ARGS__)) +#define read(...) (pthread_testcancel(), read(__VA_ARGS__)) +#define readv(...) (pthread_testcancel(), readv(__VA_ARGS__)) +#define recv(...) (pthread_testcancel(), recv(__VA_ARGS__)) +#define recvfrom(...) (pthread_testcancel(), recvfrom(__VA_ARGS__)) +#define recvmsg(...) (pthread_testcancel(), recvmsg(__VA_ARGS__)) +#define select(...) (pthread_testcancel(), select(__VA_ARGS__)) +#define sem_timedwait(...) (pthread_testcancel(), sem_timedwait(__VA_ARGS__)) +#define sem_wait(...) (pthread_testcancel(), sem_wait(__VA_ARGS__)) +#define send(...) (pthread_testcancel(), send(__VA_ARGS__)) +#define sendmsg(...) (pthread_testcancel(), sendmsg(__VA_ARGS__)) +#define sendto(...) (pthread_testcancel(), sendto(__VA_ARGS__)) +#define sigpause(...) (pthread_testcancel(), sigpause(__VA_ARGS__)) +#define sigsuspend(...) (pthread_testcancel(), sigsuspend(__VA_ARGS__)) +#define sigwait(...) (pthread_testcancel(), sigwait(__VA_ARGS__)) +#define sigwaitinfo(...) (pthread_testcancel(), sigwaitinfo(__VA_ARGS__)) +#define sleep(...) (pthread_testcancel(), sleep(__VA_ARGS__)) +//#define Sleep(...) (pthread_testcancel(), Sleep(__VA_ARGS__)) +#define system(...) (pthread_testcancel(), system(__VA_ARGS__)) +#define access(...) (pthread_testcancel(), access(__VA_ARGS__)) +#define asctime(...) (pthread_testcancel(), asctime(__VA_ARGS__)) +#define catclose(...) (pthread_testcancel(), catclose(__VA_ARGS__)) +#define catgets(...) (pthread_testcancel(), catgets(__VA_ARGS__)) +#define catopen(...) (pthread_testcancel(), catopen(__VA_ARGS__)) +#define closedir(...) (pthread_testcancel(), closedir(__VA_ARGS__)) +#define closelog(...) (pthread_testcancel(), closelog(__VA_ARGS__)) +#define ctermid(...) (pthread_testcancel(), ctermid(__VA_ARGS__)) +#define ctime(...) (pthread_testcancel(), ctime(__VA_ARGS__)) +#define dbm_close(...) (pthread_testcancel(), dbm_close(__VA_ARGS__)) +#define dbm_delete(...) (pthread_testcancel(), dbm_delete(__VA_ARGS__)) +#define dbm_fetch(...) (pthread_testcancel(), dbm_fetch(__VA_ARGS__)) +#define dbm_nextkey(...) (pthread_testcancel(), dbm_nextkey(__VA_ARGS__)) +#define dbm_open(...) (pthread_testcancel(), dbm_open(__VA_ARGS__)) +#define dbm_store(...) (pthread_testcancel(), dbm_store(__VA_ARGS__)) +#define dlclose(...) (pthread_testcancel(), dlclose(__VA_ARGS__)) +#define dlopen(...) (pthread_testcancel(), dlopen(__VA_ARGS__)) +#define endgrent(...) (pthread_testcancel(), endgrent(__VA_ARGS__)) +#define endhostent(...) (pthread_testcancel(), endhostent(__VA_ARGS__)) +#define endnetent(...) (pthread_testcancel(), endnetent(__VA_ARGS__)) +#define endprotoent(...) (pthread_testcancel(), endprotoend(__VA_ARGS__)) +#define endpwent(...) (pthread_testcancel(), endpwent(__VA_ARGS__)) +#define endservent(...) (pthread_testcancel(), endservent(__VA_ARGS__)) +#define endutxent(...) (pthread_testcancel(), endutxent(__VA_ARGS__)) +#define fclose(...) (pthread_testcancel(), fclose(__VA_ARGS__)) +#define fflush(...) (pthread_testcancel(), fflush(__VA_ARGS__)) +#define fgetc(...) (pthread_testcancel(), fgetc(__VA_ARGS__)) +#define fgetpos(...) (pthread_testcancel(), fgetpos(__VA_ARGS__)) +#define fgets(...) (pthread_testcancel(), fgets(__VA_ARGS__)) +#define fgetwc(...) (pthread_testcancel(), fgetwc(__VA_ARGS__)) +#define fgetws(...) (pthread_testcancel(), fgetws(__VA_ARGS__)) +#define fmtmsg(...) (pthread_testcancel(), fmtmsg(__VA_ARGS__)) +#define fopen(...) (pthread_testcancel(), fopen(__VA_ARGS__)) +#define fpathconf(...) (pthread_testcancel(), fpathconf(__VA_ARGS__)) +#define fprintf(...) (pthread_testcancel(), fprintf(__VA_ARGS__)) +#define fputc(...) (pthread_testcancel(), fputc(__VA_ARGS__)) +#define fputs(...) (pthread_testcancel(), fputs(__VA_ARGS__)) +#define fputwc(...) (pthread_testcancel(), fputwc(__VA_ARGS__)) +#define fputws(...) (pthread_testcancel(), fputws(__VA_ARGS__)) +#define fread(...) (pthread_testcancel(), fread(__VA_ARGS__)) +#define freopen(...) (pthread_testcancel(), freopen(__VA_ARGS__)) +#define fscanf(...) (pthread_testcancel(), fscanf(__VA_ARGS__)) +#define fseek(...) (pthread_testcancel(), fseek(__VA_ARGS__)) +#define fseeko(...) (pthread_testcancel(), fseeko(__VA_ARGS__)) +#define fsetpos(...) (pthread_testcancel(), fsetpos(__VA_ARGS__)) +#define fstat(...) (pthread_testcancel(), fstat(__VA_ARGS__)) +#define ftell(...) (pthread_testcancel(), ftell(__VA_ARGS__)) +#define ftello(...) (pthread_testcancel(), ftello(__VA_ARGS__)) +#define ftw(...) (pthread_testcancel(), ftw(__VA_ARGS__)) +#define fwprintf(...) (pthread_testcancel(), fwprintf(__VA_ARGS__)) +#define fwrite(...) (pthread_testcancel(), fwrite(__VA_ARGS__)) +#define fwscanf(...) (pthread_testcancel(), fwscanf(__VA_ARGS__)) +#define getaddrinfo(...) (pthread_testcancel(), getaddrinfo(__VA_ARGS__)) +#define getc(...) (pthread_testcancel(), getc(__VA_ARGS__)) +#define getc_unlocked(...) (pthread_testcancel(), getc_unlocked(__VA_ARGS__)) +#define getchar(...) (pthread_testcancel(), getchar(__VA_ARGS__)) +#define getchar_unlocked(...) (pthread_testcancel(), getchar_unlocked(__VA_ARGS__)) +#define getcwd(...) (pthread_testcancel(), getcwd(__VA_ARGS__)) +#define getdate(...) (pthread_testcancel(), getdate(__VA_ARGS__)) +#define getgrent(...) (pthread_testcancel(), getgrent(__VA_ARGS__)) +#define getgrgid(...) (pthread_testcancel(), getgrgid(__VA_ARGS__)) +#define getgrgid_r(...) (pthread_testcancel(), getgrgid_r(__VA_ARGS__)) +#define gergrnam(...) (pthread_testcancel(), getgrnam(__VA_ARGS__)) +#define getgrnam_r(...) (pthread_testcancel(), getgrnam_r(__VA_ARGS__)) +#define gethostbyaddr(...) (pthread_testcancel(), gethostbyaddr(__VA_ARGS__)) +#define gethostbyname(...) (pthread_testcancel(), gethostbyname(__VA_ARGS__)) +#define gethostent(...) (pthread_testcancel(), gethostent(__VA_ARGS__)) +#define gethostid(...) (pthread_testcancel(), gethostid(__VA_ARGS__)) +#define gethostname(...) (pthread_testcancel(), gethostname(__VA_ARGS__)) +#define getlogin(...) (pthread_testcancel(), getlogin(__VA_ARGS__)) +#define getlogin_r(...) (pthread_testcancel(), getlogin_r(__VA_ARGS__)) +#define getnameinfo(...) (pthread_testcancel(), getnameinfo(__VA_ARGS__)) +#define getnetbyaddr(...) (pthread_testcancel(), getnetbyaddr(__VA_ARGS__)) +#define getnetbyname(...) (pthread_testcancel(), getnetbyname(__VA_ARGS__)) +#define getnetent(...) (pthread_testcancel(), getnetent(__VA_ARGS__)) +#define getopt(...) (pthread_testcancel(), getopt(__VA_ARGS__)) +#define getprotobyname(...) (pthread_testcancel(), getprotobyname(__VA_ARGS__)) +#define getprotobynumber(...) (pthread_testcancel(), getprotobynumber(__VA_ARGS__)) +#define getprotoent(...) (pthread_testcancel(), getprotoent(__VA_ARGS__)) +#define getpwent(...) (pthread_testcancel(), getpwent(__VA_ARGS__)) +#define getpwnam(...) (pthread_testcancel(), getpwnam(__VA_ARGS__)) +#define getpwnam_r(...) (pthread_testcancel(), getpwnam_r(__VA_ARGS__)) +#define getpwuid(...) (pthread_testcancel(), getpwuid(__VA_ARGS__)) +#define getpwuid_r(...) (pthread_testcancel(), getpwuid_r(__VA_ARGS__)) +#define gets(...) (pthread_testcancel(), gets(__VA_ARGS__)) +#define getservbyname(...) (pthread_testcancel(), getservbyname(__VA_ARGS__)) +#define getservbyport(...) (pthread_testcancel(), getservbyport(__VA_ARGS__)) +#define getservent(...) (pthread_testcancel(), getservent(__VA_ARGS__)) +#define getutxent(...) (pthread_testcancel(), getutxent(__VA_ARGS__)) +#define getutxid(...) (pthread_testcancel(), getutxid(__VA_ARGS__)) +#define getutxline(...) (pthread_testcancel(), getutxline(__VA_ARGS__)) +#undef getwc +#define getwc(...) (pthread_testcancel(), getwc(__VA_ARGS__)) +#undef getwchar +#define getwchar(...) (pthread_testcancel(), getwchar(__VA_ARGS__)) +#define getwd(...) (pthread_testcancel(), getwd(__VA_ARGS__)) +#define glob(...) (pthread_testcancel(), glob(__VA_ARGS__)) +#define iconv_close(...) (pthread_testcancel(), iconv_close(__VA_ARGS__)) +#define iconv_open(...) (pthread_testcancel(), iconv_open(__VA_ARGS__)) +#define ioctl(...) (pthread_testcancel(), ioctl(__VA_ARGS__)) +#define link(...) (pthread_testcancel(), link(__VA_ARGS__)) +#define localtime(...) (pthread_testcancel(), localtime(__VA_ARGS__)) +#define lseek(...) (pthread_testcancel(), lseek(__VA_ARGS__)) +#define lstat(...) (pthread_testcancel(), lstat(__VA_ARGS__)) +#define mkstemp(...) (pthread_testcancel(), mkstemp(__VA_ARGS__)) +#define nftw(...) (pthread_testcancel(), nftw(__VA_ARGS__)) +#define opendir(...) (pthread_testcancel(), opendir(__VA_ARGS__)) +#define openlog(...) (pthread_testcancel(), openlog(__VA_ARGS__)) +#define pathconf(...) (pthread_testcancel(), pathconf(__VA_ARGS__)) +#define pclose(...) (pthread_testcancel(), pclose(__VA_ARGS__)) +#define perror(...) (pthread_testcancel(), perror(__VA_ARGS__)) +#define popen(...) (pthread_testcancel(), popen(__VA_ARGS__)) +#define posix_fadvise(...) (pthread_testcancel(), posix_fadvise(__VA_ARGS__)) +#define posix_fallocate(...) (pthread_testcancel(), posix_fallocate(__VA_ARGS__)) +#define posix_madvise(...) (pthread_testcancel(), posix_madvise(__VA_ARGS__)) +#define posix_openpt(...) (pthread_testcancel(), posix_openpt(__VA_ARGS__)) +#define posix_spawn(...) (pthread_testcancel(), posix_spawn(__VA_ARGS__)) +#define posix_spawnp(...) (pthread_testcancel(), posix_spawnp(__VA_ARGS__)) +#define posix_trace_clear(...) (pthread_testcancel(), posix_trace_clear(__VA_ARGS__)) +#define posix_trace_close(...) (pthread_testcancel(), posix_trace_close(__VA_ARGS__)) +#define posix_trace_create(...) (pthread_testcancel(), posix_trace_create(__VA_ARGS__)) +#define posix_trace_create_withlog(...) (pthread_testcancel(), posix_trace_create_withlog(__VA_ARGS__)) +#define posix_trace_eventtypelist_getne(...) (pthread_testcancel(), posix_trace_eventtypelist_getne(__VA_ARGS__)) +#define posix_trace_eventtypelist_rewin(...) (pthread_testcancel(), posix_trace_eventtypelist_rewin(__VA_ARGS__)) +#define posix_trace_flush(...) (pthread_testcancel(), posix_trace_flush(__VA_ARGS__)) +#define posix_trace_get_attr(...) (pthread_testcancel(), posix_trace_get_attr(__VA_ARGS__)) +#define posix_trace_get_filter(...) (pthread_testcancel(), posix_trace_get_filter(__VA_ARGS__)) +#define posix_trace_get_status(...) (pthread_testcancel(), posix_trace_get_status(__VA_ARGS__)) +#define posix_trace_getnext_event(...) (pthread_testcancel(), posix_trace_getnext_event(__VA_ARGS__)) +#define posix_trace_open(...) (pthread_testcancel(), posix_trace_open(__VA_ARGS__)) +#define posix_trace_rewind(...) (pthread_testcancel(), posix_trace_rewind(__VA_ARGS__)) +#define posix_trace_setfilter(...) (pthread_testcancel(), posix_trace_setfilter(__VA_ARGS__)) +#define posix_trace_shutdown(...) (pthread_testcancel(), posix_trace_shutdown(__VA_ARGS__)) +#define posix_trace_timedgetnext_event(...) (pthread_testcancel(), posix_trace_timedgetnext_event(__VA_ARGS__)) +#define posix_typed_mem_open(...) (pthread_testcancel(), posix_typed_mem_open(__VA_ARGS__)) +#define printf(...) (pthread_testcancel(), printf(__VA_ARGS__)) +#define putc(...) (pthread_testcancel(), putc(__VA_ARGS__)) +#define putc_unlocked(...) (pthread_testcancel(), putc_unlocked(__VA_ARGS__)) +#define putchar(...) (pthread_testcancel(), putchar(__VA_ARGS__)) +#define putchar_unlocked(...) (pthread_testcancel(), putchar_unlocked(__VA_ARGS__)) +#define puts(...) (pthread_testcancel(), puts(__VA_ARGS__)) +#define pututxline(...) (pthread_testcancel(), pututxline(__VA_ARGS__)) +#undef putwc +#define putwc(...) (pthread_testcancel(), putwc(__VA_ARGS__)) +#undef putwchar +#define putwchar(...) (pthread_testcancel(), putwchar(__VA_ARGS__)) +#define readdir(...) (pthread_testcancel(), readdir(__VA_ARSG__)) +#define readdir_r(...) (pthread_testcancel(), readdir_r(__VA_ARGS__)) +#define remove(...) (pthread_testcancel(), remove(__VA_ARGS__)) +#define rename(...) (pthread_testcancel(), rename(__VA_ARGS__)) +#define rewind(...) (pthread_testcancel(), rewind(__VA_ARGS__)) +#define rewinddir(...) (pthread_testcancel(), rewinddir(__VA_ARGS__)) +#define scanf(...) (pthread_testcancel(), scanf(__VA_ARGS__)) +#define seekdir(...) (pthread_testcancel(), seekdir(__VA_ARGS__)) +#define semop(...) (pthread_testcancel(), semop(__VA_ARGS__)) +#define setgrent(...) (pthread_testcancel(), setgrent(__VA_ARGS__)) +#define sethostent(...) (pthread_testcancel(), sethostemt(__VA_ARGS__)) +#define setnetent(...) (pthread_testcancel(), setnetent(__VA_ARGS__)) +#define setprotoent(...) (pthread_testcancel(), setprotoent(__VA_ARGS__)) +#define setpwent(...) (pthread_testcancel(), setpwent(__VA_ARGS__)) +#define setservent(...) (pthread_testcancel(), setservent(__VA_ARGS__)) +#define setutxent(...) (pthread_testcancel(), setutxent(__VA_ARGS__)) +#define stat(...) (pthread_testcancel(), stat(__VA_ARGS__)) +#define strerror(...) (pthread_testcancel(), strerror(__VA_ARGS__)) +#define strerror_r(...) (pthread_testcancel(), strerror_r(__VA_ARGS__)) +#define strftime(...) (pthread_testcancel(), strftime(__VA_ARGS__)) +#define symlink(...) (pthread_testcancel(), symlink(__VA_ARGS__)) +#define sync(...) (pthread_testcancel(), sync(__VA_ARGS__)) +#define syslog(...) (pthread_testcancel(), syslog(__VA_ARGS__)) +#define tmpfile(...) (pthread_testcancel(), tmpfile(__VA_ARGS__)) +#define tmpnam(...) (pthread_testcancel(), tmpnam(__VA_ARGS__)) +#define ttyname(...) (pthread_testcancel(), ttyname(__VA_ARGS__)) +#define ttyname_r(...) (pthread_testcancel(), ttyname_r(__VA_ARGS__)) +#define tzset(...) (pthread_testcancel(), tzset(__VA_ARGS__)) +#define ungetc(...) (pthread_testcancel(), ungetc(__VA_ARGS__)) +#define ungetwc(...) (pthread_testcancel(), ungetwc(__VA_ARGS__)) +#define unlink(...) (pthread_testcancel(), unlink(__VA_ARGS__)) +#define vfprintf(...) (pthread_testcancel(), vfprintf(__VA_ARGS__)) +#define vfwprintf(...) (pthread_testcancel(), vfwprintf(__VA_ARGS__)) +#define vprintf(...) (pthread_testcancel(), vprintf(__VA_ARGS__)) +#define vwprintf(...) (pthread_testcancel(), vwprintf(__VA_ARGS__)) +#define wcsftime(...) (pthread_testcancel(), wcsftime(__VA_ARGS__)) +#define wordexp(...) (pthread_testcancel(), wordexp(__VA_ARGS__)) +#define wprintf(...) (pthread_testcancel(), wprintf(__VA_ARGS__)) +#define wscanf(...) (pthread_testcancel(), wscanf(__VA_ARGS__)) +#endif + +/* We deal here with a gcc issue for posix threading on Windows. + We would need to change here gcc's gthr-posix.h header, but this + got rejected. So we deal it within this header. */ +#ifdef _GTHREAD_USE_MUTEX_INIT_FUNC +#undef _GTHREAD_USE_MUTEX_INIT_FUNC +#endif +#define _GTHREAD_USE_MUTEX_INIT_FUNC 1 + +#ifdef __cplusplus +} +#endif + +#endif /* WIN_PTHREADS_H */ diff --git a/include/pthread_compat.h b/include/pthread_compat.h new file mode 100644 index 0000000..63f5f49 --- /dev/null +++ b/include/pthread_compat.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +/* + * Parts of this library are derived by: + * + * Posix Threads library for Microsoft Windows + * + * Use at own risk, there is no implied warranty to this code. + * It uses undocumented features of Microsoft Windows that can change + * at any time in the future. + * + * (C) 2010 Lockless Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Lockless Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WIN_PTHREADS_PTHREAD_COMPAT_H +#define WIN_PTHREADS_PTHREAD_COMPAT_H + +#ifdef __GNUC__ + +#define WINPTHREADS_INLINE inline +#define WINPTHREADS_ATTRIBUTE(X) __attribute__(X) +#define WINPTHREADS_SECTION(X) __section__(X) + +#elif _MSC_VER + +#include "pthread_time.h" + +#ifdef _WIN64 +typedef __int64 pid_t; +#else +typedef int pid_t; +#endif +typedef int clockid_t; + +#define WINPTHREADS_INLINE __inline +#define WINPTHREADS_ATTRIBUTE(X) __declspec X +#define WINPTHREADS_SECTION(X) allocate(X) + +#endif + +#endif diff --git a/include/pthread_signal.h b/include/pthread_signal.h new file mode 100644 index 0000000..42a50ae --- /dev/null +++ b/include/pthread_signal.h @@ -0,0 +1,29 @@ +/* + Copyright (c) 2013-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREADS_SIGNAL_H +#define WIN_PTHREADS_SIGNAL_H + +/* Windows has rudimentary signals support. */ +#define pthread_sigmask(H, S1, S2) 0 + +#endif /* WIN_PTHREADS_SIGNAL_H */ diff --git a/include/pthread_time.h b/include/pthread_time.h new file mode 100644 index 0000000..eec4168 --- /dev/null +++ b/include/pthread_time.h @@ -0,0 +1,102 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 + +#ifndef WIN_PTHREADS_TIME_H +#define WIN_PTHREADS_TIME_H + +/* Posix timers are supported */ +#ifndef _POSIX_TIMERS +#define _POSIX_TIMERS 200809L +#endif + +/* Monotonic clocks are available. */ +#ifndef _POSIX_MONOTONIC_CLOCK +#define _POSIX_MONOTONIC_CLOCK 200809L +#endif + +/* CPU-time clocks are available. */ +#ifndef _POSIX_CPUTIME +#define _POSIX_CPUTIME 200809L +#endif + +/* Clock support in threads are available. */ +#ifndef _POSIX_THREAD_CPUTIME +#define _POSIX_THREAD_CPUTIME 200809L +#endif + +#ifndef __clockid_t_defined +typedef int clockid_t; +#define __clockid_t_defined 1 +#endif /* __clockid_t_defined */ + +#ifndef TIMER_ABSTIME +#define TIMER_ABSTIME 1 +#endif + +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 +#endif + +#ifndef CLOCK_PROCESS_CPUTIME_ID +#define CLOCK_PROCESS_CPUTIME_ID 2 +#endif + +#ifndef CLOCK_THREAD_CPUTIME_ID +#define CLOCK_THREAD_CPUTIME_ID 3 +#endif + +#ifndef CLOCK_REALTIME_COARSE +#define CLOCK_REALTIME_COARSE 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Make sure we provide default for WINPTHREAD_API, if not defined. */ +#pragma push_macro("WINPTHREAD_API") +#ifndef WINPTHREAD_API +#define WINPTHREAD_API +#endif + +/* These should really be dllimport'ed if using winpthread dll */ +WINPTHREAD_API int __cdecl nanosleep(const struct timespec *request, struct timespec *remain); + +WINPTHREAD_API int __cdecl clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain); +WINPTHREAD_API int __cdecl clock_getres(clockid_t clock_id, struct timespec *res); +WINPTHREAD_API int __cdecl clock_gettime(clockid_t clock_id, struct timespec *tp); +WINPTHREAD_API int __cdecl clock_settime(clockid_t clock_id, const struct timespec *tp); + +#pragma pop_macro("WINPTHREAD_API") + +#ifdef __cplusplus +} +#endif + +#endif /* WIN_PTHREADS_TIME_H */ + diff --git a/include/pthread_unistd.h b/include/pthread_unistd.h new file mode 100644 index 0000000..6469353 --- /dev/null +++ b/include/pthread_unistd.h @@ -0,0 +1,192 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREADS_UNISTD_H +#define WIN_PTHREADS_UNISTD_H + +/* Set defines described by the POSIX Threads Extension (1003.1c-1995) */ +/* _SC_THREADS + Basic support for POSIX threads is available. The functions + + pthread_atfork(), + pthread_attr_destroy(), + pthread_attr_getdetachstate(), + pthread_attr_getschedparam(), + pthread_attr_init(), + pthread_attr_setdetachstate(), + pthread_attr_setschedparam(), + pthread_cancel(), + pthread_cleanup_push(), + pthread_cleanup_pop(), + pthread_cond_broadcast(), + pthread_cond_destroy(), + pthread_cond_init(), + pthread_cond_signal(), + pthread_cond_timedwait(), + pthread_cond_wait(), + pthread_condattr_destroy(), + pthread_condattr_init(), + pthread_create(), + pthread_detach(), + pthread_equal(), + pthread_exit(), + pthread_getspecific(), + pthread_join(, + pthread_key_create(), + pthread_key_delete(), + pthread_mutex_destroy(), + pthread_mutex_init(), + pthread_mutex_lock(), + pthread_mutex_trylock(), + pthread_mutex_unlock(), + pthread_mutexattr_destroy(), + pthread_mutexattr_init(), + pthread_once(), + pthread_rwlock_destroy(), + pthread_rwlock_init(), + pthread_rwlock_rdlock(), + pthread_rwlock_tryrdlock(), + pthread_rwlock_trywrlock(), + pthread_rwlock_unlock(), + pthread_rwlock_wrlock(), + pthread_rwlockattr_destroy(), + pthread_rwlockattr_init(), + pthread_self(), + pthread_setcancelstate(), + pthread_setcanceltype(), + pthread_setspecific(), + pthread_testcancel() + + are present. */ +#undef _POSIX_THREADS +#define _POSIX_THREADS 200112L + +/* _SC_READER_WRITER_LOCKS + This option implies the _POSIX_THREADS option. Conversely, under + POSIX 1003.1-2001 the _POSIX_THREADS option implies this option. + + The functions + pthread_rwlock_destroy(), + pthread_rwlock_init(), + pthread_rwlock_rdlock(), + pthread_rwlock_tryrdlock(), + pthread_rwlock_trywrlock(), + pthread_rwlock_unlock(), + pthread_rwlock_wrlock(), + pthread_rwlockattr_destroy(), + pthread_rwlockattr_init() + + are present. +*/ +#undef _POSIX_READER_WRITER_LOCKS +#define _POSIX_READER_WRITER_LOCKS 200112L + +/* _SC_SPIN_LOCKS + This option implies the _POSIX_THREADS and _POSIX_THREAD_SAFE_FUNCTIONS + options. The functions + + pthread_spin_destroy(), + pthread_spin_init(), + pthread_spin_lock(), + pthread_spin_trylock(), + pthread_spin_unlock() + + are present. */ +#undef _POSIX_SPIN_LOCKS +#define _POSIX_SPIN_LOCKS 200112L + +/* _SC_BARRIERS + This option implies the _POSIX_THREADS and _POSIX_THREAD_SAFE_FUNCTIONS + options. The functions + + pthread_barrier_destroy(), + pthread_barrier_init(), + pthread_barrier_wait(), + pthread_barrierattr_destroy(), + pthread_barrierattr_init() + + are present. +*/ +#undef _POSIX_BARRIERS +#define _POSIX_BARRIERS 200112L + +/* _SC_TIMEOUTS + The functions + + mq_timedreceive(), - not supported + mq_timedsend(), - not supported + posix_trace_timedgetnext_event(), - not supported + pthread_mutex_timedlock(), + pthread_rwlock_timedrdlock(), + pthread_rwlock_timedwrlock(), + sem_timedwait(), + + are present. */ +#undef _POSIX_TIMEOUTS +#define _POSIX_TIMEOUTS 200112L + +/* _SC_TIMERS - not supported + The functions + + clock_getres(), + clock_gettime(), + clock_settime(), + nanosleep(), + timer_create(), + timer_delete(), + timer_gettime(), + timer_getoverrun(), + timer_settime() + + are present. */ +/* #undef _POSIX_TIMERS */ + +/* _SC_CLOCK_SELECTION + This option implies the _POSIX_TIMERS option. The functions + + pthread_condattr_getclock(), + pthread_condattr_setclock(), + clock_nanosleep() + + are present. +*/ +#undef _POSIX_CLOCK_SELECTION +#define _POSIX_CLOCK_SELECTION 200112 + +/* _SC_SEMAPHORES + The include file is present. The functions + + sem_close(), + sem_destroy(), + sem_getvalue(), + sem_init(), + sem_open(), + sem_post(), + sem_trywait(), + sem_unlink(), + sem_wait() + + are present. */ +#undef _POSIX_SEMAPHORES +#define _POSIX_SEMAPHORES 200112 + +#endif /* WIN_PTHREADS_UNISTD_H */ diff --git a/include/sched.h b/include/sched.h new file mode 100644 index 0000000..bf28487 --- /dev/null +++ b/include/sched.h @@ -0,0 +1,83 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 +#include +#include + +#include +#include +#include + +#include + +#ifndef WIN_PTHREADS_SCHED_H +#define WIN_PTHREADS_SCHED_H + +#ifndef SCHED_OTHER +/* Some POSIX realtime extensions, mostly stubbed */ +#define SCHED_OTHER 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 +#define SCHED_MIN SCHED_OTHER +#define SCHED_MAX SCHED_RR + +struct sched_param { + int sched_priority; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(IN_WINPTHREAD) +# if defined(DLL_EXPORT) && !defined(WINPTHREAD_EXPORT_ALL_DEBUG) +# define WINPTHREAD_SCHED_API __declspec(dllexport) /* building the DLL */ +# else +# define WINPTHREAD_SCHED_API /* building the static library */ +# endif +#else +# if defined(WINPTHREADS_USE_DLLIMPORT) +# define WINPTHREAD_SCHED_API __declspec(dllimport) /* user wants explicit `dllimport` */ +# else +# define WINPTHREAD_SCHED_API /* the default; auto imported in case of DLL */ +# endif +#endif + +WINPTHREAD_SCHED_API int sched_yield(void); +WINPTHREAD_SCHED_API int sched_get_priority_min(int pol); +WINPTHREAD_SCHED_API int sched_get_priority_max(int pol); +WINPTHREAD_SCHED_API int sched_getscheduler(pid_t pid); +WINPTHREAD_SCHED_API int sched_setscheduler(pid_t pid, int pol, const struct sched_param *param); + +#ifdef __cplusplus +} +#endif + +#endif + +#ifndef sched_rr_get_interval +#define sched_rr_get_interval(_p, _i) \ + ( errno = ENOTSUP, (int) -1 ) +#endif + +#endif /* WIN_PTHREADS_SCHED_H */ diff --git a/include/semaphore.h b/include/semaphore.h new file mode 100644 index 0000000..8e3fa6f --- /dev/null +++ b/include/semaphore.h @@ -0,0 +1,85 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREADS_SEMAPHORE_H +#define WIN_PTHREADS_SEMAPHORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(IN_WINPTHREAD) +# if defined(DLL_EXPORT) && !defined(WINPTHREAD_EXPORT_ALL_DEBUG) +# define WINPTHREAD_SEMA_API __declspec(dllexport) /* building the DLL */ +# else +# define WINPTHREAD_SEMA_API /* building the static library */ +# endif +#else +# if defined(WINPTHREADS_USE_DLLIMPORT) +# define WINPTHREAD_SEMA_API __declspec(dllimport) /* user wants explicit `dllimport` */ +# else +# define WINPTHREAD_SEMA_API /* the default; auto imported in case of DLL */ +# endif +#endif + +/* Set this to 0 to disable it */ +#define USE_SEM_CriticalSection_SpinCount 100 + +#define SEM_VALUE_MAX INT_MAX + +#ifndef _MODE_T_ +#define _MODE_T_ +typedef unsigned short mode_t; +#endif + +typedef void *sem_t; + +#define SEM_FAILED NULL + +WINPTHREAD_SEMA_API int sem_init(sem_t * sem, int pshared, unsigned int value); + +WINPTHREAD_SEMA_API int sem_destroy(sem_t *sem); + +WINPTHREAD_SEMA_API int sem_trywait(sem_t *sem); + +WINPTHREAD_SEMA_API int sem_wait(sem_t *sem); + +WINPTHREAD_SEMA_API int sem_timedwait(sem_t * sem, const struct timespec *t); + +WINPTHREAD_SEMA_API int sem_post(sem_t *sem); + +WINPTHREAD_SEMA_API int sem_post_multiple(sem_t *sem, int count); + +/* yes, it returns a semaphore (or SEM_FAILED) */ +WINPTHREAD_SEMA_API sem_t * sem_open(const char * name, int oflag, mode_t mode, unsigned int value); + +WINPTHREAD_SEMA_API int sem_close(sem_t * sem); + +WINPTHREAD_SEMA_API int sem_unlink(const char * name); + +WINPTHREAD_SEMA_API int sem_getvalue(sem_t * sem, int * sval); + +#ifdef __cplusplus +} +#endif + +#endif /* WIN_PTHREADS_SEMAPHORE_H */ diff --git a/src/barrier.c b/src/barrier.c new file mode 100644 index 0000000..e973aaa --- /dev/null +++ b/src/barrier.c @@ -0,0 +1,246 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 +#include +#include +#include "pthread.h" +#include "barrier.h" +#include "ref.h" +#include "misc.h" + +static pthread_spinlock_t barrier_global = PTHREAD_SPINLOCK_INITIALIZER; + +static WINPTHREADS_ATTRIBUTE((noinline)) int +barrier_unref(volatile pthread_barrier_t *barrier, int res) +{ + pthread_spin_lock(&barrier_global); +#ifdef WINPTHREAD_DBG + assert((((barrier_t *)*barrier)->valid == LIFE_BARRIER) && (((barrier_t *)*barrier)->busy > 0)); +#endif + ((barrier_t *)*barrier)->busy -= 1; + pthread_spin_unlock(&barrier_global); + return res; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int barrier_ref(volatile pthread_barrier_t *barrier) +{ + int r = 0; + pthread_spin_lock(&barrier_global); + + if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL; + else { + ((barrier_t *)*barrier)->busy += 1; + } + + pthread_spin_unlock(&barrier_global); + + return r; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int +barrier_ref_destroy(volatile pthread_barrier_t *barrier, pthread_barrier_t *bDestroy) +{ + int r = 0; + + *bDestroy = NULL; + pthread_spin_lock(&barrier_global); + + if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL; + else { + barrier_t *b_ = (barrier_t *)*barrier; + if (b_->busy) r = EBUSY; + else { + *bDestroy = *barrier; + *barrier = NULL; + } + } + + pthread_spin_unlock(&barrier_global); + return r; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) void +barrier_ref_set (volatile pthread_barrier_t *barrier, void *v) +{ + pthread_spin_lock(&barrier_global); + *barrier = v; + pthread_spin_unlock(&barrier_global); +} + +int pthread_barrier_destroy(pthread_barrier_t *b_) +{ + pthread_barrier_t bDestroy; + barrier_t *b; + int r; + + while ((r = barrier_ref_destroy(b_,&bDestroy)) == EBUSY) + Sleep(0); + + if (r) + return r; + + b = (barrier_t *)bDestroy; + + pthread_mutex_lock(&b->m); + + if (sem_destroy(&b->sems[0]) != 0) + { + /* Could this happen? */ + *b_ = bDestroy; + pthread_mutex_unlock (&b->m); + return EBUSY; + } + if (sem_destroy(&b->sems[1]) != 0) + { + sem_init (&b->sems[0], b->share, 0); + *b_ = bDestroy; + pthread_mutex_unlock (&b->m); + return -1; + } + pthread_mutex_unlock(&b->m); + if(pthread_mutex_destroy(&b->m) != 0) { + sem_init (&b->sems[0], b->share, 0); + sem_init (&b->sems[1], b->share, 0); + *b_ = bDestroy; + return -1; + } + b->valid = DEAD_BARRIER; + free(bDestroy); + return 0; + +} + +int +pthread_barrier_init (pthread_barrier_t *b_, const void *attr, + unsigned int count) +{ + barrier_t *b; + + if (!count || !b_) + return EINVAL; + + if ((b = (pthread_barrier_t)calloc(1,sizeof(*b))) == NULL) + return ENOMEM; + if (!attr || *((int **)attr) == NULL) + b->share = PTHREAD_PROCESS_PRIVATE; + else + memcpy (&b->share, *((void **) attr), sizeof (int)); + b->total = count; + b->count = count; + b->valid = LIFE_BARRIER; + b->sel = 0; + + if (pthread_mutex_init(&b->m, NULL) != 0) + { + free (b); + return ENOMEM; + } + + if (sem_init(&b->sems[0], b->share, 0) != 0) + { + pthread_mutex_destroy(&b->m); + free (b); + return ENOMEM; + } + if (sem_init(&b->sems[1], b->share, 0) != 0) + { + pthread_mutex_destroy(&b->m); + sem_destroy(&b->sems[0]); + free (b); + return ENOMEM; + } + barrier_ref_set (b_,b); + + return 0; +} + +int pthread_barrier_wait(pthread_barrier_t *b_) +{ + long sel; + int r, e, rslt; + barrier_t *b; + + r = barrier_ref(b_); + if(r) return r; + + b = (barrier_t *)*b_; + + if ((r = pthread_mutex_lock(&b->m)) != 0) return barrier_unref(b_,EINVAL); + sel = b->sel; + InterlockedDecrement((long*)&b->total); + if (b->total == 0) + { + b->total = b->count; + b->sel = (sel != 0 ? 0 : 1); + e = 1; + rslt = PTHREAD_BARRIER_SERIAL_THREAD; + r = (b->count > 1 ? sem_post_multiple (&b->sems[sel], b->count - 1) : 0); + } + else { e = 0; rslt= 0; } + pthread_mutex_unlock(&b->m); + if (!e) + r = sem_wait(&b->sems[sel]); + + if (!r) r = rslt; + return barrier_unref(b_,r); +} + +int pthread_barrierattr_init(void **attr) +{ + int *p; + + if ((p = (int *) calloc (1, sizeof (int))) == NULL) + return ENOMEM; + + *p = PTHREAD_PROCESS_PRIVATE; + *attr = p; + + return 0; +} + +int pthread_barrierattr_destroy(void **attr) +{ + void *p; + if (!attr || (p = *attr) == NULL) + return EINVAL; + *attr = NULL; + free (p); + return 0; +} + +int pthread_barrierattr_setpshared(void **attr, int s) +{ + if (!attr || *attr == NULL + || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + memcpy (*attr, &s, sizeof (int)); + return 0; +} + +int pthread_barrierattr_getpshared(void **attr, int *s) +{ + if (!attr || !s || *attr == NULL) + return EINVAL; + memcpy (s, *attr, sizeof (int)); + return 0; +} diff --git a/src/barrier.h b/src/barrier.h new file mode 100644 index 0000000..5509678 --- /dev/null +++ b/src/barrier.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREADS_BARRIER_H +#define WIN_PTHREADS_BARRIER_H + +#define LIFE_BARRIER 0xBAB1FEED +#define DEAD_BARRIER 0xDEADB00F + +#define _PTHREAD_BARRIER_FLAG (1<<30) + +#define CHECK_BARRIER(b) \ + do { \ + if (!(b) || ( ((barrier_t *)(*b))->valid != (unsigned int)LIFE_BARRIER ) ) \ + return EINVAL; \ + } while (0) + +#include "semaphore.h" + +typedef struct barrier_t barrier_t; +struct barrier_t +{ + int valid; + int busy; + int count; + int total; + int share; + long sel; + pthread_mutex_t m; + sem_t sems[2]; +}; + +#endif diff --git a/src/clock.c b/src/clock.c new file mode 100644 index 0000000..954d845 --- /dev/null +++ b/src/clock.c @@ -0,0 +1,257 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include +#include +#include +#include +#ifndef IN_WINPTHREAD +#define IN_WINPTHREAD 1 +#endif +#include "pthread.h" +#include "pthread_time.h" +#include "misc.h" + +#define POW10_7 10000000 +#define POW10_9 1000000000 + +/* Number of 100ns-seconds between the beginning of the Windows epoch + * (Jan. 1, 1601) and the Unix epoch (Jan. 1, 1970) + */ +#define DELTA_EPOCH_IN_100NS INT64_C(116444736000000000) + +static WINPTHREADS_INLINE int lc_set_errno(int result) +{ + if (result != 0) { + errno = result; + return -1; + } + return 0; +} + +/** + * Get the resolution of the specified clock clock_id and + * stores it in the struct timespec pointed to by res. + * @param clock_id The clock_id argument is the identifier of the particular + * clock on which to act. The following clocks are supported: + *
+ *     CLOCK_REALTIME  System-wide real-time clock. Setting this clock
+ *                 requires appropriate privileges.
+ *     CLOCK_MONOTONIC Clock that cannot be set and represents monotonic
+ *                 time since some unspecified starting point.
+ *     CLOCK_PROCESS_CPUTIME_ID High-resolution per-process timer from the CPU.
+ *     CLOCK_THREAD_CPUTIME_ID  Thread-specific CPU-time clock.
+ * 
+ * @param res The pointer to a timespec structure to receive the time + * resolution. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +int clock_getres(clockid_t clock_id, struct timespec *res) +{ + clockid_t id = clock_id; + + if (id == CLOCK_REALTIME && _pthread_get_system_time_best_as_file_time == GetSystemTimeAsFileTime) + id = CLOCK_REALTIME_COARSE; /* GetSystemTimePreciseAsFileTime() not available */ + + switch(id) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + { + LARGE_INTEGER pf; + + if (QueryPerformanceFrequency(&pf) == 0) + return lc_set_errno(EINVAL); + + res->tv_sec = 0; + res->tv_nsec = (int) ((POW10_9 + (pf.QuadPart >> 1)) / pf.QuadPart); + if (res->tv_nsec < 1) + res->tv_nsec = 1; + + return 0; + } + + case CLOCK_REALTIME_COARSE: + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: + { + DWORD timeAdjustment, timeIncrement; + BOOL isTimeAdjustmentDisabled; + + (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled); + res->tv_sec = 0; + res->tv_nsec = timeIncrement * 100; + + return 0; + } + default: + break; + } + + return lc_set_errno(EINVAL); +} + +/** + * Get the time of the specified clock clock_id and stores it in the struct + * timespec pointed to by tp. + * @param clock_id The clock_id argument is the identifier of the particular + * clock on which to act. The following clocks are supported: + *
+ *     CLOCK_REALTIME  System-wide real-time clock. Setting this clock
+ *                 requires appropriate privileges.
+ *     CLOCK_MONOTONIC Clock that cannot be set and represents monotonic
+ *                 time since some unspecified starting point.
+ *     CLOCK_PROCESS_CPUTIME_ID High-resolution per-process timer from the CPU.
+ *     CLOCK_THREAD_CPUTIME_ID  Thread-specific CPU-time clock.
+ * 
+ * @param tp The pointer to a timespec structure to receive the time. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +int clock_gettime(clockid_t clock_id, struct timespec *tp) +{ + unsigned __int64 t; + LARGE_INTEGER pf, pc; + union { + unsigned __int64 u64; + FILETIME ft; + } ct, et, kt, ut; + + switch(clock_id) { + case CLOCK_REALTIME: + { + _pthread_get_system_time_best_as_file_time(&ct.ft); + t = ct.u64 - DELTA_EPOCH_IN_100NS; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; + } + + case CLOCK_REALTIME_COARSE: + { + GetSystemTimeAsFileTime(&ct.ft); + t = ct.u64 - DELTA_EPOCH_IN_100NS; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; + } + + case CLOCK_MONOTONIC: + { + if (QueryPerformanceFrequency(&pf) == 0) + return lc_set_errno(EINVAL); + + if (QueryPerformanceCounter(&pc) == 0) + return lc_set_errno(EINVAL); + + tp->tv_sec = pc.QuadPart / pf.QuadPart; + tp->tv_nsec = (int) (((pc.QuadPart % pf.QuadPart) * POW10_9 + (pf.QuadPart >> 1)) / pf.QuadPart); + if (tp->tv_nsec >= POW10_9) { + tp->tv_sec ++; + tp->tv_nsec -= POW10_9; + } + + return 0; + } + + case CLOCK_PROCESS_CPUTIME_ID: + { + if(0 == GetProcessTimes(GetCurrentProcess(), &ct.ft, &et.ft, &kt.ft, &ut.ft)) + return lc_set_errno(EINVAL); + t = kt.u64 + ut.u64; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; + } + + case CLOCK_THREAD_CPUTIME_ID: + { + if(0 == GetThreadTimes(GetCurrentThread(), &ct.ft, &et.ft, &kt.ft, &ut.ft)) + return lc_set_errno(EINVAL); + t = kt.u64 + ut.u64; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; + } + + default: + break; + } + + return lc_set_errno(EINVAL); +} + +/** + * Sleep for the specified time. + * @param clock_id This argument should always be CLOCK_REALTIME (0). + * @param flags 0 for relative sleep interval, others for absolute waking up. + * @param request The desired sleep interval or absolute waking up time. + * @param remain The remain amount of time to sleep. + * The current implemention just ignore it. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain) +{ + struct timespec tp; + + if (clock_id != CLOCK_REALTIME) + return lc_set_errno(EINVAL); + + if (flags == 0) + return nanosleep(request, remain); + + /* TIMER_ABSTIME = 1 */ + clock_gettime(CLOCK_REALTIME, &tp); + + tp.tv_sec = request->tv_sec - tp.tv_sec; + tp.tv_nsec = request->tv_nsec - tp.tv_nsec; + if (tp.tv_nsec < 0) { + tp.tv_nsec += POW10_9; + tp.tv_sec --; + } + + return nanosleep(&tp, remain); +} + +/** + * Set the time of the specified clock clock_id. + * @param clock_id This argument should always be CLOCK_REALTIME (0). + * @param tp The requested time. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +int clock_settime(clockid_t clock_id, const struct timespec *tp) +{ + SYSTEMTIME st; + + union { + unsigned __int64 u64; + FILETIME ft; + } t; + + if (clock_id != CLOCK_REALTIME) + return lc_set_errno(EINVAL); + + t.u64 = tp->tv_sec * (__int64) POW10_7 + tp->tv_nsec / 100 + DELTA_EPOCH_IN_100NS; + if (FileTimeToSystemTime(&t.ft, &st) == 0) + return lc_set_errno(EINVAL); + + if (SetSystemTime(&st) == 0) + return lc_set_errno(EPERM); + + return 0; +} diff --git a/src/cond.c b/src/cond.c new file mode 100644 index 0000000..813648f --- /dev/null +++ b/src/cond.c @@ -0,0 +1,755 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +/* + * Posix Condition Variables for Microsoft Windows. + * 22-9-2010 Partly based on the ACE framework implementation. + */ +#include +#include +#include +#include +#include "pthread.h" +#include "pthread_time.h" +#include "ref.h" +#include "cond.h" +#include "thread.h" +#include "misc.h" +#include "winpthread_internal.h" + +#include "pthread_compat.h" + +int __pthread_shallcancel (void); + +static int do_sema_b_wait (HANDLE sema, int nointerrupt, DWORD timeout,CRITICAL_SECTION *cs, LONG *val); +static int do_sema_b_release(HANDLE sema, LONG count,CRITICAL_SECTION *cs, LONG *val); +static void cleanup_wait(void *arg); + +typedef struct sCondWaitHelper { + cond_t *c; + pthread_mutex_t *external_mutex; + int *r; +} sCondWaitHelper; + +int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout); + +#ifdef WINPTHREAD_DBG +static int print_state = 0; +static FILE *fo; +void cond_print_set(int state, FILE *f) +{ + if (f) fo = f; + if (!fo) fo = stdout; + print_state = state; +} + +void cond_print(volatile pthread_cond_t *c, char *txt) +{ + if (!print_state) return; + cond_t *c_ = (cond_t *)*c; + if (c_ == NULL) { + fprintf(fo,"C%p %lu %s\n",(void *)*c,GetCurrentThreadId(),txt); + } else { + fprintf(fo,"C%p %lu V=%0X w=%ld %s\n", + (void *)*c, + GetCurrentThreadId(), + (int)c_->valid, + c_->waiters_count_, + txt + ); + } +} +#endif + +static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER; + +static int +cond_static_init (pthread_cond_t *c) +{ + int r = 0; + + pthread_spin_lock (&cond_locked); + if (c == NULL) + r = EINVAL; + else if (*c == PTHREAD_COND_INITIALIZER) + r = pthread_cond_init (c, NULL); + else + /* We assume someone was faster ... */ + r = 0; + pthread_spin_unlock (&cond_locked); + return r; +} + +int +pthread_condattr_destroy (pthread_condattr_t *a) +{ + if (!a) + return EINVAL; + *a = 0; + return 0; +} + +int +pthread_condattr_init (pthread_condattr_t *a) +{ + if (!a) + return EINVAL; + *a = 0; + return 0; +} + +int +pthread_condattr_getpshared (const pthread_condattr_t *a, int *s) +{ + if (!a || !s) + return EINVAL; + *s = *a; + return 0; +} + +int +pthread_condattr_getclock (const pthread_condattr_t *a, clockid_t *clock_id) +{ + if (!a || !clock_id) + return EINVAL; + *clock_id = 0; + return 0; +} + +int +pthread_condattr_setclock(pthread_condattr_t *a, clockid_t clock_id) +{ + if (!a || clock_id != 0) + return EINVAL; + return 0; +} + +int +__pthread_clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *rqtp, + struct timespec *rmtp) +{ + unsigned long long tick, tick2; + unsigned long long delay; + DWORD dw; + + if (clock_id != CLOCK_REALTIME + && clock_id != CLOCK_MONOTONIC + && clock_id != CLOCK_PROCESS_CPUTIME_ID) + return EINVAL; + if ((flags & TIMER_ABSTIME) != 0) + delay = _pthread_rel_time_in_ms (rqtp); + else + delay = _pthread_time_in_ms_from_timespec (rqtp); + do + { + dw = (DWORD) (delay >= 99999ULL ? 99999ULL : delay); + tick = _pthread_time_in_ms (); + pthread_delay_np_ms (dw); + tick2 = _pthread_time_in_ms (); + tick2 -= tick; + if (tick2 >= delay) + delay = 0; + else + delay -= tick2; + } + while (delay != 0ULL); + if (rmtp) + memset (rmtp, 0, sizeof (*rmtp)); + return 0; +} + +int +pthread_condattr_setpshared (pthread_condattr_t *a, int s) +{ + if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + if (s == PTHREAD_PROCESS_SHARED) + { + *a = PTHREAD_PROCESS_PRIVATE; + return ENOSYS; + } + *a = s; + return 0; +} + +int +pthread_cond_init (pthread_cond_t *c, const pthread_condattr_t *a) +{ + cond_t *_c; + int r = 0; + + if (!c) + return EINVAL; + if (a && *a == PTHREAD_PROCESS_SHARED) + return ENOSYS; + + if ((_c = calloc(1, sizeof(*_c))) == NULL) + return ENOMEM; + + _c->valid = DEAD_COND; + _c->busy = 0; + _c->waiters_count_ = 0; + _c->waiters_count_gone_ = 0; + _c->waiters_count_unblock_ = 0; + + _c->sema_q = CreateSemaphore (NULL, /* no security */ + 0, /* initially 0 */ + 0x7fffffff, /* max count */ + NULL); /* unnamed */ + _c->sema_b = CreateSemaphore (NULL, /* no security */ + 0, /* initially 0 */ + 0x7fffffff, /* max count */ + NULL); + if (_c->sema_q == NULL || _c->sema_b == NULL) { + if (_c->sema_q != NULL) + CloseHandle (_c->sema_q); + if (_c->sema_b != NULL) + CloseHandle (_c->sema_b); + free (_c); + r = EAGAIN; + } else { + InitializeCriticalSection(&_c->waiters_count_lock_); + InitializeCriticalSection(&_c->waiters_b_lock_); + InitializeCriticalSection(&_c->waiters_q_lock_); + _c->value_q = 0; + _c->value_b = 1; + } + if (!r) + { + _c->valid = LIFE_COND; + *c = (pthread_cond_t)_c; + } + else + *c = (pthread_cond_t)NULL; + return r; +} + +int +pthread_cond_destroy (pthread_cond_t *c) +{ + cond_t *_c; + int r; + if (!c || !*c) + return EINVAL; + if (*c == PTHREAD_COND_INITIALIZER) + { + pthread_spin_lock (&cond_locked); + if (*c == PTHREAD_COND_INITIALIZER) + { + *c = (pthread_cond_t)NULL; + r = 0; + } + else + r = EBUSY; + pthread_spin_unlock (&cond_locked); + return r; + } + _c = (cond_t *) *c; + r = do_sema_b_wait(_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + if (!TryEnterCriticalSection (&_c->waiters_count_lock_)) + { + do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + return EBUSY; + } + if (_c->waiters_count_ > _c->waiters_count_gone_) + { + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (!r) r = EBUSY; + LeaveCriticalSection(&_c->waiters_count_lock_); + return r; + } + *c = (pthread_cond_t)NULL; + do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + + if (!CloseHandle (_c->sema_q) && !r) + r = EINVAL; + if (!CloseHandle (_c->sema_b) && !r) + r = EINVAL; + LeaveCriticalSection (&_c->waiters_count_lock_); + DeleteCriticalSection(&_c->waiters_count_lock_); + DeleteCriticalSection(&_c->waiters_b_lock_); + DeleteCriticalSection(&_c->waiters_q_lock_); + _c->valid = DEAD_COND; + free(_c); + return 0; +} + +int +pthread_cond_signal (pthread_cond_t *c) +{ + cond_t *_c; + int r; + + if (!c || !*c) + return EINVAL; + _c = (cond_t *)*c; + if (_c == (cond_t *)PTHREAD_COND_INITIALIZER) + return 0; + else if (_c->valid != (unsigned int)LIFE_COND) + return EINVAL; + + EnterCriticalSection (&_c->waiters_count_lock_); + /* If there aren't any waiters, then this is a no-op. */ + if (_c->waiters_count_unblock_ != 0) + { + if (_c->waiters_count_ == 0) + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return 0; + } + _c->waiters_count_ -= 1; + _c->waiters_count_unblock_ += 1; + } + else if (_c->waiters_count_ > _c->waiters_count_gone_) + { + r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return r; + } + if (_c->waiters_count_gone_ != 0) + { + _c->waiters_count_ -= _c->waiters_count_gone_; + _c->waiters_count_gone_ = 0; + } + _c->waiters_count_ -= 1; + _c->waiters_count_unblock_ = 1; + } + else + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return 0; + } + LeaveCriticalSection (&_c->waiters_count_lock_); + r = do_sema_b_release(_c->sema_q, 1,&_c->waiters_q_lock_,&_c->value_q); + /* pthread_testcancel(); */ + return r; +} + +int +pthread_cond_broadcast (pthread_cond_t *c) +{ + cond_t *_c; + int r; + int relCnt = 0; + + if (!c || !*c) + return EINVAL; + _c = (cond_t *)*c; + if (_c == (cond_t*)PTHREAD_COND_INITIALIZER) + return 0; + else if (_c->valid != (unsigned int)LIFE_COND) + return EINVAL; + + EnterCriticalSection (&_c->waiters_count_lock_); + /* If there aren't any waiters, then this is a no-op. */ + if (_c->waiters_count_unblock_ != 0) + { + if (_c->waiters_count_ == 0) + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return 0; + } + relCnt = _c->waiters_count_; + _c->waiters_count_ = 0; + _c->waiters_count_unblock_ += relCnt; + } + else if (_c->waiters_count_ > _c->waiters_count_gone_) + { + r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return r; + } + if (_c->waiters_count_gone_ != 0) + { + _c->waiters_count_ -= _c->waiters_count_gone_; + _c->waiters_count_gone_ = 0; + } + relCnt = _c->waiters_count_; + _c->waiters_count_ = 0; + _c->waiters_count_unblock_ = relCnt; + } + else + { + LeaveCriticalSection (&_c->waiters_count_lock_); + /* pthread_testcancel(); */ + return 0; + } + LeaveCriticalSection (&_c->waiters_count_lock_); + r = do_sema_b_release(_c->sema_q, relCnt,&_c->waiters_q_lock_,&_c->value_q); + /* pthread_testcancel(); */ + return r; +} + +int +pthread_cond_wait (pthread_cond_t *c, pthread_mutex_t *external_mutex) +{ + sCondWaitHelper ch; + cond_t *_c; + int r; + + /* pthread_testcancel(); */ + + if (!c || *c == (pthread_cond_t)NULL) + return EINVAL; + _c = (cond_t *)*c; + if (*c == PTHREAD_COND_INITIALIZER) + { + r = cond_static_init(c); + if (r != 0 && r != EBUSY) + return r; + _c = (cond_t *) *c; + } else if (_c->valid != (unsigned int)LIFE_COND) + return EINVAL; + +tryagain: + r = do_sema_b_wait (_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + + if (!TryEnterCriticalSection (&_c->waiters_count_lock_)) + { + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + sched_yield(); + goto tryagain; + } + + _c->waiters_count_++; + LeaveCriticalSection(&_c->waiters_count_lock_); + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + + ch.c = _c; + ch.r = &r; + ch.external_mutex = external_mutex; + + pthread_cleanup_push(cleanup_wait, (void *) &ch); + r = pthread_mutex_unlock(external_mutex); + if (!r) + r = do_sema_b_wait (_c->sema_q, 0, INFINITE,&_c->waiters_q_lock_,&_c->value_q); + + pthread_cleanup_pop(1); + return r; +} + +static int +pthread_cond_timedwait_impl (pthread_cond_t *c, pthread_mutex_t *external_mutex, const struct timespec *t, int rel) +{ + sCondWaitHelper ch; + DWORD dwr; + int r; + cond_t *_c; + + /* pthread_testcancel(); */ + + if (!c || !*c) + return EINVAL; + _c = (cond_t *)*c; + if (_c == (cond_t *)PTHREAD_COND_INITIALIZER) + { + r = cond_static_init(c); + if (r && r != EBUSY) + return r; + _c = (cond_t *) *c; + } else if ((_c)->valid != (unsigned int)LIFE_COND) + return EINVAL; + + if (rel == 0) + { + dwr = dwMilliSecs(_pthread_rel_time_in_ms(t)); + } + else + { + dwr = dwMilliSecs(_pthread_time_in_ms_from_timespec(t)); + } + +tryagain: + r = do_sema_b_wait (_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + + if (!TryEnterCriticalSection (&_c->waiters_count_lock_)) + { + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + sched_yield(); + goto tryagain; + } + + _c->waiters_count_++; + LeaveCriticalSection(&_c->waiters_count_lock_); + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + return r; + + ch.c = _c; + ch.r = &r; + ch.external_mutex = external_mutex; + { + pthread_cleanup_push(cleanup_wait, (void *) &ch); + + r = pthread_mutex_unlock(external_mutex); + if (!r) + r = do_sema_b_wait (_c->sema_q, 0, dwr,&_c->waiters_q_lock_,&_c->value_q); + + pthread_cleanup_pop(1); + } + return r; +} + +int +pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *t) +{ + return pthread_cond_timedwait_impl(c, m, t, 0); +} + +int +pthread_cond_timedwait_relative_np(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *t) +{ + return pthread_cond_timedwait_impl(c, m, t, 1); +} + +static void +cleanup_wait (void *arg) +{ + int n, r; + sCondWaitHelper *ch = (sCondWaitHelper *) arg; + cond_t *_c; + + _c = ch->c; + EnterCriticalSection (&_c->waiters_count_lock_); + n = _c->waiters_count_unblock_; + if (n != 0) + _c->waiters_count_unblock_ -= 1; + else if ((INT_MAX/2) - 1 == _c->waiters_count_gone_) + { + _c->waiters_count_gone_ += 1; + r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + LeaveCriticalSection(&_c->waiters_count_lock_); + ch->r[0] = r; + return; + } + _c->waiters_count_ -= _c->waiters_count_gone_; + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + LeaveCriticalSection(&_c->waiters_count_lock_); + ch->r[0] = r; + return; + } + _c->waiters_count_gone_ = 0; + } + else + _c->waiters_count_gone_ += 1; + LeaveCriticalSection (&_c->waiters_count_lock_); + + if (n == 1) + { + r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b); + if (r != 0) + { + ch->r[0] = r; + return; + } + } + r = pthread_mutex_lock(ch->external_mutex); + if (r != 0) + ch->r[0] = r; +} + +static int +do_sema_b_wait (HANDLE sema, int nointerrupt, DWORD timeout,CRITICAL_SECTION *cs, LONG *val) +{ + int r; + LONG v; + EnterCriticalSection(cs); + InterlockedDecrement(val); + v = val[0]; + LeaveCriticalSection(cs); + if (v >= 0) + return 0; + r = do_sema_b_wait_intern (sema, nointerrupt, timeout); + EnterCriticalSection(cs); + if (r != 0) + InterlockedIncrement(val); + LeaveCriticalSection(cs); + return r; +} + +int +do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout) +{ + HANDLE arr[2]; + DWORD maxH = 1; + int r = 0; + DWORD res, dt; + if (nointerrupt == 1) + { + res = _pthread_wait_for_single_object(sema, timeout); + switch (res) { + case WAIT_TIMEOUT: + r = ETIMEDOUT; + break; + case WAIT_ABANDONED: + r = EPERM; + break; + case WAIT_OBJECT_0: + break; + default: + /*We can only return EINVAL though it might not be posix compliant */ + r = EINVAL; + } + if (r != 0 && r != EINVAL && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0) + r = 0; + return r; + } + arr[0] = sema; + arr[1] = (HANDLE) pthread_getevent (); + if (arr[1] != NULL) maxH += 1; + if (maxH == 2) + { +redo: + res = _pthread_wait_for_multiple_objects(maxH, arr, 0, timeout); + switch (res) { + case WAIT_TIMEOUT: + r = ETIMEDOUT; + break; + case (WAIT_OBJECT_0 + 1): + ResetEvent(arr[1]); + if (nointerrupt != 2) + { + pthread_testcancel(); + return EINVAL; + } + pthread_testcancel (); + goto redo; + case WAIT_ABANDONED: + r = EPERM; + break; + case WAIT_OBJECT_0: + r = 0; + break; + default: + /*We can only return EINVAL though it might not be posix compliant */ + r = EINVAL; + } + if (r != 0 && r != EINVAL && WaitForSingleObject(arr[0], 0) == WAIT_OBJECT_0) + r = 0; + if (r != 0 && nointerrupt != 2 && __pthread_shallcancel ()) + return EINVAL; + return r; + } + if (timeout == INFINITE) + { + do { + res = _pthread_wait_for_single_object(sema, 40); + switch (res) { + case WAIT_TIMEOUT: + r = ETIMEDOUT; + break; + case WAIT_ABANDONED: + r = EPERM; + break; + case WAIT_OBJECT_0: + r = 0; + break; + default: + /*We can only return EINVAL though it might not be posix compliant */ + r = EINVAL; + } + if (r != 0 && __pthread_shallcancel ()) + { + if (nointerrupt != 2) + pthread_testcancel(); + return EINVAL; + } + } while (r == ETIMEDOUT); + if (r != 0 && r != EINVAL && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0) + r = 0; + return r; + } + dt = 20; + do { + if (dt > timeout) dt = timeout; + res = _pthread_wait_for_single_object(sema, dt); + switch (res) { + case WAIT_TIMEOUT: + r = ETIMEDOUT; + break; + case WAIT_ABANDONED: + r = EPERM; + break; + case WAIT_OBJECT_0: + r = 0; + break; + default: + /*We can only return EINVAL though it might not be posix compliant */ + r = EINVAL; + } + timeout -= dt; + if (timeout != 0 && r != 0 && __pthread_shallcancel ()) + return EINVAL; + } while (r == ETIMEDOUT && timeout != 0); + if (r != 0 && r == ETIMEDOUT && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0) + r = 0; + if (r != 0 && nointerrupt != 2) + pthread_testcancel(); + return r; +} + +static int +do_sema_b_release(HANDLE sema, LONG count,CRITICAL_SECTION *cs, LONG *val) +{ + int wc; + EnterCriticalSection(cs); + if (((long long) val[0] + (long long) count) > (long long) 0x7fffffffLL) + { + LeaveCriticalSection(cs); + return ERANGE; + } + wc = -val[0]; + InterlockedExchangeAdd(val, count); + if (wc <= 0 || ReleaseSemaphore(sema, (wc < count ? wc : count), NULL)) + { + LeaveCriticalSection(cs); + return 0; + } + InterlockedExchangeAdd(val, -count); + LeaveCriticalSection(cs); + return EINVAL; +} diff --git a/src/cond.h b/src/cond.h new file mode 100644 index 0000000..5e869e9 --- /dev/null +++ b/src/cond.h @@ -0,0 +1,63 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREADS_COND_H +#define WIN_PTHREADS_COND_H + +#include + +#define CHECK_COND(c) \ + do { \ + if (!(c) || !*c || (*c == PTHREAD_COND_INITIALIZER) \ + || ( ((cond_t *)(*c))->valid != (unsigned int)LIFE_COND ) ) \ + return EINVAL; \ + } while (0) + +#define LIFE_COND 0xC0BAB1FD +#define DEAD_COND 0xC0DEADBF + +#define STATIC_COND_INITIALIZER(x) ((pthread_cond_t)(x) == ((pthread_cond_t)PTHREAD_COND_INITIALIZER)) + +typedef struct cond_t cond_t; +struct cond_t +{ + unsigned int valid; + int busy; + LONG waiters_count_; /* Number of waiting threads. */ + LONG waiters_count_unblock_; /* Number of waiting threads whitch can be unblocked. */ + LONG waiters_count_gone_; /* Number of waiters which are gone. */ + CRITICAL_SECTION waiters_count_lock_; /* Serialize access to . */ + CRITICAL_SECTION waiters_q_lock_; /* Serialize access to sema_q. */ + LONG value_q; + CRITICAL_SECTION waiters_b_lock_; /* Serialize access to sema_b. */ + LONG value_b; + HANDLE sema_q; /* Semaphore used to queue up threads waiting for the condition to + become signaled. */ + HANDLE sema_b; /* Semaphore used to queue up threads waiting for the condition which + became signaled. */ +}; + +void cond_print_set(int state, FILE *f); + +void cond_print(volatile pthread_cond_t *c, char *txt); + +#endif diff --git a/src/libgcc/dll_dependency.S b/src/libgcc/dll_dependency.S new file mode 100644 index 0000000..0496e94 --- /dev/null +++ b/src/libgcc/dll_dependency.S @@ -0,0 +1,90 @@ +/* Implementation for gcc's internal stack-allocation routines. */ +#if defined(__i386__) || defined(__x86_64__) +.global ___chkstk +.global __alloca + +.global ___chkstk_ms +___chkstk_ms: +#ifdef _WIN64 + pushq %rax + pushq %rcx + cmpq $0x1000, %rax + leaq 24(%rsp), %rcx + jb .Lchkstk_ms_end +.Lchkstk_ms_loop: + subq $0x1000, %rcx + subq $0x1000, %rax + orq $0x0, (%rcx) + cmpq $0x1000, %rax + ja .Lchkstk_ms_loop +.Lchkstk_ms_end: + subq %rax, %rcx + orq $0x0, (%rcx) + popq %rcx + popq %rax + ret +#else + pushl %eax + pushl %ecx + cmpl $0x1000, %eax + leal 12(%esp), %ecx + jb chkstk_ms_end +chkstk_ms_loop: + subl $0x1000, %ecx + subl $0x1000, %eax + orl $0x0, (%ecx) + cmpl $0x1000, %eax + ja chkstk_ms_loop +chkstk_ms_end: + subl %eax, %ecx + orl $0x0, (%ecx) + popl %ecx + popl %eax + ret +#endif + +#ifdef _WIN64 +__alloca: + movq %rcx, %rax +.align 4 +___chkstk: + popq %r11 + movq %rsp, %r10 + cmpq $0x1000, %rax + jb .Lchkstk_end +.Lchkstk_loop: + subq $0x1000, %r10 + subq $0x1000, %rax + orl $0x0, (%r10) + cmpq $0x1000, %rax + ja .Lchkstk_loop +.Lchkstk_end: + subq %rax, %r10 + movq %rsp, %rax + orl $0x0, (%r10) + movq %r10, %rsp + pushq %r11 + ret +#else +___chkstk: +__alloca: + pushl %ecx + leal 8(%esp), %ecx + cmpl $0x1000, %eax /* > 4k ?*/ + jb chkstk_end +chkstk_loop: + subl $0x1000, %ecx + subl $0x1000, %eax + orl $0x0, (%ecx) + cmpl $0x1000, %eax + ja chkstk_loop +chkstk_end: + subl %eax, %ecx + orl $0x0, (%ecx) + movl %esp, %eax + movl %ecx, %esp + movl (%eax), %ecx + pushl 4(%eax) + ret +#endif +#endif diff --git a/src/libgcc/dll_math.c b/src/libgcc/dll_math.c new file mode 100644 index 0000000..77bb1fe --- /dev/null +++ b/src/libgcc/dll_math.c @@ -0,0 +1,586 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBKERN_QUAD_H_ +#define _LIBKERN_QUAD_H_ + +/* + * Quad arithmetic. + * + * This library makes the following assumptions: + * + * - The type long long (aka quad_t) exists. + * + * - A quad variable is exactly twice as long as `long'. + * + * - The machine's arithmetic is two's complement. + * + * This library can provide 128-bit arithmetic on a machine with 128-bit + * quads and 64-bit longs, for instance, or 96-bit arithmetic on machines + * with 48-bit longs. + */ +/* +#include +#include +#include +#include +*/ + +#include +typedef long long quad_t; +typedef unsigned long long u_quad_t; +typedef unsigned long u_long; +#define CHAR_BIT __CHAR_BIT__ + +/* + * Define the order of 32-bit words in 64-bit words. + * For little endian only. + */ +#define _QUAD_HIGHWORD 1 +#define _QUAD_LOWWORD 0 + +/* + * Depending on the desired operation, we view a `long long' (aka quad_t) in + * one or more of the following formats. + */ +union uu { + quad_t q; /* as a (signed) quad */ + quad_t uq; /* as an unsigned quad */ + long sl[2]; /* as two signed longs */ + u_long ul[2]; /* as two unsigned longs */ +}; + +/* + * Define high and low longwords. + */ +#define H _QUAD_HIGHWORD +#define L _QUAD_LOWWORD + +/* + * Total number of bits in a quad_t and in the pieces that make it up. + * These are used for shifting, and also below for halfword extraction + * and assembly. + */ +#define QUAD_BITS (sizeof(quad_t) * CHAR_BIT) +#define LONG_BITS (sizeof(long) * CHAR_BIT) +#define HALF_BITS (sizeof(long) * CHAR_BIT / 2) + +/* + * Extract high and low shortwords from longword, and move low shortword of + * longword to upper half of long, i.e., produce the upper longword of + * ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.) + * + * These are used in the multiply code, to split a longword into upper + * and lower halves, and to reassemble a product as a quad_t, shifted left + * (sizeof(long)*CHAR_BIT/2). + */ +#define HHALF(x) ((x) >> HALF_BITS) +#define LHALF(x) ((x) & ((1 << HALF_BITS) - 1)) +#define LHUP(x) ((x) << HALF_BITS) + +typedef unsigned int qshift_t; + +quad_t __ashldi3(quad_t, qshift_t); +quad_t __ashrdi3(quad_t, qshift_t); +int __cmpdi2(quad_t a, quad_t b); +quad_t __divdi3(quad_t a, quad_t b); +quad_t __lshrdi3(quad_t, qshift_t); +quad_t __moddi3(quad_t a, quad_t b); +u_quad_t __qdivrem(u_quad_t u, u_quad_t v, u_quad_t *rem); +u_quad_t __udivdi3(u_quad_t a, u_quad_t b); +u_quad_t __umoddi3(u_quad_t a, u_quad_t b); +int __ucmpdi2(u_quad_t a, u_quad_t b); +quad_t __divmoddi4(quad_t a, quad_t b, quad_t *rem); +u_quad_t __udivmoddi4(u_quad_t a, u_quad_t b, u_quad_t *rem); + +#endif /* !_LIBKERN_QUAD_H_ */ + +#if defined (_X86_) && !defined (__x86_64__) +/* + * Shift a (signed) quad value left (arithmetic shift left). + * This is the same as logical shift left! + */ +quad_t +__ashldi3(a, shift) + quad_t a; + qshift_t shift; +{ + union uu aa; + + aa.q = a; + if (shift >= LONG_BITS) { + aa.ul[H] = shift >= QUAD_BITS ? 0 : + aa.ul[L] << (shift - LONG_BITS); + aa.ul[L] = 0; + } else if (shift > 0) { + aa.ul[H] = (aa.ul[H] << shift) | + (aa.ul[L] >> (LONG_BITS - shift)); + aa.ul[L] <<= shift; + } + return (aa.q); +} + +/* + * Shift a (signed) quad value right (arithmetic shift right). + */ +quad_t +__ashrdi3(a, shift) + quad_t a; + qshift_t shift; +{ + union uu aa; + + aa.q = a; + if (shift >= LONG_BITS) { + long s; + + /* + * Smear bits rightward using the machine's right-shift + * method, whether that is sign extension or zero fill, + * to get the `sign word' s. Note that shifting by + * LONG_BITS is undefined, so we shift (LONG_BITS-1), + * then 1 more, to get our answer. + */ + s = (aa.sl[H] >> (LONG_BITS - 1)) >> 1; + aa.ul[L] = shift >= QUAD_BITS ? s : + aa.sl[H] >> (shift - LONG_BITS); + aa.ul[H] = s; + } else if (shift > 0) { + aa.ul[L] = (aa.ul[L] >> shift) | + (aa.ul[H] << (LONG_BITS - shift)); + aa.sl[H] >>= shift; + } + return (aa.q); +} + +/* + * Return 0, 1, or 2 as a <, =, > b respectively. + * Both a and b are considered signed---which means only the high word is + * signed. + */ +int +__cmpdi2(a, b) + quad_t a, b; +{ + union uu aa, bb; + + aa.q = a; + bb.q = b; + return (aa.sl[H] < bb.sl[H] ? 0 : aa.sl[H] > bb.sl[H] ? 2 : + aa.ul[L] < bb.ul[L] ? 0 : aa.ul[L] > bb.ul[L] ? 2 : 1); +} + +/* + * Divide two signed quads. + * ??? if -1/2 should produce -1 on this machine, this code is wrong + */ +quad_t +__divdi3(a, b) + quad_t a, b; +{ + u_quad_t ua, ub, uq; + int neg; + + if (a < 0) + ua = -(u_quad_t)a, neg = 1; + else + ua = a, neg = 0; + if (b < 0) + ub = -(u_quad_t)b, neg ^= 1; + else + ub = b; + uq = __qdivrem(ua, ub, (u_quad_t *)0); + return (neg ? -uq : uq); +} + +/* + * Shift an (unsigned) quad value right (logical shift right). + */ +quad_t +__lshrdi3(a, shift) + quad_t a; + qshift_t shift; +{ + union uu aa; + + aa.q = a; + if (shift >= LONG_BITS) { + aa.ul[L] = shift >= QUAD_BITS ? 0 : + aa.ul[H] >> (shift - LONG_BITS); + aa.ul[H] = 0; + } else if (shift > 0) { + aa.ul[L] = (aa.ul[L] >> shift) | + (aa.ul[H] << (LONG_BITS - shift)); + aa.ul[H] >>= shift; + } + return (aa.q); +} + +/* + * Return remainder after dividing two signed quads. + * + * XXX + * If -1/2 should produce -1 on this machine, this code is wrong. + */ +quad_t +__moddi3(a, b) + quad_t a, b; +{ + u_quad_t ua, ub, ur; + int neg; + + if (a < 0) + ua = -(u_quad_t)a, neg = 1; + else + ua = a, neg = 0; + if (b < 0) + ub = -(u_quad_t)b; + else + ub = b; + (void)__qdivrem(ua, ub, &ur); + return (neg ? -ur : ur); +} + + +/* + * Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed), + * section 4.3.1, pp. 257--259. + */ + +#define B (1 << HALF_BITS) /* digit base */ + +/* Combine two `digits' to make a single two-digit number. */ +#define COMBINE(a, b) (((u_long)(a) << HALF_BITS) | (b)) + +/* select a type for digits in base B: use unsigned short if they fit */ +#if ULONG_MAX == 0xffffffff && USHRT_MAX >= 0xffff +typedef unsigned short digit; +#else +typedef u_long digit; +#endif + +/* + * Shift p[0]..p[len] left `sh' bits, ignoring any bits that + * `fall out' the left (there never will be any such anyway). + * We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS. + */ +static void +__shl(register digit *p, register int len, register int sh) +{ + register int i; + + for (i = 0; i < len; i++) + p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh)); + p[i] = LHALF(p[i] << sh); +} + +/* + * __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v. + * + * We do this in base 2-sup-HALF_BITS, so that all intermediate products + * fit within u_long. As a consequence, the maximum length dividend and + * divisor are 4 `digits' in this base (they are shorter if they have + * leading zeros). + */ +u_quad_t +__qdivrem(uq, vq, arq) + u_quad_t uq, vq, *arq; +{ + union uu tmp; + digit *u, *v, *q; + register digit v1, v2; + u_long qhat, rhat, t; + int m, n, d, j, i; + digit uspace[5], vspace[5], qspace[5]; + + /* + * Take care of special cases: divide by zero, and u < v. + */ + if (vq == 0) { + /* divide by zero. */ + static volatile const unsigned int zero = 0; + + tmp.ul[H] = tmp.ul[L] = 1 / zero; + if (arq) + *arq = uq; + return (tmp.q); + } + if (uq < vq) { + if (arq) + *arq = uq; + return (0); + } + u = &uspace[0]; + v = &vspace[0]; + q = &qspace[0]; + + /* + * Break dividend and divisor into digits in base B, then + * count leading zeros to determine m and n. When done, we + * will have: + * u = (u[1]u[2]...u[m+n]) sub B + * v = (v[1]v[2]...v[n]) sub B + * v[1] != 0 + * 1 < n <= 4 (if n = 1, we use a different division algorithm) + * m >= 0 (otherwise u < v, which we already checked) + * m + n = 4 + * and thus + * m = 4 - n <= 2 + */ + tmp.uq = uq; + u[0] = 0; + u[1] = HHALF(tmp.ul[H]); + u[2] = LHALF(tmp.ul[H]); + u[3] = HHALF(tmp.ul[L]); + u[4] = LHALF(tmp.ul[L]); + tmp.uq = vq; + v[1] = HHALF(tmp.ul[H]); + v[2] = LHALF(tmp.ul[H]); + v[3] = HHALF(tmp.ul[L]); + v[4] = LHALF(tmp.ul[L]); + for (n = 4; v[1] == 0; v++) { + if (--n == 1) { + u_long rbj; /* r*B+u[j] (not root boy jim) */ + digit q1, q2, q3, q4; + + /* + * Change of plan, per exercise 16. + * r = 0; + * for j = 1..4: + * q[j] = floor((r*B + u[j]) / v), + * r = (r*B + u[j]) % v; + * We unroll this completely here. + */ + t = v[2]; /* nonzero, by definition */ + q1 = u[1] / t; + rbj = COMBINE(u[1] % t, u[2]); + q2 = rbj / t; + rbj = COMBINE(rbj % t, u[3]); + q3 = rbj / t; + rbj = COMBINE(rbj % t, u[4]); + q4 = rbj / t; + if (arq) + *arq = rbj % t; + tmp.ul[H] = COMBINE(q1, q2); + tmp.ul[L] = COMBINE(q3, q4); + return (tmp.q); + } + } + + /* + * By adjusting q once we determine m, we can guarantee that + * there is a complete four-digit quotient at &qspace[1] when + * we finally stop. + */ + for (m = 4 - n; u[1] == 0; u++) + m--; + for (i = 4 - m; --i >= 0;) + q[i] = 0; + q += 4 - m; + + /* + * Here we run Program D, translated from MIX to C and acquiring + * a few minor changes. + * + * D1: choose multiplier 1 << d to ensure v[1] >= B/2. + */ + d = 0; + for (t = v[1]; t < B / 2; t <<= 1) + d++; + if (d > 0) { + __shl(&u[0], m + n, d); /* u <<= d */ + __shl(&v[1], n - 1, d); /* v <<= d */ + } + /* + * D2: j = 0. + */ + j = 0; + v1 = v[1]; /* for D3 -- note that v[1..n] are constant */ + v2 = v[2]; /* for D3 */ + do { + register digit uj0, uj1, uj2; + + /* + * D3: Calculate qhat (\^q, in TeX notation). + * Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and + * let rhat = (u[j]*B + u[j+1]) mod v[1]. + * While rhat < B and v[2]*qhat > rhat*B+u[j+2], + * decrement qhat and increase rhat correspondingly. + * Note that if rhat >= B, v[2]*qhat < rhat*B. + */ + uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */ + uj1 = u[j + 1]; /* for D3 only */ + uj2 = u[j + 2]; /* for D3 only */ + if (uj0 == v1) { + qhat = B; + rhat = uj1; + goto qhat_too_big; + } else { + u_long nn = COMBINE(uj0, uj1); + qhat = nn / v1; + rhat = nn % v1; + } + while (v2 * qhat > COMBINE(rhat, uj2)) { + qhat_too_big: + qhat--; + if ((rhat += v1) >= B) + break; + } + /* + * D4: Multiply and subtract. + * The variable `t' holds any borrows across the loop. + * We split this up so that we do not require v[0] = 0, + * and to eliminate a final special case. + */ + for (t = 0, i = n; i > 0; i--) { + t = u[i + j] - v[i] * qhat - t; + u[i + j] = LHALF(t); + t = (B - HHALF(t)) & (B - 1); + } + t = u[j] - t; + u[j] = LHALF(t); + /* + * D5: test remainder. + * There is a borrow if and only if HHALF(t) is nonzero; + * in that (rare) case, qhat was too large (by exactly 1). + * Fix it by adding v[1..n] to u[j..j+n]. + */ + if (HHALF(t)) { + qhat--; + for (t = 0, i = n; i > 0; i--) { /* D6: add back. */ + t += u[i + j] + v[i]; + u[i + j] = LHALF(t); + t = HHALF(t); + } + u[j] = LHALF(u[j] + t); + } + q[j] = qhat; + } while (++j <= m); /* D7: loop on j. */ + + /* + * If caller wants the remainder, we have to calculate it as + * u[m..m+n] >> d (this is at most n digits and thus fits in + * u[m+1..m+n], but we may need more source digits). + */ + if (arq) { + if (d) { + for (i = m + n; i > m; --i) + u[i] = (u[i] >> d) | + LHALF(u[i - 1] << (HALF_BITS - d)); + u[i] = 0; + } + tmp.ul[H] = COMBINE(uspace[1], uspace[2]); + tmp.ul[L] = COMBINE(uspace[3], uspace[4]); + *arq = tmp.q; + } + + tmp.ul[H] = COMBINE(qspace[1], qspace[2]); + tmp.ul[L] = COMBINE(qspace[3], qspace[4]); + return (tmp.q); +} + +/* + * Return 0, 1, or 2 as a <, =, > b respectively. + * Neither a nor b are considered signed. + */ +int +__ucmpdi2(a, b) + u_quad_t a, b; +{ + union uu aa, bb; + + aa.uq = a; + bb.uq = b; + return (aa.ul[H] < bb.ul[H] ? 0 : aa.ul[H] > bb.ul[H] ? 2 : + aa.ul[L] < bb.ul[L] ? 0 : aa.ul[L] > bb.ul[L] ? 2 : 1); +} + +/* + * Divide two unsigned quads. + */ +u_quad_t +__udivdi3(a, b) + u_quad_t a, b; +{ + + return (__qdivrem(a, b, (u_quad_t *)0)); +} + +/* + * Return remainder after dividing two unsigned quads. + */ +u_quad_t +__umoddi3(a, b) + u_quad_t a, b; +{ + u_quad_t r; + + (void)__qdivrem(a, b, &r); + return (r); +} + +/* + * Divide two signed quads. + * This function is new in GCC 7. + */ +quad_t +__divmoddi4(a, b, rem) + quad_t a, b, *rem; +{ + u_quad_t ua, ub, uq, ur; + int negq, negr; + + if (a < 0) + ua = -(u_quad_t)a, negq = 1, negr = 1; + else + ua = a, negq = 0, negr = 0; + if (b < 0) + ub = -(u_quad_t)b, negq ^= 1; + else + ub = b; + uq = __qdivrem(ua, ub, &ur); + if (rem) + *rem = (negr ? -ur : ur); + return (negq ? -uq : uq); +} + +u_quad_t +__udivmoddi4(u_quad_t a, u_quad_t b, u_quad_t *rem) +{ + return __qdivrem(a, b, rem); +} + +#else +static int __attribute__((unused)) dummy; +#endif /*deined (_X86_) && !defined (__x86_64__)*/ + diff --git a/src/misc.c b/src/misc.c new file mode 100644 index 0000000..457bc86 --- /dev/null +++ b/src/misc.c @@ -0,0 +1,197 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 +#include "pthread.h" +#include "misc.h" + +void (WINAPI *_pthread_get_system_time_best_as_file_time) (LPFILETIME) = NULL; +static ULONGLONG (WINAPI *_pthread_get_tick_count_64) (VOID); + +#if defined(__GNUC__) || defined(__clang__) +__attribute__((constructor)) +#endif +static void winpthreads_init(void) +{ + HMODULE mod = GetModuleHandleA("kernel32.dll"); + if (mod) + { + _pthread_get_tick_count_64 = + (ULONGLONG (WINAPI *)(VOID))(void*) GetProcAddress(mod, "GetTickCount64"); + + /* <1us precision on Windows 10 */ + _pthread_get_system_time_best_as_file_time = + (void (WINAPI *)(LPFILETIME))(void*) GetProcAddress(mod, "GetSystemTimePreciseAsFileTime"); + } + + if (!_pthread_get_system_time_best_as_file_time) + /* >15ms precision on Windows 10 */ + _pthread_get_system_time_best_as_file_time = GetSystemTimeAsFileTime; +} + +#if defined(_MSC_VER) && !defined(__clang__) +/* Force a reference to __xc_t to prevent whole program optimization + * from discarding the variable. */ + +/* On x86, symbols are prefixed with an underscore. */ +# if defined(_M_IX86) +# pragma comment(linker, "/include:___xc_t") +# else +# pragma comment(linker, "/include:__xc_t") +# endif + +#pragma section(".CRT$XCT", long, read) +__declspec(allocate(".CRT$XCT")) +extern const _PVFV __xc_t; +const _PVFV __xc_t = winpthreads_init; +#endif + +unsigned long long _pthread_time_in_ms(void) +{ + FILETIME ft; + + GetSystemTimeAsFileTime(&ft); + return (((unsigned long long)ft.dwHighDateTime << 32) + ft.dwLowDateTime + - 0x19DB1DED53E8000ULL) / 10000ULL; +} + +unsigned long long _pthread_time_in_ms_from_timespec(const struct timespec *ts) +{ + unsigned long long t = (unsigned long long) ts->tv_sec * 1000LL; + /* The +999999 is here to ensure that the division always rounds up */ + t += (unsigned long long) (ts->tv_nsec + 999999) / 1000000; + + return t; +} + +unsigned long long _pthread_rel_time_in_ms(const struct timespec *ts) +{ + unsigned long long t1 = _pthread_time_in_ms_from_timespec(ts); + unsigned long long t2 = _pthread_time_in_ms(); + + /* Prevent underflow */ + if (t1 < t2) return 0; + return t1 - t2; +} + +static unsigned long long +_pthread_get_tick_count (long long *frequency) +{ + if (_pthread_get_tick_count_64 != NULL) + return _pthread_get_tick_count_64 (); + + LARGE_INTEGER freq, timestamp; + + if (*frequency == 0) + { + if (QueryPerformanceFrequency (&freq)) + *frequency = freq.QuadPart; + else + *frequency = -1; + } + + if (*frequency > 0 && QueryPerformanceCounter (×tamp)) + return timestamp.QuadPart / (*frequency / 1000); + + /* Fallback */ + return GetTickCount (); +} + +/* A wrapper around WaitForSingleObject() that ensures that + * the wait function does not time out before the time + * actually runs out. This is needed because WaitForSingleObject() + * might have poor accuracy, returning earlier than expected. + * On the other hand, returning a bit *later* than expected + * is acceptable in a preemptive multitasking environment. + */ +unsigned long +_pthread_wait_for_single_object (void *handle, unsigned long timeout) +{ + DWORD result; + unsigned long long start_time, end_time; + unsigned long wait_time; + long long frequency = 0; + + if (timeout == INFINITE || timeout == 0) + return WaitForSingleObject ((HANDLE) handle, (DWORD) timeout); + + start_time = _pthread_get_tick_count (&frequency); + end_time = start_time + timeout; + wait_time = timeout; + + do + { + unsigned long long current_time; + + result = WaitForSingleObject ((HANDLE) handle, (DWORD) wait_time); + if (result != WAIT_TIMEOUT) + break; + + current_time = _pthread_get_tick_count (&frequency); + if (current_time >= end_time) + break; + + wait_time = (DWORD) (end_time - current_time); + } while (TRUE); + + return result; +} + +/* A wrapper around WaitForMultipleObjects() that ensures that + * the wait function does not time out before the time + * actually runs out. This is needed because WaitForMultipleObjects() + * might have poor accuracy, returning earlier than expected. + * On the other hand, returning a bit *later* than expected + * is acceptable in a preemptive multitasking environment. + */ +unsigned long +_pthread_wait_for_multiple_objects (unsigned long count, void **handles, unsigned int all, unsigned long timeout) +{ + DWORD result; + unsigned long long start_time, end_time; + unsigned long wait_time; + long long frequency = 0; + + if (timeout == INFINITE || timeout == 0) + return WaitForMultipleObjects ((DWORD) count, (HANDLE *) handles, all, (DWORD) timeout); + + start_time = _pthread_get_tick_count (&frequency); + end_time = start_time + timeout; + wait_time = timeout; + + do + { + unsigned long long current_time; + + result = WaitForMultipleObjects ((DWORD) count, (HANDLE *) handles, all, (DWORD) wait_time); + if (result != WAIT_TIMEOUT) + break; + + current_time = _pthread_get_tick_count (&frequency); + if (current_time >= end_time) + break; + + wait_time = (DWORD) (end_time - current_time); + } while (TRUE); + + return result; +} diff --git a/src/misc.h b/src/misc.h new file mode 100644 index 0000000..edefb0d --- /dev/null +++ b/src/misc.h @@ -0,0 +1,126 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREADS_MISC_H +#define WIN_PTHREADS_MISC_H + +#include "pthread_compat.h" + +#ifndef assert + +#ifndef ASSERT_TRACE +# define ASSERT_TRACE 0 +#else +# undef ASSERT_TRACE +# define ASSERT_TRACE 0 +#endif + +# define assert(e) \ + ((e) ? ((ASSERT_TRACE) ? fprintf(stderr, \ + "Assertion succeeded: (%s), file %s, line %d\n", \ + #e, __FILE__, (int) __LINE__), \ + fflush(stderr) : \ + 0) : \ + (fprintf(stderr, "Assertion failed: (%s), file %s, line %d\n", \ + #e, __FILE__, (int) __LINE__), exit(1), 0)) + +# define fixme(e) \ + ((e) ? ((ASSERT_TRACE) ? fprintf(stderr, \ + "Assertion succeeded: (%s), file %s, line %d\n", \ + #e, __FILE__, (int) __LINE__), \ + fflush(stderr) : \ + 0) : \ + (fprintf(stderr, "FIXME: (%s), file %s, line %d\n", \ + #e, __FILE__, (int) __LINE__), 0, 0)) + +#endif + +#define PTR2INT(x) ((int)(uintptr_t)(x)) + +#if SIZE_MAX>UINT_MAX +typedef long long LONGBAG; +#else +typedef long LONGBAG; +#endif + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#undef GetHandleInformation +#define GetHandleInformation(h,f) (1) +#endif + +#define CHECK_HANDLE(h) \ + do { \ + DWORD dwFlags; \ + if (!(h) || ((h) == INVALID_HANDLE_VALUE) || !GetHandleInformation((h), &dwFlags)) \ + return EINVAL; \ + } while (0) + +#define CHECK_PTR(p) do { if (!(p)) return EINVAL; } while (0) + +#define UPD_RESULT(x,r) do { int _r = (x); (r) = (r) ? (r) : _r; } while (0) + +#define CHECK_THREAD(t) \ + do { \ + CHECK_PTR(t); \ + CHECK_HANDLE((t)->h); \ + } while (0) + +#define CHECK_OBJECT(o, e) \ + do { \ + DWORD dwFlags; \ + if (!(o)) return e; \ + if (!((o)->h) || (((o)->h) == INVALID_HANDLE_VALUE) || !GetHandleInformation(((o)->h), &dwFlags)) \ + return e; \ + } while (0) + +#define VALID(x) if (!(p)) return EINVAL; + +/* ms can be 64 bit, solve wrap-around issues: */ +static WINPTHREADS_INLINE unsigned long dwMilliSecs(unsigned long long ms) +{ + if (ms >= 0xffffffffULL) return 0xfffffffful; + return (unsigned long) ms; +} + +unsigned long long _pthread_time_in_ms(void); +unsigned long long _pthread_time_in_ms_from_timespec(const struct timespec *ts); +unsigned long long _pthread_rel_time_in_ms(const struct timespec *ts); +unsigned long _pthread_wait_for_single_object (void *handle, unsigned long timeout); +unsigned long _pthread_wait_for_multiple_objects (unsigned long count, void **handles, unsigned int all, unsigned long timeout); + +extern void (WINAPI *_pthread_get_system_time_best_as_file_time) (LPFILETIME); + +#if defined(__GNUC__) || defined(__clang__) +#define likely(cond) __builtin_expect((cond) != 0, 1) +#define unlikely(cond) __builtin_expect((cond) != 0, 0) +#else +#define likely(cond) (cond) +#define unlikely(cond) (cond) +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define UNREACHABLE() __builtin_unreachable() +#elif defined(_MSC_VER) +#define UNREACHABLE() __assume(0) +#endif + +#endif diff --git a/src/mutex.c b/src/mutex.c new file mode 100644 index 0000000..866e18d --- /dev/null +++ b/src/mutex.c @@ -0,0 +1,381 @@ +/* + Copyright (c) 2011, 2014 mingw-w64 project + Copyright (c) 2015 Intel Corporation + + 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 +#include +#include +#include +#include "pthread.h" +#include "misc.h" + +typedef enum { + Unlocked, /* Not locked. */ + Locked, /* Locked but without waiters. */ + Waiting, /* Locked, may have waiters. */ +} mutex_state_t; + +typedef enum { + Normal, + Errorcheck, + Recursive, +} mutex_type_t; + +/* The heap-allocated part of a mutex. */ +typedef struct { + mutex_state_t state; + mutex_type_t type; + HANDLE event; /* Auto-reset event, or NULL if not yet allocated. */ + unsigned rec_lock; /* For recursive mutexes, the number of times the + mutex has been locked in excess by the same thread. */ + volatile DWORD owner; /* For recursive and error-checking mutexes, the + ID of the owning thread if the mutex is locked. */ +} mutex_impl_t; + +/* Whether a mutex is still a static initializer (not a pointer to + a mutex_impl_t). */ +static bool +is_static_initializer(pthread_mutex_t m) +{ + /* Treat 0 as a static initializer as well (for normal mutexes), + to tolerate sloppy code in libgomp. (We should rather fix that code!) */ + intptr_t v = (intptr_t)m; + return v >= -3 && v <= 0; +/* Should be simple: + return (uintptr_t)m >= (uintptr_t)-3; */ +} + +/* Create and return the implementation part of a mutex from a static + initialiser. Return NULL on out-of-memory error. */ +static WINPTHREADS_ATTRIBUTE((noinline)) mutex_impl_t * +mutex_impl_init(pthread_mutex_t *m, mutex_impl_t *mi) +{ + mutex_impl_t *new_mi = malloc(sizeof(mutex_impl_t)); + if (new_mi == NULL) + return NULL; + new_mi->state = Unlocked; + new_mi->type = (mi == (void *)PTHREAD_RECURSIVE_MUTEX_INITIALIZER ? Recursive + : mi == (void *)PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ? Errorcheck + : Normal); + new_mi->event = NULL; + new_mi->rec_lock = 0; + new_mi->owner = (DWORD)-1; + if (InterlockedCompareExchangePointer((PVOID volatile *)m, new_mi, mi) == mi) { + return new_mi; + } else { + /* Someone created the struct before us. */ + free(new_mi); + return (mutex_impl_t *)*m; + } +} + +/* Return the implementation part of a mutex, creating it if necessary. + Return NULL on out-of-memory error. */ +static inline mutex_impl_t * +mutex_impl(pthread_mutex_t *m) +{ + mutex_impl_t *mi = (mutex_impl_t *)*m; + if (is_static_initializer((pthread_mutex_t)mi)) { + return mutex_impl_init(m, mi); + } else { + /* mi cannot be null here; avoid a test in the fast path. */ + if (mi == NULL) + UNREACHABLE(); + return mi; + } +} + +/* Lock a mutex. Give up after 'timeout' ms (with ETIMEDOUT), + or never if timeout=INFINITE. */ +static inline int +pthread_mutex_lock_intern (pthread_mutex_t *m, DWORD timeout) +{ + mutex_impl_t *mi = mutex_impl(m); + if (mi == NULL) + return ENOMEM; + mutex_state_t old_state = InterlockedExchange((long *)&mi->state, Locked); + if (unlikely(old_state != Unlocked)) { + /* The mutex is already locked. */ + + if (mi->type != Normal) { + /* Recursive or Errorcheck */ + if (mi->owner == GetCurrentThreadId()) { + /* FIXME: A recursive mutex should not need two atomic ops when locking + recursively. We could rewrite by doing compare-and-swap instead of + test-and-set the first time, but it would lead to more code + duplication and add a conditional branch to the critical path. */ + InterlockedCompareExchange((long *)&mi->state, old_state, Locked); + if (mi->type == Recursive) { + mi->rec_lock++; + return 0; + } else { + /* type == Errorcheck */ + return EDEADLK; + } + } + } + + /* Make sure there is an event object on which to wait. */ + if (mi->event == NULL) { + /* Make an auto-reset event object. */ + HANDLE ev = CreateEvent(NULL, false, false, NULL); + if (ev == NULL) { + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + return EPERM; + default: + return ENOMEM; /* Probably accurate enough. */ + } + } + if (InterlockedCompareExchangePointer(&mi->event, ev, NULL) != NULL) { + /* Someone created the event before us. */ + CloseHandle(ev); + } + } + + /* At this point, mi->event is non-NULL. */ + + while (InterlockedExchange((long *)&mi->state, Waiting) != Unlocked) { + /* For timed locking attempts, it is possible (although unlikely) + that we are woken up but someone else grabs the lock before us, + and we have to go back to sleep again. In that case, the total + wait may be longer than expected. */ + + unsigned r = _pthread_wait_for_single_object(mi->event, timeout); + switch (r) { + case WAIT_TIMEOUT: + return ETIMEDOUT; + case WAIT_OBJECT_0: + break; + default: + return EINVAL; + } + } + } + + if (mi->type != Normal) + mi->owner = GetCurrentThreadId(); + + return 0; +} + +int +pthread_mutex_lock (pthread_mutex_t *m) +{ + return pthread_mutex_lock_intern (m, INFINITE); +} + +int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *ts) +{ + unsigned long long patience; + if (ts != NULL) { + unsigned long long end = _pthread_time_in_ms_from_timespec(ts); + unsigned long long now = _pthread_time_in_ms(); + patience = end > now ? end - now : 0; + if (patience > 0xffffffff) + patience = INFINITE; + } else { + patience = INFINITE; + } + return pthread_mutex_lock_intern(m, patience); +} + +int pthread_mutex_unlock(pthread_mutex_t *m) +{ + /* Here m might an initialiser of an error-checking or recursive mutex, in + which case the behaviour is well-defined, so we can't skip this check. */ + mutex_impl_t *mi = mutex_impl(m); + if (mi == NULL) + return ENOMEM; + + if (unlikely(mi->type != Normal)) { + if (mi->state == Unlocked) + return EINVAL; + if (mi->owner != GetCurrentThreadId()) + return EPERM; + if (mi->rec_lock > 0) { + mi->rec_lock--; + return 0; + } + mi->owner = (DWORD)-1; + } + if (unlikely(InterlockedExchange((long *)&mi->state, Unlocked) == Waiting)) { + if (!SetEvent(mi->event)) + return EPERM; + } + return 0; +} + +int pthread_mutex_trylock(pthread_mutex_t *m) +{ + mutex_impl_t *mi = mutex_impl(m); + if (mi == NULL) + return ENOMEM; + + if (InterlockedCompareExchange((long *)&mi->state, Locked, Unlocked) == Unlocked) { + if (mi->type != Normal) + mi->owner = GetCurrentThreadId(); + return 0; + } else { + if (mi->type == Recursive && mi->owner == GetCurrentThreadId()) { + mi->rec_lock++; + return 0; + } + return EBUSY; + } +} + +int +pthread_mutex_init (pthread_mutex_t *m, const pthread_mutexattr_t *a) +{ + pthread_mutex_t init = PTHREAD_MUTEX_INITIALIZER; + if (a != NULL) { + int pshared; + if (pthread_mutexattr_getpshared(a, &pshared) == 0 + && pshared == PTHREAD_PROCESS_SHARED) + return ENOSYS; + + int type; + if (pthread_mutexattr_gettype(a, &type) == 0) { + switch (type) { + case PTHREAD_MUTEX_ERRORCHECK: + init = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER; + break; + case PTHREAD_MUTEX_RECURSIVE: + init = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + break; + default: + init = PTHREAD_MUTEX_INITIALIZER; + break; + } + } + } + *m = init; + return 0; +} + +int pthread_mutex_destroy (pthread_mutex_t *m) +{ + mutex_impl_t *mi = (mutex_impl_t *)*m; + if (!is_static_initializer((pthread_mutex_t)mi)) { + if (mi->event != NULL) + CloseHandle(mi->event); + free(mi); + /* Sabotage attempts to re-use the mutex before initialising it again. */ + *m = (pthread_mutex_t)NULL; + } + + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t *a) +{ + *a = PTHREAD_MUTEX_NORMAL | (PTHREAD_PROCESS_PRIVATE << 3); + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *a) +{ + if (!a) + return EINVAL; + + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type) +{ + if (!a || !type) + return EINVAL; + + *type = *a & 3; + + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type) +{ + if (!a || (type != PTHREAD_MUTEX_NORMAL && type != PTHREAD_MUTEX_RECURSIVE && type != PTHREAD_MUTEX_ERRORCHECK)) + return EINVAL; + *a &= ~3; + *a |= type; + + return 0; +} + +int pthread_mutexattr_getpshared(const pthread_mutexattr_t *a, int *type) +{ + if (!a || !type) + return EINVAL; + *type = (*a & 4 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE); + + return 0; +} + +int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type) +{ + int r = 0; + if (!a || (type != PTHREAD_PROCESS_SHARED + && type != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + if (type == PTHREAD_PROCESS_SHARED) + { + type = PTHREAD_PROCESS_PRIVATE; + r = ENOSYS; + } + type = (type == PTHREAD_PROCESS_SHARED ? 4 : 0); + + *a &= ~4; + *a |= type; + + return r; +} + +int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *a, int *type) +{ + *type = *a & (8 + 16); + + return 0; +} + +int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type) +{ + if ((type & (8 + 16)) != 8 + 16) return EINVAL; + + *a &= ~(8 + 16); + *a |= type; + + return 0; +} + +int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *a, int * prio) +{ + *prio = *a / PTHREAD_PRIO_MULT; + return 0; +} + +int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio) +{ + *a &= (PTHREAD_PRIO_MULT - 1); + *a += prio * PTHREAD_PRIO_MULT; + + return 0; +} diff --git a/src/nanosleep.c b/src/nanosleep.c new file mode 100644 index 0000000..0cce449 --- /dev/null +++ b/src/nanosleep.c @@ -0,0 +1,71 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include +#include +#include +#include "pthread.h" +#include "pthread_time.h" +#include "winpthread_internal.h" + +#define POW10_3 1000 +#define POW10_4 10000 +#define POW10_6 1000000 +#define POW10_9 1000000000 +#define MAX_SLEEP_IN_MS 4294967294UL + +/** + * Sleep for the specified time. + * @param request The desired amount of time to sleep. + * @param remain The remain amount of time to sleep. + * @return If the function succeeds, the return value is 0. + * If the function fails, the return value is -1, + * with errno set to indicate the error. + */ +int nanosleep(const struct timespec *request, struct timespec *remain) +{ + unsigned long ms, rc = 0; + unsigned __int64 u64, want, real; + + union { + unsigned __int64 ns100; + FILETIME ft; + } _start, _end; + + if (request->tv_sec < 0 || request->tv_nsec < 0 || request->tv_nsec >= POW10_9) { + errno = EINVAL; + return -1; + } + + if (remain != NULL) GetSystemTimeAsFileTime(&_start.ft); + + want = u64 = request->tv_sec * POW10_3 + request->tv_nsec / POW10_6; + while (u64 > 0 && rc == 0) { + if (u64 >= MAX_SLEEP_IN_MS) ms = MAX_SLEEP_IN_MS; + else ms = (unsigned long) u64; + + u64 -= ms; + rc = pthread_delay_np_ms(ms); + } + + if (rc != 0) { /* WAIT_IO_COMPLETION (192) */ + if (remain != NULL) { + GetSystemTimeAsFileTime(&_end.ft); + real = (_end.ns100 - _start.ns100) / POW10_4; + + if (real >= want) u64 = 0; + else u64 = want - real; + + remain->tv_sec = u64 / POW10_3; + remain->tv_nsec = (long) (u64 % POW10_3) * POW10_6; + } + + errno = EINTR; + return -1; + } + + return 0; +} diff --git a/src/ref.c b/src/ref.c new file mode 100644 index 0000000..0344d45 --- /dev/null +++ b/src/ref.c @@ -0,0 +1,34 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 +#include +#include +#include "pthread.h" +#include "semaphore.h" +#include "rwlock.h" +#include "cond.h" +#include "barrier.h" +#include "sem.h" +#include "ref.h" +#include "misc.h" + diff --git a/src/ref.h b/src/ref.h new file mode 100644 index 0000000..5ca6750 --- /dev/null +++ b/src/ref.h @@ -0,0 +1,29 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREADS_REF_H +#define WIN_PTHREADS_REF_H +#include "pthread.h" +#include "semaphore.h" + +#endif + diff --git a/src/rwlock.c b/src/rwlock.c new file mode 100644 index 0000000..76669df --- /dev/null +++ b/src/rwlock.c @@ -0,0 +1,537 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 +#include +#include +#include "pthread.h" +#include "thread.h" +#include "ref.h" +#include "rwlock.h" +#include "misc.h" + +static pthread_spinlock_t rwl_global = PTHREAD_SPINLOCK_INITIALIZER; + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw); + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_unref(volatile pthread_rwlock_t *rwl, int res) +{ + pthread_spin_lock(&rwl_global); +#ifdef WINPTHREAD_DBG + assert((((rwlock_t *)*rwl)->valid == LIFE_RWLOCK) && (((rwlock_t *)*rwl)->busy > 0)); +#endif + ((rwlock_t *)*rwl)->busy--; + pthread_spin_unlock(&rwl_global); + return res; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref(pthread_rwlock_t *rwl, int f ) +{ + int r = 0; + if (STATIC_RWL_INITIALIZER(*rwl)) { + r = rwlock_static_init(rwl); + if (r != 0 && r != EBUSY) + return r; + } + pthread_spin_lock(&rwl_global); + + if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL; + else { + ((rwlock_t *)*rwl)->busy ++; + } + + pthread_spin_unlock(&rwl_global); + + return r; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_unlock(pthread_rwlock_t *rwl ) +{ + int r = 0; + + pthread_spin_lock(&rwl_global); + + if (!rwl || !*rwl || ((rwlock_t *)*rwl)->valid != LIFE_RWLOCK) r = EINVAL; + else if (STATIC_RWL_INITIALIZER(*rwl)) r= EPERM; + else { + ((rwlock_t *)*rwl)->busy ++; + } + + pthread_spin_unlock(&rwl_global); + + return r; +} + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwl_ref_destroy(pthread_rwlock_t *rwl, pthread_rwlock_t *rDestroy ) +{ + int r = 0; + + *rDestroy = (pthread_rwlock_t)NULL; + pthread_spin_lock(&rwl_global); + + if (!rwl || !*rwl) r = EINVAL; + else { + rwlock_t *r_ = (rwlock_t *)*rwl; + if (STATIC_RWL_INITIALIZER(*rwl)) *rwl = (pthread_rwlock_t)NULL; + else if (r_->valid != LIFE_RWLOCK) r = EINVAL; + else if (r_->busy) r = EBUSY; + else { + *rDestroy = *rwl; + *rwl = (pthread_rwlock_t)NULL; + } + } + + pthread_spin_unlock(&rwl_global); + return r; +} + +static int rwlock_gain_both_locks(rwlock_t *rwlock) +{ + int ret; + ret = pthread_mutex_lock(&rwlock->mex); + if (ret != 0) + return ret; + ret = pthread_mutex_lock(&rwlock->mcomplete); + if (ret != 0) + pthread_mutex_unlock(&rwlock->mex); + return ret; +} + +static int rwlock_free_both_locks(rwlock_t *rwlock, int last_fail) +{ + int ret, ret2; + ret = pthread_mutex_unlock(&rwlock->mcomplete); + ret2 = pthread_mutex_unlock(&rwlock->mex); + if (last_fail && ret2 != 0) + ret = ret2; + else if (!last_fail && !ret) + ret = ret2; + return ret; +} + +#ifdef WINPTHREAD_DBG +static int print_state = 0; +void rwl_print_set(int state) +{ + print_state = state; +} + +void rwl_print(volatile pthread_rwlock_t *rwl, char *txt) +{ + if (!print_state) return; + rwlock_t *r = (rwlock_t *)*rwl; + if (r == NULL) { + printf("RWL%p %lu %s\n",(void *)*rwl,GetCurrentThreadId(),txt); + } else { + printf("RWL%p %lu V=%0X B=%d r=%ld w=%ld L=%p %s\n", + (void *)*rwl, + GetCurrentThreadId(), + (int)r->valid, + (int)r->busy, + 0L,0L,NULL,txt); + } +} +#endif + +static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER; + +static WINPTHREADS_ATTRIBUTE((noinline)) int rwlock_static_init(pthread_rwlock_t *rw) +{ + int r; + pthread_spin_lock(&cond_locked); + if (*rw != PTHREAD_RWLOCK_INITIALIZER) + { + pthread_spin_unlock(&cond_locked); + return EINVAL; + } + r = pthread_rwlock_init (rw, NULL); + pthread_spin_unlock(&cond_locked); + + return r; +} + +int pthread_rwlock_init (pthread_rwlock_t *rwlock_, const pthread_rwlockattr_t *attr) +{ + rwlock_t *rwlock; + int r; + + if(!rwlock_) + return EINVAL; + *rwlock_ = (pthread_rwlock_t)NULL; + if ((rwlock = calloc(1, sizeof(*rwlock))) == NULL) + return ENOMEM; + rwlock->valid = DEAD_RWLOCK; + + rwlock->nex_count = rwlock->nsh_count = rwlock->ncomplete = 0; + if ((r = pthread_mutex_init (&rwlock->mex, NULL)) != 0) + { + free(rwlock); + return r; + } + if ((r = pthread_mutex_init (&rwlock->mcomplete, NULL)) != 0) + { + pthread_mutex_destroy(&rwlock->mex); + free(rwlock); + return r; + } + if ((r = pthread_cond_init (&rwlock->ccomplete, NULL)) != 0) + { + pthread_mutex_destroy(&rwlock->mex); + pthread_mutex_destroy (&rwlock->mcomplete); + free(rwlock); + return r; + } + rwlock->valid = LIFE_RWLOCK; + *rwlock_ = (pthread_rwlock_t)rwlock; + return r; +} + +int pthread_rwlock_destroy (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + pthread_rwlock_t rDestroy; + int r, r2; + + pthread_spin_lock(&cond_locked); + r = rwl_ref_destroy(rwlock_,&rDestroy); + pthread_spin_unlock(&cond_locked); + + if(r) return r; + if(!rDestroy) return 0; /* destroyed a (still) static initialized rwl */ + + rwlock = (rwlock_t *)rDestroy; + r = rwlock_gain_both_locks (rwlock); + if (r != 0) + { + *rwlock_ = rDestroy; + return r; + } + if (rwlock->nsh_count > rwlock->ncomplete || rwlock->nex_count > 0) + { + *rwlock_ = rDestroy; + r = rwlock_free_both_locks(rwlock, 1); + if (!r) + r = EBUSY; + return r; + } + rwlock->valid = DEAD_RWLOCK; + r = rwlock_free_both_locks(rwlock, 0); + if (r != 0) { *rwlock_ = rDestroy; return r; } + + r = pthread_cond_destroy(&rwlock->ccomplete); + r2 = pthread_mutex_destroy(&rwlock->mex); + if (!r) r = r2; + r2 = pthread_mutex_destroy(&rwlock->mcomplete); + if (!r) r = r2; + rwlock->valid = DEAD_RWLOCK; + free((void *)rDestroy); + return 0; +} + +int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + /* pthread_testcancel(); */ + + ret = rwl_ref(rwlock_,0); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + + ret = pthread_mutex_lock(&rwlock->mex); + if (ret != 0) return rwl_unref(rwlock_, ret); + InterlockedIncrement((long*)&rwlock->nsh_count); + if (rwlock->nsh_count == INT_MAX) + { + ret = pthread_mutex_lock(&rwlock->mcomplete); + if (ret != 0) + { + pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_,ret); + } + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + ret = rwlock_free_both_locks(rwlock, 0); + return rwl_unref(rwlock_, ret); + } + ret = pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_, ret); +} + +int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock_, const struct timespec *ts) +{ + rwlock_t *rwlock; + int ret; + + /* pthread_testcancel(); */ + + ret = rwl_ref(rwlock_,0); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + if ((ret = pthread_mutex_timedlock (&rwlock->mex, ts)) != 0) + return rwl_unref(rwlock_, ret); + InterlockedIncrement(&rwlock->nsh_count); + if (rwlock->nsh_count == INT_MAX) + { + ret = pthread_mutex_timedlock(&rwlock->mcomplete, ts); + if (ret != 0) + { + if (ret == ETIMEDOUT) + InterlockedIncrement(&rwlock->ncomplete); + pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_, ret); + } + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + ret = rwlock_free_both_locks(rwlock, 0); + return rwl_unref(rwlock_, ret); + } + ret = pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_, ret); +} + +int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + ret = rwl_ref(rwlock_,RWL_TRY); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + ret = pthread_mutex_trylock(&rwlock->mex); + if (ret != 0) + return rwl_unref(rwlock_, ret); + InterlockedIncrement(&rwlock->nsh_count); + if (rwlock->nsh_count == INT_MAX) + { + ret = pthread_mutex_lock(&rwlock->mcomplete); + if (ret != 0) + { + pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_, ret); + } + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + ret = rwlock_free_both_locks(rwlock, 0); + return rwl_unref(rwlock_, ret); + } + ret = pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_,ret); +} + +int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + ret = rwl_ref(rwlock_,RWL_TRY); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + ret = pthread_mutex_trylock (&rwlock->mex); + if (ret != 0) + return rwl_unref(rwlock_, ret); + ret = pthread_mutex_trylock(&rwlock->mcomplete); + if (ret != 0) + { + int r1 = pthread_mutex_unlock(&rwlock->mex); + if (r1 != 0) + ret = r1; + return rwl_unref(rwlock_, ret); + } + if (rwlock->nex_count != 0) + return rwl_unref(rwlock_, EBUSY); + if (rwlock->ncomplete > 0) + { + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + } + if (rwlock->nsh_count > 0) + { + ret = rwlock_free_both_locks(rwlock, 0); + if (!ret) + ret = EBUSY; + return rwl_unref(rwlock_, ret); + } + rwlock->nex_count = 1; + return rwl_unref(rwlock_, 0); +} + +int pthread_rwlock_unlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + ret = rwl_ref_unlock(rwlock_); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + if (rwlock->nex_count == 0) + { + ret = pthread_mutex_lock(&rwlock->mcomplete); + if (!ret) + { + int r1; + InterlockedIncrement(&rwlock->ncomplete); + if (rwlock->ncomplete == 0) + ret = pthread_cond_signal(&rwlock->ccomplete); + r1 = pthread_mutex_unlock(&rwlock->mcomplete); + if (!ret) + ret = r1; + } + } + else + { + InterlockedDecrement(&rwlock->nex_count); + ret = rwlock_free_both_locks(rwlock, 0); + } + return rwl_unref(rwlock_, ret); +} + +static void st_cancelwrite (void *arg) +{ + rwlock_t *rwlock = (rwlock_t *)arg; + + rwlock->nsh_count = - rwlock->ncomplete; + rwlock->ncomplete = 0; + rwlock_free_both_locks(rwlock, 0); +} + +int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock_) +{ + rwlock_t *rwlock; + int ret; + + /* pthread_testcancel(); */ + ret = rwl_ref(rwlock_,0); + if(ret != 0) return ret; + + rwlock = (rwlock_t *)*rwlock_; + ret = rwlock_gain_both_locks(rwlock); + if (ret != 0) + return rwl_unref(rwlock_,ret); + + if (rwlock->nex_count == 0) + { + if (rwlock->ncomplete > 0) + { + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + } + if (rwlock->nsh_count > 0) + { + rwlock->ncomplete = -rwlock->nsh_count; + pthread_cleanup_push(st_cancelwrite, (void *) rwlock); + do { + ret = pthread_cond_wait(&rwlock->ccomplete, &rwlock->mcomplete); + } while (!ret && rwlock->ncomplete < 0); + + pthread_cleanup_pop(!ret ? 0 : 1); + if (!ret) + rwlock->nsh_count = 0; + } + } + if(!ret) + InterlockedIncrement((long*)&rwlock->nex_count); + return rwl_unref(rwlock_,ret); +} + +int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock_, const struct timespec *ts) +{ + int ret; + rwlock_t *rwlock; + + /* pthread_testcancel(); */ + if (!rwlock_ || !ts) + return EINVAL; + if ((ret = rwl_ref(rwlock_,0)) != 0) + return ret; + rwlock = (rwlock_t *)*rwlock_; + + ret = pthread_mutex_timedlock(&rwlock->mex, ts); + if (ret != 0) + return rwl_unref(rwlock_,ret); + ret = pthread_mutex_timedlock (&rwlock->mcomplete, ts); + if (ret != 0) + { + pthread_mutex_unlock(&rwlock->mex); + return rwl_unref(rwlock_,ret); + } + if (rwlock->nex_count == 0) + { + if (rwlock->ncomplete > 0) + { + rwlock->nsh_count -= rwlock->ncomplete; + rwlock->ncomplete = 0; + } + if (rwlock->nsh_count > 0) + { + rwlock->ncomplete = -rwlock->nsh_count; + pthread_cleanup_push(st_cancelwrite, (void *) rwlock); + do { + ret = pthread_cond_timedwait(&rwlock->ccomplete, &rwlock->mcomplete, ts); + } while (rwlock->ncomplete < 0 && !ret); + pthread_cleanup_pop(!ret ? 0 : 1); + + if (!ret) + rwlock->nsh_count = 0; + } + } + if(!ret) + InterlockedIncrement((long*)&rwlock->nex_count); + return rwl_unref(rwlock_,ret); +} + +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a) +{ + if (!a) + return EINVAL; + return 0; +} + +int pthread_rwlockattr_init(pthread_rwlockattr_t *a) +{ + if (!a) + return EINVAL; + *a = PTHREAD_PROCESS_PRIVATE; + return 0; +} + +int pthread_rwlockattr_getpshared(pthread_rwlockattr_t *a, int *s) +{ + if (!a || !s) + return EINVAL; + *s = *a; + return 0; +} + +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int s) +{ + if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE)) + return EINVAL; + *a = s; + return 0; +} diff --git a/src/rwlock.h b/src/rwlock.h new file mode 100644 index 0000000..2d640fa --- /dev/null +++ b/src/rwlock.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREADS_RWLOCK_H +#define WIN_PTHREADS_RWLOCK_H + +#define LIFE_RWLOCK 0xBAB1F0ED +#define DEAD_RWLOCK 0xDEADB0EF + +#define STATIC_RWL_INITIALIZER(x) ((pthread_rwlock_t)(x) == ((pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER)) + +typedef struct rwlock_t rwlock_t; +struct rwlock_t { + unsigned int valid; + int busy; + LONG nex_count; /* Exclusive access counter. */ + LONG nsh_count; /* Shared access counter. */ + LONG ncomplete; /* Shared completed counter. */ + pthread_mutex_t mex; /* Exclusive access protection. */ + pthread_mutex_t mcomplete; /* Shared completed protection. */ + pthread_cond_t ccomplete; /* Shared access completed queue. */ +}; + +#define RWL_SET 0x01 +#define RWL_TRY 0x02 + +void rwl_print(volatile pthread_rwlock_t *rwl, char *txt); +void rwl_print_set(int state); + +#endif diff --git a/src/sched.c b/src/sched.c new file mode 100644 index 0000000..976bcc1 --- /dev/null +++ b/src/sched.c @@ -0,0 +1,218 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 +#include +#include "pthread.h" +#include "thread.h" + +#include "misc.h" + +int sched_get_priority_min(int pol) +{ + if (pol < SCHED_MIN || pol > SCHED_MAX) { + errno = EINVAL; + return -1; + } + + return THREAD_PRIORITY_IDLE; +} + +int sched_get_priority_max(int pol) +{ + if (pol < SCHED_MIN || pol > SCHED_MAX) { + errno = EINVAL; + return -1; + } + + return THREAD_PRIORITY_TIME_CRITICAL; +} + +int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *p) +{ + int r = 0; + + if (attr == NULL || p == NULL) { + return EINVAL; + } + memcpy(&attr->param, p, sizeof (*p)); + return r; +} + +int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *p) +{ + int r = 0; + + if (attr == NULL || p == NULL) { + return EINVAL; + } + memcpy(p, &attr->param, sizeof (*p)); + return r; +} + +int pthread_attr_setschedpolicy (pthread_attr_t *attr, int pol) +{ + if (!attr || pol < SCHED_MIN || pol > SCHED_MAX) + return EINVAL; + if (pol != SCHED_OTHER) + return ENOTSUP; + return 0; +} + +int pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *pol) +{ + if (!attr || !pol) + return EINVAL; + *pol = SCHED_OTHER; + return 0; +} + +static int pthread_check(pthread_t t) +{ + struct _pthread_v *pv; + + if (!t) + return ESRCH; + pv = __pth_gpointer_locked (t); + if (pv->ended == 0) + return 0; + CHECK_OBJECT(pv, ESRCH); + return 0; +} + +int pthread_getschedparam(pthread_t t, int *pol, struct sched_param *p) +{ + int r; + //if (!t) + // t = pthread_self(); + + if ((r = pthread_check(t)) != 0) + { + return r; + } + + if (!p || !pol) + { + return EINVAL; + } + *pol = __pth_gpointer_locked (t)->sched_pol; + p->sched_priority = __pth_gpointer_locked (t)->sched.sched_priority; + + return 0; +} + +int pthread_setschedparam(pthread_t t, int pol, const struct sched_param *p) +{ + struct _pthread_v *pv; + int r, pr = 0; + //if (!t.p) t = pthread_self(); + + if ((r = pthread_check(t)) != 0) + return r; + + if (pol < SCHED_MIN || pol > SCHED_MAX || p == NULL) + return EINVAL; + if (pol != SCHED_OTHER) + return ENOTSUP; + pr = p->sched_priority; + if (pr < sched_get_priority_min(pol) || pr > sched_get_priority_max(pol)) + return EINVAL; + + /* See msdn: there are actually 7 priorities: + THREAD_PRIORITY_IDLE - -15 + THREAD_PRIORITY_LOWEST -2 + THREAD_PRIORITY_BELOW_NORMAL -1 + THREAD_PRIORITY_NORMAL 0 + THREAD_PRIORITY_ABOVE_NORMAL 1 + THREAD_PRIORITY_HIGHEST 2 + THREAD_PRIORITY_TIME_CRITICAL 15 + */ + if (pr <= THREAD_PRIORITY_IDLE) { + pr = THREAD_PRIORITY_IDLE; + } else if (pr <= THREAD_PRIORITY_LOWEST) { + pr = THREAD_PRIORITY_LOWEST; + } else if (pr >= THREAD_PRIORITY_TIME_CRITICAL) { + pr = THREAD_PRIORITY_TIME_CRITICAL; + } else if (pr >= THREAD_PRIORITY_HIGHEST) { + pr = THREAD_PRIORITY_HIGHEST; + } + pv = __pth_gpointer_locked (t); + if (SetThreadPriority(pv->h, pr)) { + pv->sched_pol = pol; + pv->sched.sched_priority = p->sched_priority; + } else + r = EINVAL; + return r; +} + +int sched_getscheduler(pid_t pid) +{ + if (pid != 0) + { + HANDLE h = NULL; + int selfPid = (int) GetCurrentProcessId (); + + if (pid != (pid_t) selfPid && (h = OpenProcess (PROCESS_QUERY_INFORMATION, 0, (DWORD) pid)) == NULL) + { + errno = (GetLastError () == (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH; + return -1; + } + if (h) + CloseHandle (h); + } + return SCHED_OTHER; +} + +int sched_setscheduler(pid_t pid, int pol, const struct sched_param *param) +{ + if (!param) + { + errno = EINVAL; + return -1; + } + if (pid != 0) + { + HANDLE h = NULL; + int selfPid = (int) GetCurrentProcessId (); + + if (pid != (pid_t) selfPid && (h = OpenProcess (PROCESS_SET_INFORMATION, 0, (DWORD) pid)) == NULL) + { + errno = (GetLastError () == (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH; + return -1; + } + if (h) + CloseHandle (h); + } + + if (pol != SCHED_OTHER) + { + errno = ENOSYS; + return -1; + } + return SCHED_OTHER; +} + +int sched_yield(void) +{ + Sleep(0); + return 0; +} diff --git a/src/sem.c b/src/sem.c new file mode 100644 index 0000000..340ff69 --- /dev/null +++ b/src/sem.c @@ -0,0 +1,354 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 +#include +#include +#include "pthread.h" +#include "thread.h" +#include "misc.h" +#include "semaphore.h" +#include "sem.h" +#include "ref.h" + +int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout); + +static int +sem_result (int res) +{ + if (res != 0) { + errno = res; + return -1; + } + return 0; +} + +int +sem_init (sem_t *sem, int pshared, unsigned int value) +{ + _sem_t *sv; + + if (!sem || value > (unsigned int)SEM_VALUE_MAX) + return sem_result (EINVAL); + if (pshared != PTHREAD_PROCESS_PRIVATE) + return sem_result (EPERM); + + if ((sv = (sem_t) calloc (1,sizeof (*sv))) == NULL) + return sem_result (ENOMEM); + + sv->value = value; + if (pthread_mutex_init (&sv->vlock, NULL) != 0) + { + free (sv); + return sem_result (ENOSPC); + } + if ((sv->s = CreateSemaphore (NULL, 0, SEM_VALUE_MAX, NULL)) == NULL) + { + pthread_mutex_destroy (&sv->vlock); + free (sv); + return sem_result (ENOSPC); + } + + sv->valid = LIFE_SEM; + *sem = sv; + return 0; +} + +int +sem_destroy (sem_t *sem) +{ + int r; + _sem_t *sv = NULL; + + if (!sem || (sv = *sem) == NULL) + return sem_result (EINVAL); + if ((r = pthread_mutex_lock (&sv->vlock)) != 0) + return sem_result (r); + +#if 0 + /* We don't wait for destroying a semaphore ... + or? */ + if (sv->value < 0) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (EBUSY); + } +#endif + + if (!CloseHandle (sv->s)) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (EINVAL); + } + *sem = NULL; + sv->value = SEM_VALUE_MAX; + pthread_mutex_unlock(&sv->vlock); + Sleep (0); + while (pthread_mutex_destroy (&sv->vlock) == EBUSY) + Sleep (0); + sv->valid = DEAD_SEM; + free (sv); + return 0; +} + +static int +sem_std_enter (sem_t *sem,_sem_t **svp, int do_test) +{ + int r; + _sem_t *sv; + + if (do_test) + pthread_testcancel (); + if (!sem) + return sem_result (EINVAL); + sv = *sem; + if (sv == NULL) + return sem_result (EINVAL); + + if ((r = pthread_mutex_lock (&sv->vlock)) != 0) + return sem_result (r); + + if (*sem == NULL) + { + pthread_mutex_unlock(&sv->vlock); + return sem_result (EINVAL); + } + *svp = sv; + return 0; +} + +int +sem_trywait (sem_t *sem) +{ + _sem_t *sv; + + if (sem_std_enter (sem, &sv, 0) != 0) + return -1; + if (sv->value <= 0) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (EAGAIN); + } + sv->value--; + pthread_mutex_unlock (&sv->vlock); + + return 0; +} + +struct sSemTimedWait +{ + sem_t *p; + int *ret; +}; + +static void +clean_wait_sem (void *s) +{ + struct sSemTimedWait *p = (struct sSemTimedWait *) s; + _sem_t *sv = NULL; + + if (sem_std_enter (p->p, &sv, 0) != 0) + return; + + if (WaitForSingleObject (sv->s, 0) != WAIT_OBJECT_0) + InterlockedIncrement (&sv->value); + else if (p->ret) + p->ret[0] = 0; + pthread_mutex_unlock (&sv->vlock); +} + +int +sem_wait (sem_t *sem) +{ + long cur_v; + int ret = 0; + _sem_t *sv; + HANDLE semh; + struct sSemTimedWait arg; + + if (sem_std_enter (sem, &sv, 1) != 0) + return -1; + + arg.ret = &ret; + arg.p = sem; + InterlockedDecrement (&sv->value); + cur_v = sv->value; + semh = sv->s; + pthread_mutex_unlock (&sv->vlock); + + if (cur_v >= 0) + return 0; + else + { + pthread_cleanup_push (clean_wait_sem, (void *) &arg); + ret = do_sema_b_wait_intern (semh, 2, INFINITE); + pthread_cleanup_pop (ret); + if (ret == EINVAL) + return 0; + } + + if (!ret) + return 0; + + return sem_result (ret); +} + +int +sem_timedwait (sem_t *sem, const struct timespec *t) +{ + int cur_v, ret = 0; + DWORD dwr; + HANDLE semh; + _sem_t *sv; + struct sSemTimedWait arg; + + if (!t) + return sem_wait (sem); + dwr = dwMilliSecs(_pthread_rel_time_in_ms (t)); + + if (sem_std_enter (sem, &sv, 1) != 0) + return -1; + + arg.ret = &ret; + arg.p = sem; + InterlockedDecrement (&sv->value); + cur_v = sv->value; + semh = sv->s; + pthread_mutex_unlock(&sv->vlock); + + if (cur_v >= 0) + return 0; + else + { + pthread_cleanup_push (clean_wait_sem, (void *) &arg); + ret = do_sema_b_wait_intern (semh, 2, dwr); + pthread_cleanup_pop (ret); + if (ret == EINVAL) + return 0; + } + + if (!ret) + return 0; + return sem_result (ret); +} + +int +sem_post (sem_t *sem) +{ + _sem_t *sv; + + if (sem_std_enter (sem, &sv, 0) != 0) + return -1; + + if (sv->value >= SEM_VALUE_MAX) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (ERANGE); + } + InterlockedIncrement (&sv->value); + if (sv->value > 0 || ReleaseSemaphore (sv->s, 1, NULL)) + { + pthread_mutex_unlock (&sv->vlock); + return 0; + } + InterlockedDecrement (&sv->value); + pthread_mutex_unlock (&sv->vlock); + + return sem_result (EINVAL); +} + +int +sem_post_multiple (sem_t *sem, int count) +{ + int waiters_count; + _sem_t *sv; + + if (count <= 0) + return sem_result (EINVAL); + if (sem_std_enter (sem, &sv, 0) != 0) + return -1; + + if (sv->value > (SEM_VALUE_MAX - count)) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (ERANGE); + } + waiters_count = -sv->value; + sv->value += count; + /*InterlockedExchangeAdd((long*)&sv->value, (long) count);*/ + if (waiters_count <= 0 + || ReleaseSemaphore (sv->s, + (waiters_count < count ? waiters_count + : count), NULL)) + { + pthread_mutex_unlock(&sv->vlock); + return 0; + } + /*InterlockedExchangeAdd((long*)&sv->value, -((long) count));*/ + sv->value -= count; + pthread_mutex_unlock(&sv->vlock); + return sem_result (EINVAL); +} + +sem_t * +sem_open (const char *name, int oflag, mode_t mode, unsigned int value) +{ + sem_result (ENOSYS); + return NULL; +} + +int +sem_close (sem_t *sem) +{ + return sem_result (ENOSYS); +} + +int +sem_unlink (const char *name) +{ + return sem_result (ENOSYS); +} + +int +sem_getvalue (sem_t *sem, int *sval) +{ + _sem_t *sv; + int r; + + if (!sval) + return sem_result (EINVAL); + + if (!sem || (sv = *sem) == NULL) + return sem_result (EINVAL); + + if ((r = pthread_mutex_lock (&sv->vlock)) != 0) + return sem_result (r); + if (*sem == NULL) + { + pthread_mutex_unlock (&sv->vlock); + return sem_result (EINVAL); + } + + *sval = (int) sv->value; + pthread_mutex_unlock (&sv->vlock); + return 0; +} diff --git a/src/sem.h b/src/sem.h new file mode 100644 index 0000000..3b3ace7 --- /dev/null +++ b/src/sem.h @@ -0,0 +1,40 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_SEM +#define WIN_SEM + +#include + +#define LIFE_SEM 0xBAB1F00D +#define DEAD_SEM 0xDEADBEEF + +typedef struct _sem_t _sem_t; +struct _sem_t +{ + unsigned int valid; + HANDLE s; + volatile long value; + pthread_mutex_t vlock; +}; + +#endif /* WIN_SEM */ diff --git a/src/spinlock.c b/src/spinlock.c new file mode 100644 index 0000000..224c5a0 --- /dev/null +++ b/src/spinlock.c @@ -0,0 +1,74 @@ +/* + Copyright (c) 2013 mingw-w64 project + Copyright (c) 2015 Intel Corporation + + 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 +#include "pthread.h" +#include "misc.h" + +/* We use the pthread_spinlock_t itself as a lock: + -1 is free, 0 is locked. + (This is dictated by PTHREAD_SPINLOCK_INITIALIZER, which we can't change + without breaking binary compatibility.) */ +typedef intptr_t spinlock_word_t; + +int +pthread_spin_init (pthread_spinlock_t *lock, int pshared) +{ + spinlock_word_t *lk = (spinlock_word_t *)lock; + *lk = -1; + return 0; +} + + +int +pthread_spin_destroy (pthread_spinlock_t *lock) +{ + return 0; +} + +int +pthread_spin_lock (pthread_spinlock_t *lock) +{ + volatile spinlock_word_t *lk = (volatile spinlock_word_t *)lock; + while (unlikely(InterlockedExchangePointer((PVOID volatile *)lk, 0) == 0)) + do { + YieldProcessor(); + } while (*lk == 0); + return 0; +} + +int +pthread_spin_trylock (pthread_spinlock_t *lock) +{ + spinlock_word_t *lk = (spinlock_word_t *)lock; + return InterlockedExchangePointer((PVOID volatile *)lk, 0) == 0 ? EBUSY : 0; +} + + +int +pthread_spin_unlock (pthread_spinlock_t *lock) +{ + volatile spinlock_word_t *lk = (volatile spinlock_word_t *)lock; + *lk = -1; + return 0; +} diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 0000000..36ee665 --- /dev/null +++ b/src/thread.c @@ -0,0 +1,1914 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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 +#include +#include +#include +#include +#include +#include "pthread.h" +#include "thread.h" +#include "misc.h" +#include "winpthread_internal.h" + +static _pthread_v *__pthread_self_lite (void); + +void (**_pthread_key_dest)(void *) = NULL; + +static volatile long _pthread_cancelling; +static int _pthread_concur; + +/* FIXME Will default to zero as needed */ +static pthread_once_t _pthread_tls_once; +static DWORD _pthread_tls = 0xffffffff; + +static pthread_rwlock_t _pthread_key_lock = PTHREAD_RWLOCK_INITIALIZER; +static unsigned long _pthread_key_max=0L; +static unsigned long _pthread_key_sch=0L; + +static _pthread_v *pthr_root = NULL, *pthr_last = NULL; +static pthread_mutex_t mtx_pthr_locked = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + +static __pthread_idlist *idList = NULL; +static size_t idListCnt = 0; +static size_t idListMax = 0; +static pthread_t idListNextId = 0; + +#if !defined(_MSC_VER) +#define USE_VEH_FOR_MSC_SETTHREADNAME +#endif +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +/* forbidden RemoveVectoredExceptionHandler/AddVectoredExceptionHandler APIs */ +#undef USE_VEH_FOR_MSC_SETTHREADNAME +#endif + +#if defined(USE_VEH_FOR_MSC_SETTHREADNAME) +static void *SetThreadName_VEH_handle = NULL; + +static LONG __stdcall +SetThreadName_VEH (PEXCEPTION_POINTERS ExceptionInfo) +{ + if (ExceptionInfo->ExceptionRecord != NULL && + ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME) + return EXCEPTION_CONTINUE_EXECUTION; + + return EXCEPTION_CONTINUE_SEARCH; +} + +static PVOID (*AddVectoredExceptionHandlerFuncPtr) (ULONG, PVECTORED_EXCEPTION_HANDLER); +static ULONG (*RemoveVectoredExceptionHandlerFuncPtr) (PVOID); + +static void __attribute__((constructor)) +ctor (void) +{ + HMODULE module = GetModuleHandleA("kernel32.dll"); + if (module) { + AddVectoredExceptionHandlerFuncPtr = (__typeof__(AddVectoredExceptionHandlerFuncPtr)) GetProcAddress(module, "AddVectoredExceptionHandler"); + RemoveVectoredExceptionHandlerFuncPtr = (__typeof__(RemoveVectoredExceptionHandlerFuncPtr)) GetProcAddress(module, "RemoveVectoredExceptionHandler"); + } +} +#endif + +typedef struct _THREADNAME_INFO +{ + DWORD dwType; /* must be 0x1000 */ + LPCSTR szName; /* pointer to name (in user addr space) */ + DWORD dwThreadID; /* thread ID (-1=caller thread) */ + DWORD dwFlags; /* reserved for future use, must be zero */ +} THREADNAME_INFO; + +static void +SetThreadName (DWORD dwThreadID, LPCSTR szThreadName) +{ + THREADNAME_INFO info; + DWORD infosize; + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + infosize = sizeof (info) / sizeof (ULONG_PTR); + +#if defined(_MSC_VER) && !defined (USE_VEH_FOR_MSC_SETTHREADNAME) + __try + { + RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (ULONG_PTR *)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } +#else + /* Without a debugger we *must* have an exception handler, + * otherwise raising an exception will crash the process. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if ((!IsDebuggerPresent ()) && (SetThreadName_VEH_handle == NULL)) +#else + if (!IsDebuggerPresent ()) +#endif + return; + + RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (ULONG_PTR *) &info); +#endif +} + +/* Search the list idList for an element with identifier ID. If + found, its associated _pthread_v pointer is returned, otherwise + NULL. + NOTE: This method is not locked. */ +static struct _pthread_v * +__pthread_get_pointer (pthread_t id) +{ + size_t l, r, p; + if (!idListCnt) + return NULL; + if (idListCnt == 1) + return (idList[0].id == id ? idList[0].ptr : NULL); + l = 0; r = idListCnt - 1; + while (l <= r) + { + p = (l + r) >> 1; + if (idList[p].id == id) + return idList[p].ptr; + else if (idList[p].id > id) + { + if (p == l) + return NULL; + r = p - 1; + } + else + { + l = p + 1; + } + } + + return NULL; +} + +static void +__pth_remove_use_for_key (pthread_key_t key) +{ + int i; + + pthread_mutex_lock (&mtx_pthr_locked); + for (i = 0; i < idListCnt; i++) + { + if (idList[i].ptr != NULL + && idList[i].ptr->keyval != NULL + && key < idList[i].ptr->keymax) + { + idList[i].ptr->keyval[key] = NULL; + idList[i].ptr->keyval_set[key] = 0; + } + } + pthread_mutex_unlock (&mtx_pthr_locked); +} + +/* Search the list idList for an element with identifier ID. If + found, its associated _pthread_v pointer is returned, otherwise + NULL. + NOTE: This method uses lock mtx_pthr_locked. */ +struct _pthread_v * +__pth_gpointer_locked (pthread_t id) +{ + struct _pthread_v *ret; + if (!id) + return NULL; + pthread_mutex_lock (&mtx_pthr_locked); + ret = __pthread_get_pointer (id); + pthread_mutex_unlock (&mtx_pthr_locked); + return ret; +} + +/* Registers in the list idList an element with _pthread_v pointer + and creates and unique identifier ID. If successful created the + ID of this element is returned, otherwise on failure zero ID gets + returned. + NOTE: This method is not locked. */ +static pthread_t +__pthread_register_pointer (struct _pthread_v *ptr) +{ + __pthread_idlist *e; + size_t i; + + if (!ptr) + return 0; + /* Check if a resize of list is necessary. */ + if (idListCnt >= idListMax) + { + if (!idListCnt) + { + e = (__pthread_idlist *) malloc (sizeof (__pthread_idlist) * 16); + if (!e) + return 0; + idListMax = 16; + idList = e; + } + else + { + e = (__pthread_idlist *) realloc (idList, sizeof (__pthread_idlist) * (idListMax + 16)); + if (!e) + return 0; + idListMax += 16; + idList = e; + } + } + do + { + ++idListNextId; + /* If two MSB are set we reset to id 1. We need to check here bits + to avoid gcc's no-overflow issue on increment. Additionally we + need to handle different size of pthread_t on 32-bit/64-bit. */ + if ((idListNextId & ( ((pthread_t) 1) << ((sizeof (pthread_t) * 8) - 2))) != 0) + idListNextId = 1; + } + while (idListNextId == 0 || __pthread_get_pointer (idListNextId)); + /* We assume insert at end of list. */ + i = idListCnt; + if (i != 0) + { + /* Find position we can actual insert sorted. */ + while (i > 0 && idList[i - 1].id > idListNextId) + --i; + if (i != idListCnt) + memmove (&idList[i + 1], &idList[i], sizeof (__pthread_idlist) * (idListCnt - i)); + } + idList[i].id = idListNextId; + idList[i].ptr = ptr; + ++idListCnt; + return idListNextId; +} + +/* Deregisters in the list idList an element with identifier ID and + returns its _pthread_v pointer on success. Otherwise NULL is returned. + NOTE: This method is not locked. */ +static struct _pthread_v * +__pthread_deregister_pointer (pthread_t id) +{ + size_t l, r, p; + if (!idListCnt) + return NULL; + l = 0; r = idListCnt - 1; + while (l <= r) + { + p = (l + r) >> 1; + if (idList[p].id == id) + { + struct _pthread_v *ret = idList[p].ptr; + p++; + if (p < idListCnt) + memmove (&idList[p - 1], &idList[p], sizeof (__pthread_idlist) * (idListCnt - p)); + --idListCnt; + /* Is this last element in list then free list. */ + if (idListCnt == 0) + { + free (idList); + idListCnt = idListMax = 0; + } + return ret; + } + else if (idList[p].id > id) + { + if (p == l) + return NULL; + r = p - 1; + } + else + { + l = p + 1; + } + } + return NULL; +} + +/* Save a _pthread_v element for reuse in pool. */ +static void +push_pthread_mem (_pthread_v *sv) +{ + if (!sv || sv->next != NULL) + return; + pthread_mutex_lock (&mtx_pthr_locked); + if (sv->x != 0) + __pthread_deregister_pointer (sv->x); + if (sv->keyval) + free (sv->keyval); + if (sv->keyval_set) + free (sv->keyval_set); + if (sv->thread_name) + free (sv->thread_name); + memset (sv, 0, sizeof(struct _pthread_v)); + if (pthr_last == NULL) + pthr_root = pthr_last = sv; + else + { + pthr_last->next = sv; + pthr_last = sv; + } + pthread_mutex_unlock (&mtx_pthr_locked); +} + +/* Get a _pthread_v element from pool, or allocate it. + Note the unique identifier is created for the element here, too. */ +static _pthread_v * +pop_pthread_mem (void) +{ + _pthread_v *r = NULL; + + pthread_mutex_lock (&mtx_pthr_locked); + if ((r = pthr_root) == NULL) + { + if ((r = (_pthread_v *)calloc (1,sizeof(struct _pthread_v))) != NULL) + { + r->x = __pthread_register_pointer (r); + if (r->x == 0) + { + free (r); + r = NULL; + } + } + pthread_mutex_unlock (&mtx_pthr_locked); + return r; + } + r->x = __pthread_register_pointer (r); + if (r->x == 0) + r = NULL; + else + { + if((pthr_root = r->next) == NULL) + pthr_last = NULL; + + r->next = NULL; + } + pthread_mutex_unlock (&mtx_pthr_locked); + return r; +} + +/* Free memory consumed in _pthread_v pointer pool. */ +static void +free_pthread_mem (void) +{ +#if 0 + _pthread_v *t; + + pthread_mutex_lock (&mtx_pthr_locked); + t = pthr_root; + while (t != NULL) + { + _pthread_v *sv = t; + t = t->next; + if (sv->x != 0 && sv->ended == 0 && sv->valid != DEAD_THREAD) + { + pthread_mutex_unlock (&mtx_pthr_locked); + pthread_cancel (t->x); + Sleep (0); + pthread_mutex_lock (&mtx_pthr_locked); + t = pthr_root; + continue; + } + else if (sv->x != 0 && sv->valid != DEAD_THREAD) + { + pthread_mutex_unlock (&mtx_pthr_locked); + Sleep (0); + pthread_mutex_lock (&mtx_pthr_locked); + continue; + } + if (sv->x != 0) + __pthread_deregister_pointer (sv->x); + sv->x = 0; + free (sv); + pthr_root = t; + } + pthread_mutex_unlock (&mtx_pthr_locked); +#endif + return; +} + +static void +replace_spin_keys (pthread_spinlock_t *old, pthread_spinlock_t new) +{ + if (old == NULL) + return; + + if (EPERM == pthread_spin_destroy (old)) + { +#define THREADERR "Error cleaning up spin_keys for thread %lu.\n" + char threaderr[sizeof(THREADERR) + 8] = { 0 }; + snprintf(threaderr, sizeof(threaderr), THREADERR, GetCurrentThreadId()); +#undef THREADERR + OutputDebugStringA (threaderr); + abort (); + } + + *old = new; +} + +/* Hook for TLS-based deregistration/registration of thread. */ +static void WINAPI +__dyn_tls_pthread (HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved) +{ + _pthread_v *t = NULL; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + if (dwReason == DLL_PROCESS_DETACH) + { +#if defined(USE_VEH_FOR_MSC_SETTHREADNAME) + if (lpreserved == NULL && SetThreadName_VEH_handle != NULL) + { + if (RemoveVectoredExceptionHandlerFuncPtr != NULL) + RemoveVectoredExceptionHandlerFuncPtr (SetThreadName_VEH_handle); + SetThreadName_VEH_handle = NULL; + } +#endif + free_pthread_mem (); + } + else if (dwReason == DLL_PROCESS_ATTACH) + { +#if defined(USE_VEH_FOR_MSC_SETTHREADNAME) + if (AddVectoredExceptionHandlerFuncPtr != NULL) + SetThreadName_VEH_handle = AddVectoredExceptionHandlerFuncPtr (1, &SetThreadName_VEH); + else + SetThreadName_VEH_handle = NULL; + /* Can't do anything on error anyway, check for NULL later */ +#endif + } + else if (dwReason == DLL_THREAD_DETACH) + { + if (_pthread_tls != 0xffffffff) + t = (_pthread_v *)TlsGetValue(_pthread_tls); + if (t && t->thread_noposix != 0) + { + _pthread_cleanup_dest (t->x); + if (t->h != NULL) + { + CloseHandle (t->h); + if (t->evStart) + CloseHandle (t->evStart); + t->evStart = NULL; + t->h = NULL; + } + pthread_mutex_destroy (&t->p_clock); + replace_spin_keys (&t->spin_keys, new_spin_keys); + push_pthread_mem (t); + t = NULL; + TlsSetValue (_pthread_tls, t); + } + else if (t && t->ended == 0) + { + if (t->evStart) + CloseHandle(t->evStart); + t->evStart = NULL; + t->ended = 1; + _pthread_cleanup_dest (t->x); + if ((t->p_state & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED) + { + t->valid = DEAD_THREAD; + if (t->h != NULL) + CloseHandle (t->h); + t->h = NULL; + pthread_mutex_destroy(&t->p_clock); + replace_spin_keys (&t->spin_keys, new_spin_keys); + push_pthread_mem (t); + t = NULL; + TlsSetValue (_pthread_tls, t); + return; + } + pthread_mutex_destroy(&t->p_clock); + replace_spin_keys (&t->spin_keys, new_spin_keys); + } + else if (t) + { + if (t->evStart) + CloseHandle (t->evStart); + t->evStart = NULL; + pthread_mutex_destroy (&t->p_clock); + replace_spin_keys (&t->spin_keys, new_spin_keys); + } + } +} + +/* TLS-runtime section variable. */ + +#if defined(_MSC_VER) +/* Force a reference to _tls_used to make the linker create the TLS + * directory if it's not already there. (e.g. if __declspec(thread) + * is not used). + * Force a reference to __xl_f to prevent whole program optimization + * from discarding the variable. */ + +/* On x86, symbols are prefixed with an underscore. */ +# if defined(_M_IX86) +# pragma comment(linker, "/include:__tls_used") +# pragma comment(linker, "/include:___xl_f") +# else +# pragma comment(linker, "/include:_tls_used") +# pragma comment(linker, "/include:__xl_f") +# endif + +/* .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK + * pointers. Pick an arbitrary location for our callback. + * + * See VC\...\crt\src\vcruntime\tlssup.cpp for reference. */ + +# pragma section(".CRT$XLF", long, read) +#endif + +WINPTHREADS_ATTRIBUTE((WINPTHREADS_SECTION(".CRT$XLF"))) +extern const PIMAGE_TLS_CALLBACK __xl_f; +const PIMAGE_TLS_CALLBACK __xl_f = __dyn_tls_pthread; + + +#ifdef WINPTHREAD_DBG +static int print_state = 0; +void thread_print_set (int state) +{ + print_state = state; +} + +void +thread_print (volatile pthread_t t, char *txt) +{ + if (!print_state) + return; + if (!t) + printf("T%p %lu %s\n",NULL,GetCurrentThreadId(),txt); + else + { + printf("T%p %lu V=%0X H=%p %s\n", + (void *) __pth_gpointer_locked (t), + GetCurrentThreadId(), + (__pth_gpointer_locked (t))->valid, + (__pth_gpointer_locked (t))->h, + txt + ); + } +} +#endif + +/* Internal collect-once structure. */ +typedef struct collect_once_t { + pthread_once_t *o; + pthread_mutex_t m; + int count; + struct collect_once_t *next; +} collect_once_t; + +static collect_once_t *once_obj = NULL; + +static pthread_spinlock_t once_global = PTHREAD_SPINLOCK_INITIALIZER; + +static collect_once_t * +enterOnceObject (pthread_once_t *o) +{ + collect_once_t *c, *p = NULL; + pthread_spin_lock (&once_global); + c = once_obj; + while (c != NULL && c->o != o) + { + c = (p = c)->next; + } + if (!c) + { + c = (collect_once_t *) calloc(1,sizeof(collect_once_t)); + c->o = o; + c->count = 1; + if (!p) + once_obj = c; + else + p->next = c; + pthread_mutex_init(&c->m, NULL); + } + else + c->count += 1; + pthread_spin_unlock (&once_global); + return c; +} + +static void +leaveOnceObject (collect_once_t *c) +{ + collect_once_t *h, *p = NULL; + if (!c) + return; + pthread_spin_lock (&once_global); + h = once_obj; + while (h != NULL && c != h) + h = (p = h)->next; + + if (h) + { + c->count -= 1; + if (c->count == 0) + { + pthread_mutex_destroy(&c->m); + if (!p) + once_obj = c->next; + else + p->next = c->next; + free (c); + } + } + else + fprintf(stderr, "%p not found?!?!\n", (void *) c); + pthread_spin_unlock (&once_global); +} + +static void +_pthread_once_cleanup (void *o) +{ + collect_once_t *co = (collect_once_t *) o; + pthread_mutex_unlock (&co->m); + leaveOnceObject (co); +} + +static int +_pthread_once_raw (pthread_once_t *o, void (*func)(void)) +{ + collect_once_t *co; + long state = *o; + + CHECK_PTR(o); + CHECK_PTR(func); + + if (state == 1) + return 0; + co = enterOnceObject(o); + pthread_mutex_lock(&co->m); + if (*o == 0) + { + func(); + *o = 1; + } + else if (*o != 1) + fprintf (stderr," once %p is %ld\n", (void *) o, (long) *o); + pthread_mutex_unlock(&co->m); + leaveOnceObject(co); + + /* Done */ + return 0; +} + +/* Unimplemented. */ +void * +pthread_timechange_handler_np(void *dummy) +{ + return NULL; +} + +/* Compatibility routine for pthread-win32. It waits for ellapse of + interval and additionally checks for possible thread-cancelation. */ +int +pthread_delay_np (const struct timespec *interval) +{ + DWORD to = (!interval ? 0 : dwMilliSecs (_pthread_time_in_ms_from_timespec (interval))); + struct _pthread_v *s = __pthread_self_lite (); + + if (!to) + { + pthread_testcancel (); + Sleep (0); + pthread_testcancel (); + return 0; + } + pthread_testcancel (); + if (s->evStart) + _pthread_wait_for_single_object (s->evStart, to); + else + Sleep (to); + pthread_testcancel (); + return 0; +} + +int pthread_delay_np_ms (DWORD to); + +int +pthread_delay_np_ms (DWORD to) +{ + struct _pthread_v *s = __pthread_self_lite (); + + if (!to) + { + pthread_testcancel (); + Sleep (0); + pthread_testcancel (); + return 0; + } + pthread_testcancel (); + if (s->evStart) + _pthread_wait_for_single_object (s->evStart, to); + else + Sleep (to); + pthread_testcancel (); + return 0; +} + +/* Compatibility routine for pthread-win32. It returns the + amount of available CPUs on system. */ +int +pthread_num_processors_np(void) +{ + int r = 0; + DWORD_PTR ProcessAffinityMask, SystemAffinityMask; + + if (GetProcessAffinityMask(GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask)) + { + for(; ProcessAffinityMask != 0; ProcessAffinityMask >>= 1) + r += (ProcessAffinityMask & 1) != 0; + } + /* assume at least 1 */ + return r ? r : 1; +} + +/* Compatiblity routine for pthread-win32. Allows to set amount of used + CPUs for process. */ +int +pthread_set_num_processors_np(int n) +{ + DWORD_PTR ProcessAffinityMask, ProcessNewAffinityMask = 0, SystemAffinityMask; + int r = 0; + /* need at least 1 */ + n = n ? n : 1; + if (GetProcessAffinityMask (GetCurrentProcess (), &ProcessAffinityMask, &SystemAffinityMask)) + { + for (; ProcessAffinityMask != 0; ProcessAffinityMask >>= 1) + { + ProcessNewAffinityMask <<= 1; + if ((ProcessAffinityMask & 1) != 0 && r < n) + { + ProcessNewAffinityMask |= 1; + r++; + } + } + SetProcessAffinityMask (GetCurrentProcess (),ProcessNewAffinityMask); + } + return r; +} + +int +pthread_once (pthread_once_t *o, void (*func)(void)) +{ + collect_once_t *co; + long state = *o; + + CHECK_PTR(o); + CHECK_PTR(func); + + if (state == 1) + return 0; + co = enterOnceObject(o); + pthread_mutex_lock(&co->m); + if (*o == 0) + { + pthread_cleanup_push(_pthread_once_cleanup, co); + func(); + pthread_cleanup_pop(0); + *o = 1; + } + else if (*o != 1) + fprintf (stderr," once %p is %ld\n", (void *) o, (long) *o); + pthread_mutex_unlock(&co->m); + leaveOnceObject(co); + + return 0; +} + +int +pthread_key_create (pthread_key_t *key, void (* dest)(void *)) +{ + unsigned int i; + long nmax; + void (**d)(void *); + + if (!key) + return EINVAL; + + pthread_rwlock_wrlock (&_pthread_key_lock); + + for (i = _pthread_key_sch; i < _pthread_key_max; i++) + { + if (!_pthread_key_dest[i]) + { + *key = i; + if (dest) + _pthread_key_dest[i] = dest; + else + _pthread_key_dest[i] = (void(*)(void *))1; + pthread_rwlock_unlock (&_pthread_key_lock); + return 0; + } + } + + for (i = 0; i < _pthread_key_sch; i++) + { + if (!_pthread_key_dest[i]) + { + *key = i; + if (dest) + _pthread_key_dest[i] = dest; + else + _pthread_key_dest[i] = (void(*)(void *))1; + pthread_rwlock_unlock (&_pthread_key_lock); + + return 0; + } + } + + if (_pthread_key_max == PTHREAD_KEYS_MAX) + { + pthread_rwlock_unlock(&_pthread_key_lock); + return ENOMEM; + } + + nmax = _pthread_key_max * 2; + if (nmax == 0) + nmax = _pthread_key_max + 1; + if (nmax > PTHREAD_KEYS_MAX) + nmax = PTHREAD_KEYS_MAX; + + /* No spare room anywhere */ + d = (void (__cdecl **)(void *))realloc(_pthread_key_dest, nmax * sizeof(*d)); + if (!d) + { + pthread_rwlock_unlock (&_pthread_key_lock); + return ENOMEM; + } + + /* Clear new region */ + memset ((void *) &d[_pthread_key_max], 0, (nmax-_pthread_key_max)*sizeof(void *)); + + /* Use new region */ + _pthread_key_dest = d; + _pthread_key_sch = _pthread_key_max + 1; + *key = _pthread_key_max; + _pthread_key_max = nmax; + + if (dest) + _pthread_key_dest[*key] = dest; + else + _pthread_key_dest[*key] = (void(*)(void *))1; + + pthread_rwlock_unlock (&_pthread_key_lock); + return 0; +} + +int +pthread_key_delete (pthread_key_t key) +{ + if (key >= _pthread_key_max || !_pthread_key_dest) + return EINVAL; + + pthread_rwlock_wrlock (&_pthread_key_lock); + + _pthread_key_dest[key] = NULL; + + /* Start next search from our location */ + if (_pthread_key_sch > key) + _pthread_key_sch = key; + /* So now we need to walk the complete list of threads + and remove key's reference for it. */ + __pth_remove_use_for_key (key); + + pthread_rwlock_unlock (&_pthread_key_lock); + return 0; +} + +void * +pthread_getspecific (pthread_key_t key) +{ + DWORD lasterr = GetLastError (); + void *r; + _pthread_v *t = __pthread_self_lite (); + pthread_spin_lock (&t->spin_keys); + r = (key >= t->keymax || t->keyval_set[key] == 0 ? NULL : t->keyval[key]); + pthread_spin_unlock (&t->spin_keys); + SetLastError (lasterr); + return r; +} + +int +pthread_setspecific (pthread_key_t key, const void *value) +{ + DWORD lasterr = GetLastError (); + _pthread_v *t = __pthread_self_lite (); + + pthread_spin_lock (&t->spin_keys); + + if (key >= t->keymax) + { + int keymax = (key + 1); + void **kv; + unsigned char *kv_set; + + kv = (void **) realloc (t->keyval, keymax * sizeof (void *)); + + if (!kv) + { + pthread_spin_unlock (&t->spin_keys); + return ENOMEM; + } + kv_set = (unsigned char *) realloc (t->keyval_set, keymax); + if (!kv_set) + { + pthread_spin_unlock (&t->spin_keys); + return ENOMEM; + } + + /* Clear new region */ + memset (&kv[t->keymax], 0, (keymax - t->keymax)*sizeof(void *)); + memset (&kv_set[t->keymax], 0, (keymax - t->keymax)); + + t->keyval = kv; + t->keyval_set = kv_set; + t->keymax = keymax; + } + + t->keyval[key] = (void *) value; + t->keyval_set[key] = 1; + pthread_spin_unlock (&t->spin_keys); + SetLastError (lasterr); + + return 0; +} + +int +pthread_equal (pthread_t t1, pthread_t t2) +{ + return (t1 == t2); +} + +void +pthread_tls_init (void) +{ + _pthread_tls = TlsAlloc(); + + /* Cannot continue if out of indexes */ + if (_pthread_tls == TLS_OUT_OF_INDEXES) + abort(); +} + +void +_pthread_cleanup_dest (pthread_t t) +{ + _pthread_v *tv; + unsigned int i, j; + + if (!t) + return; + tv = __pth_gpointer_locked (t); + if (!tv) + return; + + for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) + { + int flag = 0; + + pthread_spin_lock (&tv->spin_keys); + for (i = 0; i < tv->keymax; i++) + { + void *val = tv->keyval[i]; + + if (tv->keyval_set[i]) + { + pthread_rwlock_rdlock (&_pthread_key_lock); + if ((uintptr_t) _pthread_key_dest[i] > 1) + { + /* Call destructor */ + tv->keyval[i] = NULL; + tv->keyval_set[i] = 0; + pthread_spin_unlock (&tv->spin_keys); + _pthread_key_dest[i](val); + pthread_spin_lock (&tv->spin_keys); + flag = 1; + } + else + { + tv->keyval[i] = NULL; + tv->keyval_set[i] = 0; + } + pthread_rwlock_unlock(&_pthread_key_lock); + } + } + pthread_spin_unlock (&tv->spin_keys); + /* Nothing to do? */ + if (!flag) + return; + } +} + +static _pthread_v * +__pthread_self_lite (void) +{ + _pthread_v *t; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + _pthread_once_raw (&_pthread_tls_once, pthread_tls_init); + + t = (_pthread_v *) TlsGetValue (_pthread_tls); + if (t) + return t; + /* Main thread? */ + t = (struct _pthread_v *) pop_pthread_mem (); + + /* If cannot initialize main thread, then the only thing we can do is return null pthread_t */ + if (!__xl_f || !t) + return 0; + + t->p_state = PTHREAD_DEFAULT_ATTR /*| PTHREAD_CREATE_DETACHED*/; + t->tid = GetCurrentThreadId(); + t->evStart = CreateEvent (NULL, 1, 0, NULL); + t->p_clock = PTHREAD_MUTEX_INITIALIZER; + replace_spin_keys (&t->spin_keys, new_spin_keys); + t->sched_pol = SCHED_OTHER; + t->h = NULL; //GetCurrentThread(); + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &t->h, 0, FALSE, DUPLICATE_SAME_ACCESS)) + abort (); + t->sched.sched_priority = GetThreadPriority(t->h); + t->ended = 0; + t->thread_noposix = 1; + + /* Save for later */ + if (!TlsSetValue(_pthread_tls, t)) + abort (); + return t; +} + +pthread_t +pthread_self (void) +{ + _pthread_v *t = __pthread_self_lite (); + + if (!t) + return 0; + return t->x; +} + +/* Internal helper for getting event handle of thread T. */ +void * +pthread_getevent (void) +{ + _pthread_v *t = __pthread_self_lite (); + return (!t ? NULL : t->evStart); +} + +/* Internal helper for getting thread handle of thread T. */ +void * +pthread_gethandle (pthread_t t) +{ + struct _pthread_v *tv = __pth_gpointer_locked (t); + return (!tv ? NULL : tv->h); +} + +/* Internal helper for getting pointer of clean of current thread. */ +struct _pthread_cleanup ** +pthread_getclean (void) +{ + struct _pthread_v *t = __pthread_self_lite (); + if (!t) return NULL; + return &t->clean; +} + +int +pthread_get_concurrency (int *val) +{ + *val = _pthread_concur; + return 0; +} + +int +pthread_set_concurrency (int val) +{ + _pthread_concur = val; + return 0; +} + +void +pthread_exit (void *res) +{ + _pthread_v *t = NULL; + unsigned rslt = (unsigned) ((intptr_t) res); + struct _pthread_v *id = __pthread_self_lite (); + + id->ret_arg = res; + + _pthread_cleanup_dest (id->x); + if (id->thread_noposix == 0) + longjmp(id->jb, 1); + + /* Make sure we free ourselves if we are detached */ + if ((t = (_pthread_v *)TlsGetValue(_pthread_tls)) != NULL) + { + if (!t->h) + { + t->valid = DEAD_THREAD; + if (t->evStart) + CloseHandle (t->evStart); + t->evStart = NULL; + rslt = (unsigned) (size_t) t->ret_arg; + push_pthread_mem(t); + t = NULL; + TlsSetValue (_pthread_tls, t); + } + else + { + rslt = (unsigned) (size_t) t->ret_arg; + t->ended = 1; + if (t->evStart) + CloseHandle (t->evStart); + t->evStart = NULL; + if ((t->p_state & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED) + { + t->valid = DEAD_THREAD; + CloseHandle (t->h); + t->h = NULL; + push_pthread_mem(t); + t = NULL; + TlsSetValue(_pthread_tls, t); + } + } + } + /* Time to die */ + _endthreadex(rslt); +} + +void +_pthread_invoke_cancel (void) +{ + _pthread_cleanup *pcup; + struct _pthread_v *se = __pthread_self_lite (); + se->in_cancel = 1; + _pthread_setnobreak (1); + InterlockedDecrement(&_pthread_cancelling); + + /* Call cancel queue */ + for (pcup = se->clean; pcup; pcup = pcup->next) + { + pcup->func((pthread_once_t *)pcup->arg); + } + + _pthread_setnobreak (0); + pthread_exit(PTHREAD_CANCELED); +} + +int +__pthread_shallcancel (void) +{ + struct _pthread_v *t; + if (!_pthread_cancelling) + return 0; + t = __pthread_self_lite (); + if (t == NULL) + return 0; + if (t->nobreak <= 0 && t->cancelled && (t->p_state & PTHREAD_CANCEL_ENABLE)) + return 1; + return 0; +} + +void +_pthread_setnobreak (int v) +{ + struct _pthread_v *t = __pthread_self_lite (); + if (t == NULL) + return; + if (v > 0) + InterlockedIncrement ((long*)&t->nobreak); + else + InterlockedDecrement((long*)&t->nobreak); +} + +void +pthread_testcancel (void) +{ + struct _pthread_v *self = __pthread_self_lite (); + + if (!self || self->in_cancel) + return; + if (!_pthread_cancelling) + return; + pthread_mutex_lock (&self->p_clock); + + if (self->cancelled && (self->p_state & PTHREAD_CANCEL_ENABLE) && self->nobreak <= 0) + { + self->in_cancel = 1; + self->p_state &= ~PTHREAD_CANCEL_ENABLE; + if (self->evStart) + ResetEvent (self->evStart); + pthread_mutex_unlock (&self->p_clock); + _pthread_invoke_cancel (); + } + pthread_mutex_unlock (&self->p_clock); +} + +int +pthread_cancel (pthread_t t) +{ + struct _pthread_v *tv = __pth_gpointer_locked (t); + + if (tv == NULL) + return ESRCH; + CHECK_OBJECT(tv, ESRCH); + /*if (tv->ended) return ESRCH;*/ + pthread_mutex_lock(&tv->p_clock); + if (pthread_equal(pthread_self(), t)) + { + if(tv->cancelled) + { + pthread_mutex_unlock(&tv->p_clock); + return (tv->in_cancel ? ESRCH : 0); + } + tv->cancelled = 1; + InterlockedIncrement(&_pthread_cancelling); + if(tv->evStart) SetEvent(tv->evStart); + if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) != 0 && (tv->p_state & PTHREAD_CANCEL_ENABLE) != 0) + { + tv->p_state &= ~PTHREAD_CANCEL_ENABLE; + tv->in_cancel = 1; + pthread_mutex_unlock(&tv->p_clock); + _pthread_invoke_cancel(); + } + else + pthread_mutex_unlock(&tv->p_clock); + return 0; + } + + if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) != 0 && (tv->p_state & PTHREAD_CANCEL_ENABLE) != 0) + { + /* Dangerous asynchronous cancelling */ + CONTEXT ctxt; + + if(tv->in_cancel) + { + pthread_mutex_unlock(&tv->p_clock); + return (tv->in_cancel ? ESRCH : 0); + } + /* Already done? */ + if(tv->cancelled || tv->in_cancel) + { + /* ??? pthread_mutex_unlock (&tv->p_clock); */ + return ESRCH; + } + + ctxt.ContextFlags = CONTEXT_CONTROL; + + SuspendThread (tv->h); + if (WaitForSingleObject (tv->h, 0) == WAIT_TIMEOUT) + { + GetThreadContext(tv->h, &ctxt); +#ifdef _M_X64 + ctxt.Rip = (uintptr_t) _pthread_invoke_cancel; +#elif defined(_M_IX86) + ctxt.Eip = (uintptr_t) _pthread_invoke_cancel; +#elif defined(_M_ARM) || defined(_M_ARM64) + ctxt.Pc = (uintptr_t) _pthread_invoke_cancel; +#else +#error Unsupported architecture +#endif +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + SetThreadContext (tv->h, &ctxt); +#endif + + /* Also try deferred Cancelling */ + tv->cancelled = 1; + tv->p_state &= ~PTHREAD_CANCEL_ENABLE; + tv->in_cancel = 1; + + /* Notify everyone to look */ + InterlockedIncrement (&_pthread_cancelling); + if (tv->evStart) + SetEvent (tv->evStart); + pthread_mutex_unlock (&tv->p_clock); + + ResumeThread (tv->h); + } + } + else + { + if (tv->cancelled == 0) + { + /* Safe deferred Cancelling */ + tv->cancelled = 1; + + /* Notify everyone to look */ + InterlockedIncrement (&_pthread_cancelling); + if (tv->evStart) + SetEvent (tv->evStart); + } + else + { + pthread_mutex_unlock (&tv->p_clock); + return (tv->in_cancel ? ESRCH : 0); + } + } + pthread_mutex_unlock (&tv->p_clock); + return 0; +} + +/* half-stubbed version as we don't really well support signals */ +int +pthread_kill (pthread_t t, int sig) +{ + struct _pthread_v *tv; + + pthread_mutex_lock (&mtx_pthr_locked); + tv = __pthread_get_pointer (t); + if (!tv || t != tv->x || tv->in_cancel || tv->ended || tv->h == NULL + || tv->h == INVALID_HANDLE_VALUE) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return ESRCH; + } + pthread_mutex_unlock (&mtx_pthr_locked); + if (!sig) + return 0; + if (sig < SIGINT || sig > NSIG) + return EINVAL; + return pthread_cancel(t); +} + +unsigned +_pthread_get_state (const pthread_attr_t *attr, unsigned flag) +{ + return (attr->p_state & flag); +} + +int +_pthread_set_state (pthread_attr_t *attr, unsigned flag, unsigned val) +{ + if (~flag & val) + return EINVAL; + attr->p_state &= ~flag; + attr->p_state |= val; + + return 0; +} + +int +pthread_attr_init (pthread_attr_t *attr) +{ + memset (attr, 0, sizeof (pthread_attr_t)); + attr->p_state = PTHREAD_DEFAULT_ATTR; + attr->stack = NULL; + attr->s_size = 0; + return 0; +} + +int +pthread_attr_destroy (pthread_attr_t *attr) +{ + /* No need to do anything */ + memset (attr, 0, sizeof(pthread_attr_t)); + return 0; +} + +int +pthread_attr_setdetachstate (pthread_attr_t *a, int flag) +{ + return _pthread_set_state(a, PTHREAD_CREATE_DETACHED, flag); +} + +int +pthread_attr_getdetachstate (const pthread_attr_t *a, int *flag) +{ + *flag = _pthread_get_state(a, PTHREAD_CREATE_DETACHED); + return 0; +} + +int +pthread_attr_setinheritsched (pthread_attr_t *a, int flag) +{ + if (!a || (flag != PTHREAD_INHERIT_SCHED && flag != PTHREAD_EXPLICIT_SCHED)) + return EINVAL; + return _pthread_set_state(a, PTHREAD_INHERIT_SCHED, flag); +} + +int +pthread_attr_getinheritsched (const pthread_attr_t *a, int *flag) +{ + *flag = _pthread_get_state(a, PTHREAD_INHERIT_SCHED); + return 0; +} + +int +pthread_attr_setscope (pthread_attr_t *a, int flag) +{ + return _pthread_set_state(a, PTHREAD_SCOPE_SYSTEM, flag); +} + +int +pthread_attr_getscope (const pthread_attr_t *a, int *flag) +{ + *flag = _pthread_get_state(a, PTHREAD_SCOPE_SYSTEM); + return 0; +} + +int +pthread_attr_getstack (const pthread_attr_t *attr, void **stack, size_t *size) +{ + *stack = (char *) attr->stack - attr->s_size; + *size = attr->s_size; + return 0; +} + +int +pthread_attr_setstack (pthread_attr_t *attr, void *stack, size_t size) +{ + attr->s_size = size; + attr->stack = (char *) stack + size; + return 0; +} + +int +pthread_attr_getstackaddr (const pthread_attr_t *attr, void **stack) +{ + *stack = attr->stack; + return 0; +} + +int +pthread_attr_setstackaddr (pthread_attr_t *attr, void *stack) +{ + attr->stack = stack; + return 0; +} + +int +pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *size) +{ + *size = attr->s_size; + return 0; +} + +int +pthread_attr_setstacksize (pthread_attr_t *attr, size_t size) +{ + attr->s_size = size; + return 0; +} + +static void +test_cancel_locked (pthread_t t) +{ + struct _pthread_v *tv = __pth_gpointer_locked (t); + + if (!tv || tv->in_cancel || tv->ended != 0 || (tv->p_state & PTHREAD_CANCEL_ENABLE) == 0) + return; + if ((tv->p_state & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) + return; + if (WaitForSingleObject(tv->evStart, 0) != WAIT_OBJECT_0) + return; + pthread_mutex_unlock (&tv->p_clock); + _pthread_invoke_cancel(); +} + +int +pthread_setcancelstate (int state, int *oldstate) +{ + _pthread_v *t = __pthread_self_lite (); + + if (!t || (state & PTHREAD_CANCEL_ENABLE) != state) + return EINVAL; + + pthread_mutex_lock (&t->p_clock); + if (oldstate) + *oldstate = t->p_state & PTHREAD_CANCEL_ENABLE; + t->p_state &= ~PTHREAD_CANCEL_ENABLE; + t->p_state |= state; + test_cancel_locked (t->x); + pthread_mutex_unlock (&t->p_clock); + + return 0; +} + +int +pthread_setcanceltype (int type, int *oldtype) +{ + _pthread_v *t = __pthread_self_lite (); + + if (!t || (type & PTHREAD_CANCEL_ASYNCHRONOUS) != type) + return EINVAL; + + pthread_mutex_lock (&t->p_clock); + if (oldtype) + *oldtype = t->p_state & PTHREAD_CANCEL_ASYNCHRONOUS; + t->p_state &= ~PTHREAD_CANCEL_ASYNCHRONOUS; + t->p_state |= type; + test_cancel_locked (t->x); + pthread_mutex_unlock (&t->p_clock); + + return 0; +} + +void _fpreset (void); + +#if defined(__i386__) +/* Align ESP on 16-byte boundaries. */ +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) +__attribute__((force_align_arg_pointer)) +# endif +#endif +unsigned __stdcall +pthread_create_wrapper (void *args) +{ + unsigned rslt = 0; + struct _pthread_v *tv = (struct _pthread_v *)args; + + _fpreset(); + + pthread_mutex_lock (&mtx_pthr_locked); + pthread_mutex_lock (&tv->p_clock); + _pthread_once_raw(&_pthread_tls_once, pthread_tls_init); + TlsSetValue(_pthread_tls, tv); + tv->tid = GetCurrentThreadId(); + pthread_mutex_unlock (&tv->p_clock); + + + if (!setjmp(tv->jb)) + { + intptr_t trslt = (intptr_t) 128; + /* Provide to this thread a default exception handler. */ + #ifdef __SEH__ + asm ("\t.tl_start:\n"); + #endif /* Call function and save return value */ + pthread_mutex_unlock (&mtx_pthr_locked); + if (tv->func) + trslt = (intptr_t) tv->func(tv->ret_arg); + #ifdef __SEH__ + asm ("\tnop\n\t.tl_end: nop\n" +#ifdef __arm__ + "\t.seh_handler __C_specific_handler, %except\n" +#else + "\t.seh_handler __C_specific_handler, @except\n" +#endif + "\t.seh_handlerdata\n" + "\t.long 1\n" + "\t.rva .tl_start, .tl_end, _gnu_exception_handler ,.tl_end\n" + "\t.text" + ); + #endif + pthread_mutex_lock (&mtx_pthr_locked); + tv->ret_arg = (void*) trslt; + /* Clean up destructors */ + _pthread_cleanup_dest(tv->x); + } + else + pthread_mutex_lock (&mtx_pthr_locked); + + pthread_mutex_lock (&tv->p_clock); + rslt = (unsigned) (size_t) tv->ret_arg; + /* Make sure we free ourselves if we are detached */ + if (tv->evStart) + CloseHandle (tv->evStart); + tv->evStart = NULL; + if (!tv->h) + { + tv->valid = DEAD_THREAD; + pthread_mutex_unlock (&tv->p_clock); + pthread_mutex_destroy (&tv->p_clock); + push_pthread_mem (tv); + tv = NULL; + TlsSetValue (_pthread_tls, tv); + } + else + { + pthread_mutex_unlock (&tv->p_clock); + pthread_mutex_destroy (&tv->p_clock); + /* Reinitialise p_clock, since there may be attempts at + destroying it again in __dyn_tls_thread later on. */ + tv->p_clock = PTHREAD_MUTEX_INITIALIZER; + tv->ended = 1; + } + while (pthread_mutex_unlock (&mtx_pthr_locked) == 0) + Sleep (0); + _endthreadex (rslt); + return rslt; +} + +int +pthread_create (pthread_t *th, const pthread_attr_t *attr, void *(* func)(void *), void *arg) +{ + HANDLE thrd = NULL; + int redo = 0; + struct _pthread_v *tv; + unsigned int ssize = 0; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + if (attr && attr->s_size > UINT_MAX) + return EINVAL; + + if ((tv = pop_pthread_mem ()) == NULL) + return EAGAIN; + + if (th) + *th = tv->x; + + /* Save data in pthread_t */ + tv->ended = 0; + tv->ret_arg = arg; + tv->func = func; + tv->p_state = PTHREAD_DEFAULT_ATTR; + tv->h = INVALID_HANDLE_VALUE; + /* We retry it here a few times, as events are a limited resource ... */ + do + { + tv->evStart = CreateEvent (NULL, 1, 0, NULL); + if (tv->evStart != NULL) + break; + Sleep ((!redo ? 0 : 20)); + } + while (++redo <= 4); + + tv->p_clock = PTHREAD_MUTEX_INITIALIZER; + replace_spin_keys (&tv->spin_keys, new_spin_keys); + tv->valid = LIFE_THREAD; + tv->sched.sched_priority = THREAD_PRIORITY_NORMAL; + tv->sched_pol = SCHED_OTHER; + if (tv->evStart == NULL) + { + if (th) + memset (th, 0, sizeof (pthread_t)); + push_pthread_mem (tv); + return EAGAIN; + } + + if (attr) + { + int inh = 0; + tv->p_state = attr->p_state; + ssize = (unsigned int)attr->s_size; + pthread_attr_getinheritsched (attr, &inh); + if (inh) + { + tv->sched.sched_priority = __pthread_self_lite ()->sched.sched_priority; + } + else + tv->sched.sched_priority = attr->param.sched_priority; + } + + /* Make sure tv->h has value of INVALID_HANDLE_VALUE */ + _ReadWriteBarrier(); + + thrd = (HANDLE) _beginthreadex(NULL, ssize, pthread_create_wrapper, tv, 0x4/*CREATE_SUSPEND*/, NULL); + if (thrd == INVALID_HANDLE_VALUE) + thrd = 0; + /* Failed */ + if (!thrd) + { + if (tv->evStart) + CloseHandle (tv->evStart); + pthread_mutex_destroy (&tv->p_clock); + replace_spin_keys (&tv->spin_keys, new_spin_keys); + tv->evStart = NULL; + tv->h = 0; + if (th) + memset (th, 0, sizeof (pthread_t)); + push_pthread_mem (tv); + return EAGAIN; + } + { + int pr = tv->sched.sched_priority; + if (pr <= THREAD_PRIORITY_IDLE) { + pr = THREAD_PRIORITY_IDLE; + } else if (pr <= THREAD_PRIORITY_LOWEST) { + pr = THREAD_PRIORITY_LOWEST; + } else if (pr >= THREAD_PRIORITY_TIME_CRITICAL) { + pr = THREAD_PRIORITY_TIME_CRITICAL; + } else if (pr >= THREAD_PRIORITY_HIGHEST) { + pr = THREAD_PRIORITY_HIGHEST; + } + SetThreadPriority (thrd, pr); + } + ResetEvent (tv->evStart); + if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0) + { + tv->h = 0; + ResumeThread (thrd); + CloseHandle (thrd); + } + else + { + tv->h = thrd; + ResumeThread (thrd); + } + Sleep (0); + return 0; +} + +int +pthread_join (pthread_t t, void **res) +{ + DWORD dwFlags; + struct _pthread_v *tv = __pth_gpointer_locked (t); + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + if (!tv || tv->h == NULL || !GetHandleInformation(tv->h, &dwFlags)) + return ESRCH; + if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0) + return EINVAL; + if (pthread_equal(pthread_self(), t)) + return EDEADLK; + + /* pthread_testcancel (); */ + if (tv->ended == 0 || (tv->h != NULL && tv->h != INVALID_HANDLE_VALUE)) + WaitForSingleObject (tv->h, INFINITE); + CloseHandle (tv->h); + if (tv->evStart) + CloseHandle (tv->evStart); + tv->evStart = NULL; + /* Obtain return value */ + if (res) + *res = tv->ret_arg; + pthread_mutex_destroy (&tv->p_clock); + replace_spin_keys (&tv->spin_keys, new_spin_keys); + push_pthread_mem (tv); + + return 0; +} + +int +_pthread_tryjoin (pthread_t t, void **res) +{ + DWORD dwFlags; + struct _pthread_v *tv; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + pthread_mutex_lock (&mtx_pthr_locked); + tv = __pthread_get_pointer (t); + + if (!tv || tv->h == NULL || !GetHandleInformation(tv->h, &dwFlags)) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return ESRCH; + } + + if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return EINVAL; + } + if (pthread_equal(pthread_self(), t)) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return EDEADLK; + } + if(tv->ended == 0 && WaitForSingleObject(tv->h, 0)) + { + if (tv->ended == 0) + { + pthread_mutex_unlock (&mtx_pthr_locked); + /* pthread_testcancel (); */ + return EBUSY; + } + } + CloseHandle (tv->h); + if (tv->evStart) + CloseHandle (tv->evStart); + tv->evStart = NULL; + + /* Obtain return value */ + if (res) + *res = tv->ret_arg; + pthread_mutex_destroy (&tv->p_clock); + replace_spin_keys (&tv->spin_keys, new_spin_keys); + + push_pthread_mem (tv); + + pthread_mutex_unlock (&mtx_pthr_locked); + /* pthread_testcancel (); */ + return 0; +} + +int +pthread_detach (pthread_t t) +{ + int r = 0; + DWORD dwFlags; + struct _pthread_v *tv = __pth_gpointer_locked (t); + HANDLE dw; + pthread_spinlock_t new_spin_keys = PTHREAD_SPINLOCK_INITIALIZER; + + pthread_mutex_lock (&mtx_pthr_locked); + if (!tv || tv->h == NULL || !GetHandleInformation(tv->h, &dwFlags)) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return ESRCH; + } + if ((tv->p_state & PTHREAD_CREATE_DETACHED) != 0) + { + pthread_mutex_unlock (&mtx_pthr_locked); + return EINVAL; + } + /* if (tv->ended) r = ESRCH; */ + dw = tv->h; + tv->h = 0; + tv->p_state |= PTHREAD_CREATE_DETACHED; + _ReadWriteBarrier(); + if (dw) + { + CloseHandle (dw); + if (tv->ended) + { + if (tv->evStart) + CloseHandle (tv->evStart); + tv->evStart = NULL; + pthread_mutex_destroy (&tv->p_clock); + replace_spin_keys (&tv->spin_keys, new_spin_keys); + push_pthread_mem (tv); + } + } + pthread_mutex_unlock (&mtx_pthr_locked); + + return r; +} + +static int dummy_concurrency_level = 0; + +int +pthread_getconcurrency (void) +{ + return dummy_concurrency_level; +} + +int +pthread_setconcurrency (int new_level) +{ + dummy_concurrency_level = new_level; + return 0; +} + +int +pthread_setname_np (pthread_t thread, const char *name) +{ + struct _pthread_v *tv; + char *stored_name; + + if (name == NULL) + return EINVAL; + + tv = __pth_gpointer_locked (thread); + if (!tv || thread != tv->x || tv->in_cancel || tv->ended || tv->h == NULL + || tv->h == INVALID_HANDLE_VALUE) + return ESRCH; + + stored_name = strdup (name); + if (stored_name == NULL) + return ENOMEM; + + if (tv->thread_name != NULL) + free (tv->thread_name); + + tv->thread_name = stored_name; + SetThreadName (tv->tid, name); + return 0; +} + +int +pthread_getname_np (pthread_t thread, char *name, size_t len) +{ + HRESULT result; + struct _pthread_v *tv; + + if (name == NULL) + return EINVAL; + + tv = __pth_gpointer_locked (thread); + if (!tv || thread != tv->x || tv->in_cancel || tv->ended || tv->h == NULL + || tv->h == INVALID_HANDLE_VALUE) + return ESRCH; + + if (len < 1) + return ERANGE; + + if (tv->thread_name == NULL) + { + name[0] = '\0'; + return 0; + } + + if (strlen (tv->thread_name) >= len) + return ERANGE; + + result = StringCchCopyNA (name, len, tv->thread_name, len - 1); + if (SUCCEEDED (result)) + return 0; + + return ERANGE; +} diff --git a/src/thread.h b/src/thread.h new file mode 100644 index 0000000..5b88226 --- /dev/null +++ b/src/thread.h @@ -0,0 +1,79 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WIN_PTHREAD_H +#define WIN_PTHREAD_H + +#include +#include +#include "rwlock.h" + +#define LIFE_THREAD 0xBAB1F00D +#define DEAD_THREAD 0xDEADBEEF +#define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388) + +typedef struct _pthread_v _pthread_v; +struct _pthread_v +{ + unsigned int valid; + void *ret_arg; + void *(* func)(void *); + _pthread_cleanup *clean; + int nobreak; + HANDLE h; + HANDLE evStart; + pthread_mutex_t p_clock; + int cancelled : 2; + int in_cancel : 2; + int thread_noposix : 2; + unsigned int p_state; + unsigned int keymax; + void **keyval; + unsigned char *keyval_set; + char *thread_name; + pthread_spinlock_t spin_keys; + DWORD tid; + int rwlc; + pthread_rwlock_t rwlq[RWLS_PER_THREAD]; + int sched_pol; + int ended; + struct sched_param sched; + jmp_buf jb; + struct _pthread_v *next; + pthread_t x; /* Internal posix handle. */ +}; + +typedef struct __pthread_idlist { + struct _pthread_v *ptr; + pthread_t id; +} __pthread_idlist; + +int _pthread_tryjoin(pthread_t t, void **res); +void _pthread_setnobreak(int); +#ifdef WINPTHREAD_DBG +void thread_print_set(int state); +void thread_print(volatile pthread_t t, char *txt); +#endif +int __pthread_shallcancel(void); +struct _pthread_v *WINPTHREAD_API __pth_gpointer_locked (pthread_t id); + +#endif diff --git a/src/version.rc b/src/version.rc new file mode 100644 index 0000000..1648aed --- /dev/null +++ b/src/version.rc @@ -0,0 +1,66 @@ +/* + Copyright (c) 2011 mingw-w64 project + + 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 +#include "wpth_ver.h" + +#if defined(__MINGW64__) +# define WPTH_VERSIONINFO_COMMENT "GNU C build -- MinGW-w64 64-bit\0" +# define WPTH_VERSIONINFO_NAME "WinPthreadGC\0" +#elif defined(__MINGW32__) +# define WPTH_VERSIONINFO_COMMENT "GNU C build -- MinGW-w64 32-bit\0" +# define WPTH_VERSIONINFO_NAME "WinPthreadGC\0" +#else +# define WPTH_VERSIONINFO_COMMENT "MSVC build\0" +# define WPTH_VERSIONINFO_NAME "WinPthreadMS\0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION WPTH_VERSION + PRODUCTVERSION WPTH_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "POSIX WinThreads for Windows\0" + VALUE "ProductVersion", WPTH_VERSION_STRING + VALUE "FileVersion", WPTH_VERSION_STRING + VALUE "InternalName", WPTH_VERSIONINFO_NAME + VALUE "OriginalFilename", WPTH_VERSIONINFO_NAME + VALUE "CompanyName", "MinGW-W64 Project. All rights reserved.\0" + VALUE "LegalCopyright", "Copyright (C) MinGW-W64 Project Members 2010-2023\0" + VALUE "Licence", "MIT AND BSD-3-Clause\0" + VALUE "Info", "https://www.mingw-w64.org/\0" + VALUE "Comment", WPTH_VERSIONINFO_COMMENT + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/src/winpthread_internal.h b/src/winpthread_internal.h new file mode 100644 index 0000000..eb6838c --- /dev/null +++ b/src/winpthread_internal.h @@ -0,0 +1,27 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef WINPTHREAD_INTERNAL_H +#define WINPTHREAD_INTERNAL_H +WINPTHREAD_API struct _pthread_v * __pth_gpointer_locked (pthread_t id); +int pthread_delay_np_ms (DWORD to); +#endif /*WINPTHREAD_INTERNAL_H*/ diff --git a/src/wpth_ver.h b/src/wpth_ver.h new file mode 100644 index 0000000..1089a61 --- /dev/null +++ b/src/wpth_ver.h @@ -0,0 +1,29 @@ +/* + Copyright (c) 2011-2016 mingw-w64 project + + 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. +*/ + +#ifndef __WPTHREADS_VERSION__ +#define __WPTHREADS_VERSION__ + +#define WPTH_VERSION 1,0,0,0 +#define WPTH_VERSION_STRING "1, 0, 0, 0\0" + +#endif