Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -119,10 +119,11 @@ crc16.m \ crc32.m \ huffman_tree.m \ of_asprintf.m \ of_strptime.m \ + once.m \ pbkdf2.m \ scrypt.m \ ${UNICODE_M} \ ${USE_SRCS_FILES} \ ${USE_SRCS_PLUGINS} \ @@ -149,11 +150,14 @@ socket.m SRCS_THREADS = OFCondition.m \ OFMutex.m \ OFRecursiveMutex.m \ OFThreadPool.m \ - threading.m + condition.m \ + mutex.m \ + thread.m \ + tlskey.m SRCS_WINDOWS = OFWin32ConsoleStdIOStream.m \ OFWindowsRegistryKey.m INCLUDES_ATOMIC = atomic.h \ atomic_builtins.h \ Index: src/OFAutoreleasePool.m ================================================================== --- src/OFAutoreleasePool.m +++ src/OFAutoreleasePool.m @@ -19,11 +19,11 @@ #import "OFAutoreleasePool.h" #import "OFAutoreleasePool+Private.h" #if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) -# import "threading.h" +# import "tlskey.h" # # import "OFInitializationFailedException.h" #endif #define MAX_CACHE_SIZE 0x20 Index: src/OFBlock.m ================================================================== --- src/OFBlock.m +++ src/OFBlock.m @@ -32,11 +32,11 @@ #ifdef OF_HAVE_ATOMIC_OPS # import "atomic.h" #endif #ifdef OF_HAVE_THREADS -# import "threading.h" +# import "mutex.h" #endif typedef struct of_block_byref_t of_block_byref_t; struct of_block_byref_t { Class isa; Index: src/OFCondition.h ================================================================== --- src/OFCondition.h +++ src/OFCondition.h @@ -14,10 +14,12 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #import "OFMutex.h" + +#import "condition.h" OF_ASSUME_NONNULL_BEGIN @class OFDate; Index: src/OFMutex.h ================================================================== --- src/OFMutex.h +++ src/OFMutex.h @@ -16,11 +16,11 @@ */ #import "OFObject.h" #import "OFLocking.h" -#import "threading.h" +#import "mutex.h" OF_ASSUME_NONNULL_BEGIN /*! * @class OFMutex OFMutex.h ObjFW/OFMutex.h Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -58,11 +58,11 @@ #import "instance.h" #if defined(OF_HAVE_ATOMIC_OPS) # import "atomic.h" #elif defined(OF_HAVE_THREADS) -# import "threading.h" +# import "mutex.h" #endif #if defined(OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR) extern id of_forward(id, SEL, ...); extern struct stret of_forward_stret(id, SEL, ...); Index: src/OFRecursiveMutex.h ================================================================== --- src/OFRecursiveMutex.h +++ src/OFRecursiveMutex.h @@ -16,11 +16,11 @@ */ #import "OFObject.h" #import "OFLocking.h" -#import "threading.h" +#import "mutex.h" OF_ASSUME_NONNULL_BEGIN /*! * @class OFRecursiveMutex OFRecursiveMutex.h ObjFW/OFRecursiveMutex.h Index: src/OFSecureData.m ================================================================== --- src/OFSecureData.m +++ src/OFSecureData.m @@ -32,11 +32,11 @@ #import "OFInvalidArgumentException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #ifdef OF_HAVE_THREADS -# import "threading.h" +# import "tlskey.h" #endif #define CHUNK_SIZE 16 struct page { Index: src/OFSystemInfo.m ================================================================== --- src/OFSystemInfo.m +++ src/OFSystemInfo.m @@ -50,13 +50,11 @@ #import "OFLocale.h" #import "OFString.h" #import "OFNotImplementedException.h" -#ifdef OF_HAVE_THREADS -# import "threading.h" -#endif +#import "once.h" #if defined(OF_MACOS) || defined(OF_IOS) # ifdef HAVE_SYSDIR_H # include # endif @@ -340,36 +338,20 @@ return OBJFW_VERSION_MINOR; } + (OFString *)operatingSystemName { -#ifdef OF_HAVE_THREADS static of_once_t onceControl = OF_ONCE_INIT; of_once(&onceControl, initOperatingSystemName); -#else - static bool initialized = false; - if (!initialized) { - initOperatingSystemName(); - initialized = true; - } -#endif return operatingSystemName; } + (OFString *)operatingSystemVersion { -#ifdef OF_HAVE_THREADS static of_once_t onceControl = OF_ONCE_INIT; of_once(&onceControl, initOperatingSystemVersion); -#else - static bool initialized = false; - if (!initialized) { - initOperatingSystemVersion(); - initialized = true; - } -#endif return operatingSystemVersion; } #ifdef OF_HAVE_FILES Index: src/OFThread.h ================================================================== --- src/OFThread.h +++ src/OFThread.h @@ -18,11 +18,11 @@ #include #import "OFObject.h" #ifdef OF_HAVE_THREADS -# import "threading.h" +# import "thread.h" #endif OF_ASSUME_NONNULL_BEGIN /*! @file */ Index: src/OFThread.m ================================================================== --- src/OFThread.m +++ src/OFThread.m @@ -107,11 +107,11 @@ CloseLibrary(DOSBase); } #endif #if defined(OF_HAVE_THREADS) -# import "threading.h" +# import "tlskey.h" static of_tlskey_t threadSelfKey; static OFThread *mainThread; static void Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -243,12 +243,15 @@ # import "atomic.h" #endif #import "OFLocking.h" #import "OFThread.h" +#import "once.h" #ifdef OF_HAVE_THREADS -# import "threading.h" +# import "thread.h" +# import "mutex.h" +# import "condition.h" # import "OFThreadPool.h" # import "OFMutex.h" # import "OFRecursiveMutex.h" # import "OFCondition.h" #endif Index: src/autorelease.m ================================================================== --- src/autorelease.m +++ src/autorelease.m @@ -22,11 +22,11 @@ #import "OFObject.h" #import "OFSystemInfo.h" #if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) -# import "threading.h" +# import "tlskey.h" #endif #if defined(OF_HAVE_COMPILER_TLS) static thread_local id *objects = NULL; static thread_local id *top = NULL; ADDED src/condition.h Index: src/condition.h ================================================================== --- /dev/null +++ src/condition.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "objfw-defs.h" + +#include "platform.h" + +#if !defined(OF_HAVE_THREADS) || \ + (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS)) +# error No conditions available! +#endif + +/* For of_time_interval_t */ +#import "OFObject.h" + +#import "mutex.h" + +#if defined(OF_HAVE_PTHREADS) +# include +typedef pthread_cond_t of_condition_t; +#elif defined(OF_WINDOWS) +# include +typedef struct { + HANDLE event; + int count; +} of_condition_t; +#endif + +extern bool of_condition_new(of_condition_t *condition); +extern bool of_condition_signal(of_condition_t *condition); +extern bool of_condition_broadcast(of_condition_t *condition); +extern bool of_condition_wait(of_condition_t *condition, of_mutex_t *mutex); +extern bool of_condition_timed_wait(of_condition_t *condition, + of_mutex_t *mutex, of_time_interval_t timeout); +extern bool of_condition_free(of_condition_t *condition); ADDED src/condition.m Index: src/condition.m ================================================================== --- /dev/null +++ src/condition.m @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "condition.h" + +#if defined(OF_HAVE_PTHREADS) +# include "condition_pthread.m" +#elif defined(OF_WINDOWS) +# include "condition_winapi.m" +#endif ADDED src/condition_pthread.m Index: src/condition_pthread.m ================================================================== --- /dev/null +++ src/condition_pthread.m @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include + +bool +of_condition_new(of_condition_t *condition) +{ + return (pthread_cond_init(condition, NULL) == 0); +} + +bool +of_condition_signal(of_condition_t *condition) +{ + return (pthread_cond_signal(condition) == 0); +} + +bool +of_condition_broadcast(of_condition_t *condition) +{ + return (pthread_cond_broadcast(condition) == 0); +} + +bool +of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) +{ + return (pthread_cond_wait(condition, mutex) == 0); +} + +bool +of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, + of_time_interval_t timeout) +{ + struct timespec ts; + + ts.tv_sec = (time_t)timeout; + ts.tv_nsec = lrint((timeout - ts.tv_sec) * 1000000000); + + return (pthread_cond_timedwait(condition, mutex, &ts) == 0); +} + +bool +of_condition_free(of_condition_t *condition) +{ + return (pthread_cond_destroy(condition) == 0); +} ADDED src/condition_winapi.m Index: src/condition_winapi.m ================================================================== --- /dev/null +++ src/condition_winapi.m @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +bool +of_condition_new(of_condition_t *condition) +{ + condition->count = 0; + + if ((condition->event = CreateEvent(NULL, FALSE, 0, NULL)) == NULL) + return false; + + return true; +} + +bool +of_condition_signal(of_condition_t *condition) +{ + return SetEvent(condition->event); +} + +bool +of_condition_broadcast(of_condition_t *condition) +{ + for (int i = 0; i < condition->count; i++) + if (!SetEvent(condition->event)) + return false; + + return true; +} + +bool +of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) +{ + DWORD status; + + if (!of_mutex_unlock(mutex)) + return false; + + of_atomic_int_inc(&condition->count); + status = WaitForSingleObject(condition->event, INFINITE); + of_atomic_int_dec(&condition->count); + + if (!of_mutex_lock(mutex)) + return false; + + return (status == WAIT_OBJECT_0); +} + +bool +of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, + of_time_interval_t timeout) +{ + DWORD status; + + if (!of_mutex_unlock(mutex)) + return false; + + of_atomic_int_inc(&condition->count); + status = WaitForSingleObject(condition->event, timeout * 1000); + of_atomic_int_dec(&condition->count); + + if (!of_mutex_lock(mutex)) + return false; + + return (status == WAIT_OBJECT_0); +} + +bool +of_condition_free(of_condition_t *condition) +{ + if (condition->count != 0) + return false; + + return CloseHandle(condition->event); +} Index: src/exceptions/OFException.m ================================================================== --- src/exceptions/OFException.m +++ src/exceptions/OFException.m @@ -34,11 +34,11 @@ #import "OFInitializationFailedException.h" #import "OFLockFailedException.h" #import "OFUnlockFailedException.h" #if !defined(HAVE_STRERROR_R) && defined(OF_HAVE_THREADS) -# import "threading.h" +# import "mutex.h" #endif #if defined(OF_WINDOWS) && defined(OF_HAVE_SOCKETS) # include #endif ADDED src/mutex.h Index: src/mutex.h ================================================================== --- /dev/null +++ src/mutex.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "objfw-defs.h" + +#include "platform.h" + +#if !defined(OF_HAVE_THREADS) || \ + (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS)) +# error No mutexes available! +#endif + +#import "macros.h" + +#if defined(OF_HAVE_PTHREADS) +# include +typedef pthread_mutex_t of_mutex_t; +#elif defined(OF_WINDOWS) +# include +typedef CRITICAL_SECTION of_mutex_t; +#endif + +#if defined(OF_HAVE_ATOMIC_OPS) +# import "atomic.h" +typedef volatile int of_spinlock_t; +# define OF_SPINCOUNT 10 +#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) +typedef pthread_spinlock_t of_spinlock_t; +#else +typedef of_mutex_t of_spinlock_t; +#endif + +#ifdef OF_HAVE_SCHED_YIELD +# include +#endif + +#if defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) || defined(OF_WINDOWS) +# define of_rmutex_t of_mutex_t +#else +typedef struct { + of_mutex_t mutex; + of_tlskey_t count; +} of_rmutex_t; +#endif + +extern bool of_mutex_new(of_mutex_t *mutex); +extern bool of_mutex_lock(of_mutex_t *mutex); +extern bool of_mutex_trylock(of_mutex_t *mutex); +extern bool of_mutex_unlock(of_mutex_t *mutex); +extern bool of_mutex_free(of_mutex_t *mutex); +extern bool of_rmutex_new(of_rmutex_t *rmutex); +extern bool of_rmutex_lock(of_rmutex_t *rmutex); +extern bool of_rmutex_trylock(of_rmutex_t *rmutex); +extern bool of_rmutex_unlock(of_rmutex_t *rmutex); +extern bool of_rmutex_free(of_rmutex_t *rmutex); + +/* Spinlocks are inlined for performance. */ + +static OF_INLINE void +of_thread_yield(void) +{ +#if defined(OF_HAVE_SCHED_YIELD) + sched_yield(); +#elif defined(OF_WINDOWS) + Sleep(0); +#endif +} + +static OF_INLINE bool +of_spinlock_new(of_spinlock_t *spinlock) +{ +#if defined(OF_HAVE_ATOMIC_OPS) + *spinlock = 0; + return true; +#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) + return (pthread_spin_init(spinlock, 0) == 0); +#else + return of_mutex_new(spinlock); +#endif +} + +static OF_INLINE bool +of_spinlock_trylock(of_spinlock_t *spinlock) +{ +#if defined(OF_HAVE_ATOMIC_OPS) + if (of_atomic_int_cmpswap(spinlock, 0, 1)) { + of_memory_barrier_acquire(); + return true; + } + + return false; +#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) + return (pthread_spin_trylock(spinlock) == 0); +#else + return of_mutex_trylock(spinlock); +#endif +} + +static OF_INLINE bool +of_spinlock_lock(of_spinlock_t *spinlock) +{ +#if defined(OF_HAVE_ATOMIC_OPS) + size_t i; + + for (i = 0; i < OF_SPINCOUNT; i++) + if (of_spinlock_trylock(spinlock)) + return true; + + while (!of_spinlock_trylock(spinlock)) + of_thread_yield(); + + return true; +#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) + return (pthread_spin_lock(spinlock) == 0); +#else + return of_mutex_lock(spinlock); +#endif +} + +static OF_INLINE bool +of_spinlock_unlock(of_spinlock_t *spinlock) +{ +#if defined(OF_HAVE_ATOMIC_OPS) + bool ret = of_atomic_int_cmpswap(spinlock, 1, 0); + + of_memory_barrier_release(); + + return ret; +#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) + return (pthread_spin_unlock(spinlock) == 0); +#else + return of_mutex_unlock(spinlock); +#endif +} + +static OF_INLINE bool +of_spinlock_free(of_spinlock_t *spinlock) +{ +#if defined(OF_HAVE_ATOMIC_OPS) + return true; +#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) + return (pthread_spin_destroy(spinlock) == 0); +#else + return of_mutex_free(spinlock); +#endif +} ADDED src/mutex.m Index: src/mutex.m ================================================================== --- /dev/null +++ src/mutex.m @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "mutex.h" + +#if defined(OF_HAVE_PTHREADS) +# include "mutex_pthread.m" +#elif defined(OF_WINDOWS) +# include "mutex_winapi.m" +#endif + +#if !defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) && !defined(OF_WINDOWS) +bool +of_rmutex_new(of_rmutex_t *rmutex) +{ + if (!of_mutex_new(&rmutex->mutex)) + return false; + + if (!of_tlskey_new(&rmutex->count)) + return false; + + return true; +} + +bool +of_rmutex_lock(of_rmutex_t *rmutex) +{ + uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); + + if (count > 0) { + if (!of_tlskey_set(rmutex->count, (void *)(count + 1))) + return false; + + return true; + } + + if (!of_mutex_lock(&rmutex->mutex)) + return false; + + if (!of_tlskey_set(rmutex->count, (void *)1)) { + of_mutex_unlock(&rmutex->mutex); + return false; + } + + return true; +} + +bool +of_rmutex_trylock(of_rmutex_t *rmutex) +{ + uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); + + if (count > 0) { + if (!of_tlskey_set(rmutex->count, (void *)(count + 1))) + return false; + + return true; + } + + if (!of_mutex_trylock(&rmutex->mutex)) + return false; + + if (!of_tlskey_set(rmutex->count, (void *)1)) { + of_mutex_unlock(&rmutex->mutex); + return false; + } + + return true; +} + +bool +of_rmutex_unlock(of_rmutex_t *rmutex) +{ + uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); + + if (count > 1) { + if (!of_tlskey_set(rmutex->count, (void *)(count - 1))) + return false; + + return true; + } + + if (!of_tlskey_set(rmutex->count, (void *)0)) + return false; + + if (!of_mutex_unlock(&rmutex->mutex)) + return false; + + return true; +} + +bool +of_rmutex_free(of_rmutex_t *rmutex) +{ + if (!of_mutex_free(&rmutex->mutex)) + return false; + + if (!of_tlskey_free(rmutex->count)) + return false; + + return true; +} +#endif ADDED src/mutex_pthread.m Index: src/mutex_pthread.m ================================================================== --- /dev/null +++ src/mutex_pthread.m @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +bool +of_mutex_new(of_mutex_t *mutex) +{ + return (pthread_mutex_init(mutex, NULL) == 0); +} + +bool +of_mutex_lock(of_mutex_t *mutex) +{ + return (pthread_mutex_lock(mutex) == 0); +} + +bool +of_mutex_trylock(of_mutex_t *mutex) +{ + return (pthread_mutex_trylock(mutex) == 0); +} + +bool +of_mutex_unlock(of_mutex_t *mutex) +{ + return (pthread_mutex_unlock(mutex) == 0); +} + +bool +of_mutex_free(of_mutex_t *mutex) +{ + return (pthread_mutex_destroy(mutex) == 0); +} + +#ifdef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES +bool +of_rmutex_new(of_rmutex_t *rmutex) +{ + pthread_mutexattr_t attr; + + if (pthread_mutexattr_init(&attr) != 0) + return false; + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) + return false; + + if (pthread_mutex_init(rmutex, &attr) != 0) + return false; + + if (pthread_mutexattr_destroy(&attr) != 0) + return false; + + return true; +} + +bool +of_rmutex_lock(of_rmutex_t *rmutex) +{ + return of_mutex_lock(rmutex); +} + +bool +of_rmutex_trylock(of_rmutex_t *rmutex) +{ + return of_mutex_trylock(rmutex); +} + +bool +of_rmutex_unlock(of_rmutex_t *rmutex) +{ + return of_mutex_unlock(rmutex); +} + +bool +of_rmutex_free(of_rmutex_t *rmutex) +{ + return of_mutex_free(rmutex); +} +#endif ADDED src/mutex_winapi.m Index: src/mutex_winapi.m ================================================================== --- /dev/null +++ src/mutex_winapi.m @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +bool +of_mutex_new(of_mutex_t *mutex) +{ + InitializeCriticalSection(mutex); + + return true; +} + +bool +of_mutex_lock(of_mutex_t *mutex) +{ + EnterCriticalSection(mutex); + + return true; +} + +bool +of_mutex_trylock(of_mutex_t *mutex) +{ + return TryEnterCriticalSection(mutex); +} + +bool +of_mutex_unlock(of_mutex_t *mutex) +{ + LeaveCriticalSection(mutex); + + return true; +} + +bool +of_mutex_free(of_mutex_t *mutex) +{ + DeleteCriticalSection(mutex); + + return true; +} + +bool +of_rmutex_new(of_rmutex_t *rmutex) +{ + return of_mutex_new(rmutex); +} + +bool +of_rmutex_lock(of_rmutex_t *rmutex) +{ + return of_mutex_lock(rmutex); +} + +bool +of_rmutex_trylock(of_rmutex_t *rmutex) +{ + return of_mutex_trylock(rmutex); +} + +bool +of_rmutex_unlock(of_rmutex_t *rmutex) +{ + return of_mutex_unlock(rmutex); +} + +bool +of_rmutex_free(of_rmutex_t *rmutex) +{ + return of_mutex_free(rmutex); +} ADDED src/once.h Index: src/once.h ================================================================== --- /dev/null +++ src/once.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "objfw-defs.h" + +#include "platform.h" + +#if defined(OF_HAVE_PTHREADS) +# include +typedef pthread_once_t of_once_t; +# define OF_ONCE_INIT PTHREAD_ONCE_INIT +#elif defined(OF_HAVE_ATOMIC_OPS) +typedef volatile int of_once_t; +# define OF_ONCE_INIT 0 +#elif !defined(OF_HAVE_THREADS) +typedef int of_once_t; +# define OF_ONCE_INIT 0 +#endif + +extern void of_once(of_once_t *control, void (*func)(void)); ADDED src/once.m Index: src/once.m ================================================================== --- /dev/null +++ src/once.m @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "once.h" + +#if defined(OF_HAVE_THREADS) && defined(OF_HAVE_ATOMIC_OPS) +# import "atomic.h" +# import "mutex.h" +#endif + +void +of_once(of_once_t *control, void (*func)(void)) +{ +#if !defined(OF_HAVE_THREADS) + if (*control == 0) { + *control = 1; + func(); + } +#elif defined(OF_HAVE_PTHREADS) + pthread_once(control, func); +#elif defined(OF_HAVE_ATOMIC_OPS) + if (of_atomic_int_cmpswap(control, 0, 1)) { + func(); + + of_memory_barrier(); + + of_atomic_int_inc(control); + } else + while (*control == 1) + of_thread_yield(); +#else +# error No of_once available +#endif +} Index: src/runtime/Makefile ================================================================== --- src/runtime/Makefile +++ src/runtime/Makefile @@ -26,11 +26,12 @@ sparsearray.m \ static-instances.m \ synchronized.m \ ${USE_SRCS_THREADS} SRCS_THREADS = threading.m \ - ../threading.m + ../mutex.m \ + ../once.m INCLUDES = ObjFWRT.h includesubdir = ObjFWRT OBJS_EXTRA = ${LOOKUP_ASM_LOOKUP_ASM_A} LIB_OBJS_EXTRA = ${LOOKUP_ASM_LOOKUP_ASM_LIB_A} Index: src/runtime/arc.m ================================================================== --- src/runtime/arc.m +++ src/runtime/arc.m @@ -18,16 +18,16 @@ #include "config.h" #import "ObjFWRT.h" #import "private.h" -#ifdef OF_HAVE_THREADS -# import "threading.h" -#endif - #import "OFObject.h" #import "OFBlock.h" + +#ifdef OF_HAVE_THREADS +# import "mutex.h" +#endif struct weak_ref { id **locations; size_t count; }; Index: src/runtime/exception.m ================================================================== --- src/runtime/exception.m +++ src/runtime/exception.m @@ -26,11 +26,11 @@ #import "ObjFWRT.h" #import "private.h" #import "macros.h" #ifdef OF_HAVE_THREADS -# include "threading.h" +# include "mutex.h" #endif #ifdef HAVE_SEH_EXCEPTIONS # include #endif Index: src/runtime/property.m ================================================================== --- src/runtime/property.m +++ src/runtime/property.m @@ -23,11 +23,11 @@ #import "private.h" #import "OFObject.h" #ifdef OF_HAVE_THREADS -# import "threading.h" +# import "mutex.h" # define NUM_SPINLOCKS 8 /* needs to be a power of 2 */ # define SPINLOCK_HASH(p) ((unsigned)((uintptr_t)p >> 4) & (NUM_SPINLOCKS - 1)) static of_spinlock_t spinlocks[NUM_SPINLOCKS]; #endif Index: src/runtime/synchronized.m ================================================================== --- src/runtime/synchronized.m +++ src/runtime/synchronized.m @@ -22,11 +22,11 @@ #import "ObjFWRT.h" #import "private.h" #ifdef OF_HAVE_THREADS -# import "threading.h" +# import "mutex.h" static struct lock_s { id object; int count; of_rmutex_t rmutex; Index: src/runtime/threading.m ================================================================== --- src/runtime/threading.m +++ src/runtime/threading.m @@ -20,11 +20,12 @@ #include #include #import "ObjFWRT.h" #import "private.h" -#import "threading.h" +#import "mutex.h" +#import "once.h" static of_rmutex_t globalMutex; static void init(void) Index: src/socket.m ================================================================== --- src/socket.m +++ src/socket.m @@ -35,12 +35,13 @@ #import "OFUnlockFailedException.h" #import "socket.h" #import "socket_helpers.h" #ifdef OF_HAVE_THREADS -# include "threading.h" +# include "mutex.h" #endif +#include "once.h" #ifdef OF_NINTENDO_3DS # include <3ds/types.h> # include <3ds/services/soc.h> #endif @@ -87,20 +88,12 @@ } bool of_socket_init() { -#ifdef OF_HAVE_THREADS static of_once_t onceControl = OF_ONCE_INIT; of_once(&onceControl, init); -#else - static bool initialized = false; - if (!initialized) { - init(); - initialized = true; - } -#endif return initSuccessful; } int ADDED src/thread.h Index: src/thread.h ================================================================== --- /dev/null +++ src/thread.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "objfw-defs.h" + +#include "platform.h" + +#if !defined(OF_HAVE_THREADS) || \ + (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS)) +# error No threads available! +#endif + +#import "macros.h" + +#if defined(OF_HAVE_PTHREADS) +# include +typedef pthread_t of_thread_t; +#elif defined(OF_WINDOWS) +# include +typedef HANDLE of_thread_t; +#endif + +typedef struct of_thread_attr_t { + float priority; + size_t stackSize; +} of_thread_attr_t; + +#if defined(OF_HAVE_PTHREADS) +# define of_thread_is_current(t) pthread_equal(t, pthread_self()) +# define of_thread_current pthread_self +#elif defined(OF_WINDOWS) +# define of_thread_is_current(t) (t == GetCurrentThread()) +# define of_thread_current GetCurrentThread +#endif + +extern bool of_thread_attr_init(of_thread_attr_t *attr); +extern bool of_thread_new(of_thread_t *thread, void (*function)(id), id object, + const of_thread_attr_t *attr); +extern void of_thread_set_name(const char *name); +extern bool of_thread_join(of_thread_t thread); +extern bool of_thread_detach(of_thread_t thread); ADDED src/thread.m Index: src/thread.m ================================================================== --- /dev/null +++ src/thread.m @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "thread.h" + +#if defined(OF_HAVE_PTHREADS) +# include "thread_pthread.m" +#elif defined(OF_WINDOWS) +# include "thread_winapi.m" +#endif ADDED src/thread_pthread.m Index: src/thread_pthread.m ================================================================== --- /dev/null +++ src/thread_pthread.m @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#ifdef HAVE_PTHREAD_NP_H +# include +#endif + +#ifdef OF_HAIKU +# include +#endif + +#import "macros.h" + +static int minPrio = 0, maxPrio = 0, normalPrio = 0; + +struct thread_ctx { + void (*function)(id object); + id object; +}; + +/* + * This is done here to make sure this is done as early as possible in the main + * thread. + */ +OF_CONSTRUCTOR() +{ + pthread_attr_t pattr; + + if (pthread_attr_init(&pattr) == 0) { +#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY + int policy; +#endif + struct sched_param param; + +#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY + if (pthread_attr_getschedpolicy(&pattr, &policy) == 0) { + minPrio = sched_get_priority_min(policy); + maxPrio = sched_get_priority_max(policy); + + if (minPrio == -1 || maxPrio == -1) + minPrio = maxPrio = 0; + } + + if (pthread_attr_getschedparam(&pattr, ¶m) != 0) + normalPrio = param.sched_priority; + else + minPrio = maxPrio = 0; + + pthread_attr_destroy(&pattr); +#endif + } +} + +static void * +functionWrapper(void *data) +{ + struct thread_ctx *ctx = data; + + pthread_cleanup_push(free, data); + + ctx->function(ctx->object); + + pthread_cleanup_pop(1); + return NULL; +} + +bool +of_thread_attr_init(of_thread_attr_t *attr) +{ + pthread_attr_t pattr; + + if (pthread_attr_init(&pattr) != 0) + return false; + + @try { + attr->priority = 0; + + if (pthread_attr_getstacksize(&pattr, &attr->stackSize) != 0) + return false; + } @finally { + pthread_attr_destroy(&pattr); + } + + return true; +} + +bool +of_thread_new(of_thread_t *thread, void (*function)(id), id object, + const of_thread_attr_t *attr) +{ + bool ret; + pthread_attr_t pattr; + + if (pthread_attr_init(&pattr) != 0) + return false; + + @try { + struct thread_ctx *ctx; + + if (attr != NULL) { + struct sched_param param; + + if (attr->priority < -1 || attr->priority > 1) + return false; + +#ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED + if (pthread_attr_setinheritsched(&pattr, + PTHREAD_EXPLICIT_SCHED) != 0) + return false; +#endif + + if (attr->priority < 0) { + param.sched_priority = minPrio + + (1.0f + attr->priority) * + (normalPrio - minPrio); + } else + param.sched_priority = normalPrio + + attr->priority * (maxPrio - normalPrio); + + if (pthread_attr_setschedparam(&pattr, ¶m) != 0) + return false; + + if (attr->stackSize > 0) { + if (pthread_attr_setstacksize(&pattr, + attr->stackSize) != 0) + return false; + } + } + + if ((ctx = malloc(sizeof(*ctx))) == NULL) + return false; + + ctx->function = function; + ctx->object = object; + + ret = (pthread_create(thread, &pattr, + functionWrapper, ctx) == 0); + } @finally { + pthread_attr_destroy(&pattr); + } + + return ret; +} + +bool +of_thread_join(of_thread_t thread) +{ + void *ret; + + if (pthread_join(thread, &ret) != 0) + return false; + +#ifdef PTHREAD_CANCELED + return (ret != PTHREAD_CANCELED); +#else + return true; +#endif +} + +bool +of_thread_detach(of_thread_t thread) +{ + return (pthread_detach(thread) == 0); +} + +void +of_thread_set_name(const char *name) +{ +#if defined(OF_HAIKU) + rename_thread(find_thread(NULL), name); +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + pthread_set_name_np(pthread_self(), name); +#elif defined(HAVE_PTHREAD_SETNAME_NP) +# if defined(OF_MACOS) || defined(OF_IOS) + pthread_setname_np(name); +# elif defined(__GLIBC__) + char buffer[16]; + + strncpy(buffer, name, 15); + buffer[15] = 0; + + pthread_setname_np(pthread_self(), buffer); +# endif +#endif +} ADDED src/thread_winapi.m Index: src/thread_winapi.m ================================================================== --- /dev/null +++ src/thread_winapi.m @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "macros.h" + +bool +of_thread_attr_init(of_thread_attr_t *attr) +{ + attr->priority = 0; + attr->stackSize = 0; + + return true; +} + +bool +of_thread_new(of_thread_t *thread, void (*function)(id), id object, + const of_thread_attr_t *attr) +{ + *thread = CreateThread(NULL, (attr != NULL ? attr->stackSize : 0), + (LPTHREAD_START_ROUTINE)function, (void *)object, 0, NULL); + + if (thread == NULL) + return false; + + if (attr != NULL && attr->priority != 0) { + DWORD priority; + + if (attr->priority < -1 || attr->priority > 1) + return false; + + if (attr->priority < 0) + priority = THREAD_PRIORITY_LOWEST + + (1.0 + attr->priority) * + (THREAD_PRIORITY_NORMAL - THREAD_PRIORITY_LOWEST); + else + priority = THREAD_PRIORITY_NORMAL + + attr->priority * + (THREAD_PRIORITY_HIGHEST - THREAD_PRIORITY_NORMAL); + + if (!SetThreadPriority(*thread, priority)) + return false; + } + + return true; +} + +bool +of_thread_join(of_thread_t thread) +{ + if (WaitForSingleObject(thread, INFINITE)) + return false; + + CloseHandle(thread); + + return true; +} + +bool +of_thread_detach(of_thread_t thread) +{ + /* FIXME */ + return true; +} + +void +of_thread_set_name(const char *name) +{ +} DELETED src/threading.h Index: src/threading.h ================================================================== --- src/threading.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "objfw-defs.h" - -#include "platform.h" - -#if !defined(OF_HAVE_THREADS) || \ - (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS)) -# error No threads available! -#endif - -#include - -#import "OFObject.h" - -#if defined(OF_HAVE_PTHREADS) -# include -typedef pthread_t of_thread_t; -typedef pthread_key_t of_tlskey_t; -typedef pthread_mutex_t of_mutex_t; -typedef pthread_cond_t of_condition_t; -typedef pthread_once_t of_once_t; -# define OF_ONCE_INIT PTHREAD_ONCE_INIT -#elif defined(OF_WINDOWS) -# include -typedef HANDLE of_thread_t; -typedef DWORD of_tlskey_t; -typedef CRITICAL_SECTION of_mutex_t; -typedef struct { - HANDLE event; - int count; -} of_condition_t; -typedef volatile int of_once_t; -# define OF_ONCE_INIT 0 -#else -# error No threads available! -#endif - -#if defined(OF_HAVE_ATOMIC_OPS) -# import "atomic.h" -typedef volatile int of_spinlock_t; -# define OF_SPINCOUNT 10 -#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) -typedef pthread_spinlock_t of_spinlock_t; -#else -typedef of_mutex_t of_spinlock_t; -#endif - -#ifdef OF_HAVE_SCHED_YIELD -# include -#endif - -#if defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) || defined(OF_WINDOWS) -# define of_rmutex_t of_mutex_t -#else -typedef struct { - of_mutex_t mutex; - of_tlskey_t count; -} of_rmutex_t; -#endif - -typedef struct of_thread_attr_t { - float priority; - size_t stackSize; -} of_thread_attr_t; - -#if defined(OF_HAVE_PTHREADS) -# define of_thread_is_current(t) pthread_equal(t, pthread_self()) -# define of_thread_current pthread_self -#elif defined(OF_WINDOWS) -# define of_thread_is_current(t) (t == GetCurrentThread()) -# define of_thread_current GetCurrentThread -#else -# error of_thread_is_current not implemented! -# error of_thread_current not implemented! -#endif - -extern bool of_thread_attr_init(of_thread_attr_t *attr); -extern bool of_thread_new(of_thread_t *thread, void (*function)(id), id object, - const of_thread_attr_t *attr); -extern void of_thread_set_name(const char *name); -extern bool of_thread_join(of_thread_t thread); -extern bool of_thread_detach(of_thread_t thread); -extern void of_once(of_once_t *control, void (*func)(void)); -extern bool of_tlskey_new(of_tlskey_t *key); -extern bool of_tlskey_free(of_tlskey_t key); -extern bool of_mutex_new(of_mutex_t *mutex); -extern bool of_mutex_lock(of_mutex_t *mutex); -extern bool of_mutex_trylock(of_mutex_t *mutex); -extern bool of_mutex_unlock(of_mutex_t *mutex); -extern bool of_mutex_free(of_mutex_t *mutex); -extern bool of_rmutex_new(of_rmutex_t *rmutex); -extern bool of_rmutex_lock(of_rmutex_t *rmutex); -extern bool of_rmutex_trylock(of_rmutex_t *rmutex); -extern bool of_rmutex_unlock(of_rmutex_t *rmutex); -extern bool of_rmutex_free(of_rmutex_t *rmutex); -extern bool of_condition_new(of_condition_t *condition); -extern bool of_condition_signal(of_condition_t *condition); -extern bool of_condition_broadcast(of_condition_t *condition); -extern bool of_condition_wait(of_condition_t *condition, of_mutex_t *mutex); -extern bool of_condition_timed_wait(of_condition_t *condition, - of_mutex_t *mutex, of_time_interval_t timeout); -extern bool of_condition_free(of_condition_t *condition); - -/* TLS keys and spinlocks are inlined for performance. */ - -#if defined(OF_HAVE_PTHREADS) -static OF_INLINE void * -of_tlskey_get(of_tlskey_t key) -{ - return pthread_getspecific(key); -} - -static OF_INLINE bool -of_tlskey_set(of_tlskey_t key, void *ptr) -{ - return (pthread_setspecific(key, ptr) == 0); -} -#elif defined(OF_WINDOWS) -static OF_INLINE void * -of_tlskey_get(of_tlskey_t key) -{ - return TlsGetValue(key); -} - -static OF_INLINE bool -of_tlskey_set(of_tlskey_t key, void *ptr) -{ - return TlsSetValue(key, ptr); -} -#else -# error No thread local storage available! -#endif - -static OF_INLINE void -of_thread_yield(void) -{ -#if defined(OF_HAVE_SCHED_YIELD) - sched_yield(); -#elif defined(OF_WINDOWS) - Sleep(0); -#endif -} - -static OF_INLINE bool -of_spinlock_new(of_spinlock_t *spinlock) -{ -#if defined(OF_HAVE_ATOMIC_OPS) - *spinlock = 0; - return true; -#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) - return (pthread_spin_init(spinlock, 0) == 0); -#else - return of_mutex_new(spinlock); -#endif -} - -static OF_INLINE bool -of_spinlock_trylock(of_spinlock_t *spinlock) -{ -#if defined(OF_HAVE_ATOMIC_OPS) - if (of_atomic_int_cmpswap(spinlock, 0, 1)) { - of_memory_barrier_acquire(); - return true; - } - - return false; -#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) - return (pthread_spin_trylock(spinlock) == 0); -#else - return of_mutex_trylock(spinlock); -#endif -} - -static OF_INLINE bool -of_spinlock_lock(of_spinlock_t *spinlock) -{ -#if defined(OF_HAVE_ATOMIC_OPS) - size_t i; - - for (i = 0; i < OF_SPINCOUNT; i++) - if (of_spinlock_trylock(spinlock)) - return true; - - while (!of_spinlock_trylock(spinlock)) - of_thread_yield(); - - return true; -#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) - return (pthread_spin_lock(spinlock) == 0); -#else - return of_mutex_lock(spinlock); -#endif -} - -static OF_INLINE bool -of_spinlock_unlock(of_spinlock_t *spinlock) -{ -#if defined(OF_HAVE_ATOMIC_OPS) - bool ret = of_atomic_int_cmpswap(spinlock, 1, 0); - - of_memory_barrier_release(); - - return ret; -#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) - return (pthread_spin_unlock(spinlock) == 0); -#else - return of_mutex_unlock(spinlock); -#endif -} - -static OF_INLINE bool -of_spinlock_free(of_spinlock_t *spinlock) -{ -#if defined(OF_HAVE_ATOMIC_OPS) - return true; -#elif defined(OF_HAVE_PTHREAD_SPINLOCKS) - return (pthread_spin_destroy(spinlock) == 0); -#else - return of_mutex_free(spinlock); -#endif -} DELETED src/threading.m Index: src/threading.m ================================================================== --- src/threading.m +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#import "threading.h" - -#if defined(OF_HAVE_PTHREADS) -# include "threading_pthread.m" -#elif defined(OF_WINDOWS) -# include "threading_winapi.m" -#else -# error No threads available! -#endif - -#ifndef OF_HAVE_PTHREADS -void -of_once(of_once_t *control, void (*func)(void)) -{ - if (of_atomic_int_cmpswap(control, 0, 1)) { - func(); - - of_memory_barrier(); - - of_atomic_int_inc(control); - } else - while (*control == 1) - of_thread_yield(); -} -#endif - -#if !defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) && !defined(OF_WINDOWS) -bool -of_rmutex_new(of_rmutex_t *rmutex) -{ - if (!of_mutex_new(&rmutex->mutex)) - return false; - - if (!of_tlskey_new(&rmutex->count)) - return false; - - return true; -} - -bool -of_rmutex_lock(of_rmutex_t *rmutex) -{ - uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); - - if (count > 0) { - if (!of_tlskey_set(rmutex->count, (void *)(count + 1))) - return false; - - return true; - } - - if (!of_mutex_lock(&rmutex->mutex)) - return false; - - if (!of_tlskey_set(rmutex->count, (void *)1)) { - of_mutex_unlock(&rmutex->mutex); - return false; - } - - return true; -} - -bool -of_rmutex_trylock(of_rmutex_t *rmutex) -{ - uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); - - if (count > 0) { - if (!of_tlskey_set(rmutex->count, (void *)(count + 1))) - return false; - - return true; - } - - if (!of_mutex_trylock(&rmutex->mutex)) - return false; - - if (!of_tlskey_set(rmutex->count, (void *)1)) { - of_mutex_unlock(&rmutex->mutex); - return false; - } - - return true; -} - -bool -of_rmutex_unlock(of_rmutex_t *rmutex) -{ - uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); - - if (count > 1) { - if (!of_tlskey_set(rmutex->count, (void *)(count - 1))) - return false; - - return true; - } - - if (!of_tlskey_set(rmutex->count, (void *)0)) - return false; - - if (!of_mutex_unlock(&rmutex->mutex)) - return false; - - return true; -} - -bool -of_rmutex_free(of_rmutex_t *rmutex) -{ - if (!of_mutex_free(&rmutex->mutex)) - return false; - - if (!of_tlskey_free(rmutex->count)) - return false; - - return true; -} -#endif DELETED src/threading_pthread.m Index: src/threading_pthread.m ================================================================== --- src/threading_pthread.m +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#ifdef HAVE_PTHREAD_NP_H -# include -#endif - -#import "macros.h" - -#ifdef OF_HAIKU -# include -#endif - -static int minPrio = 0, maxPrio = 0, normalPrio = 0; - -struct thread_ctx { - void (*function)(id object); - id object; -}; - -/* - * This is done here to make sure this is done as early as possible in the main - * thread. - */ -OF_CONSTRUCTOR() -{ - pthread_attr_t pattr; - - if (pthread_attr_init(&pattr) == 0) { -#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY - int policy; -#endif - struct sched_param param; - -#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY - if (pthread_attr_getschedpolicy(&pattr, &policy) == 0) { - minPrio = sched_get_priority_min(policy); - maxPrio = sched_get_priority_max(policy); - - if (minPrio == -1 || maxPrio == -1) - minPrio = maxPrio = 0; - } - - if (pthread_attr_getschedparam(&pattr, ¶m) != 0) - normalPrio = param.sched_priority; - else - minPrio = maxPrio = 0; - - pthread_attr_destroy(&pattr); -#endif - } -} - -static void * -function_wrapper(void *data) -{ - struct thread_ctx *ctx = data; - - pthread_cleanup_push(free, data); - - ctx->function(ctx->object); - - pthread_cleanup_pop(1); - return NULL; -} - -bool -of_thread_attr_init(of_thread_attr_t *attr) -{ - pthread_attr_t pattr; - - if (pthread_attr_init(&pattr) != 0) - return false; - - @try { - attr->priority = 0; - - if (pthread_attr_getstacksize(&pattr, &attr->stackSize) != 0) - return false; - } @finally { - pthread_attr_destroy(&pattr); - } - - return true; -} - -bool -of_thread_new(of_thread_t *thread, void (*function)(id), id object, - const of_thread_attr_t *attr) -{ - bool ret; - pthread_attr_t pattr; - - if (pthread_attr_init(&pattr) != 0) - return false; - - @try { - struct thread_ctx *ctx; - - if (attr != NULL) { - struct sched_param param; - - if (attr->priority < -1 || attr->priority > 1) - return false; - -#ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED - if (pthread_attr_setinheritsched(&pattr, - PTHREAD_EXPLICIT_SCHED) != 0) - return false; -#endif - - if (attr->priority < 0) { - param.sched_priority = minPrio + - (1.0f + attr->priority) * - (normalPrio - minPrio); - } else - param.sched_priority = normalPrio + - attr->priority * (maxPrio - normalPrio); - - if (pthread_attr_setschedparam(&pattr, ¶m) != 0) - return false; - - if (attr->stackSize > 0) { - if (pthread_attr_setstacksize(&pattr, - attr->stackSize) != 0) - return false; - } - } - - if ((ctx = malloc(sizeof(*ctx))) == NULL) - return false; - - ctx->function = function; - ctx->object = object; - - ret = (pthread_create(thread, &pattr, - function_wrapper, ctx) == 0); - } @finally { - pthread_attr_destroy(&pattr); - } - - return ret; -} - -bool -of_thread_join(of_thread_t thread) -{ - void *ret; - - if (pthread_join(thread, &ret) != 0) - return false; - -#ifdef PTHREAD_CANCELED - return (ret != PTHREAD_CANCELED); -#else - return true; -#endif -} - -bool -of_thread_detach(of_thread_t thread) -{ - return (pthread_detach(thread) == 0); -} - -void -of_thread_set_name(const char *name) -{ -#if defined(OF_HAIKU) - rename_thread(find_thread(NULL), name); -#elif defined(HAVE_PTHREAD_SET_NAME_NP) - pthread_set_name_np(pthread_self(), name); -#elif defined(HAVE_PTHREAD_SETNAME_NP) -# if defined(OF_MACOS) || defined(OF_IOS) - pthread_setname_np(name); -# elif defined(__GLIBC__) - char buffer[16]; - - strncpy(buffer, name, 15); - buffer[15] = 0; - - pthread_setname_np(pthread_self(), buffer); -# endif -#endif -} - -void -of_once(of_once_t *control, void (*func)(void)) -{ - pthread_once(control, func); -} - -bool -of_tlskey_new(of_tlskey_t *key) -{ - return (pthread_key_create(key, NULL) == 0); -} - -bool -of_tlskey_free(of_tlskey_t key) -{ - return (pthread_key_delete(key) == 0); -} - -bool -of_mutex_new(of_mutex_t *mutex) -{ - return (pthread_mutex_init(mutex, NULL) == 0); -} - -bool -of_mutex_lock(of_mutex_t *mutex) -{ - return (pthread_mutex_lock(mutex) == 0); -} - -bool -of_mutex_trylock(of_mutex_t *mutex) -{ - return (pthread_mutex_trylock(mutex) == 0); -} - -bool -of_mutex_unlock(of_mutex_t *mutex) -{ - return (pthread_mutex_unlock(mutex) == 0); -} - -bool -of_mutex_free(of_mutex_t *mutex) -{ - return (pthread_mutex_destroy(mutex) == 0); -} - -#ifdef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES -bool -of_rmutex_new(of_rmutex_t *rmutex) -{ - pthread_mutexattr_t attr; - - if (pthread_mutexattr_init(&attr) != 0) - return false; - - if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) - return false; - - if (pthread_mutex_init(rmutex, &attr) != 0) - return false; - - if (pthread_mutexattr_destroy(&attr) != 0) - return false; - - return true; -} - -bool -of_rmutex_lock(of_rmutex_t *rmutex) -{ - return of_mutex_lock(rmutex); -} - -bool -of_rmutex_trylock(of_rmutex_t *rmutex) -{ - return of_mutex_trylock(rmutex); -} - -bool -of_rmutex_unlock(of_rmutex_t *rmutex) -{ - return of_mutex_unlock(rmutex); -} - -bool -of_rmutex_free(of_rmutex_t *rmutex) -{ - return of_mutex_free(rmutex); -} -#endif - -bool -of_condition_new(of_condition_t *condition) -{ - return (pthread_cond_init(condition, NULL) == 0); -} - -bool -of_condition_signal(of_condition_t *condition) -{ - return (pthread_cond_signal(condition) == 0); -} - -bool -of_condition_broadcast(of_condition_t *condition) -{ - return (pthread_cond_broadcast(condition) == 0); -} - -bool -of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) -{ - return (pthread_cond_wait(condition, mutex) == 0); -} - -bool -of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, - of_time_interval_t timeout) -{ - struct timespec ts; - - ts.tv_sec = (time_t)timeout; - ts.tv_nsec = lrint((timeout - ts.tv_sec) * 1000000000); - - return (pthread_cond_timedwait(condition, mutex, &ts) == 0); -} - -bool -of_condition_free(of_condition_t *condition) -{ - return (pthread_cond_destroy(condition) == 0); -} DELETED src/threading_winapi.m Index: src/threading_winapi.m ================================================================== --- src/threading_winapi.m +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#import "macros.h" - -bool -of_thread_attr_init(of_thread_attr_t *attr) -{ - attr->priority = 0; - attr->stackSize = 0; - - return true; -} - -bool -of_thread_new(of_thread_t *thread, void (*function)(id), id object, - const of_thread_attr_t *attr) -{ - *thread = CreateThread(NULL, (attr != NULL ? attr->stackSize : 0), - (LPTHREAD_START_ROUTINE)function, (void *)object, 0, NULL); - - if (thread == NULL) - return false; - - if (attr != NULL && attr->priority != 0) { - DWORD priority; - - if (attr->priority < -1 || attr->priority > 1) - return false; - - if (attr->priority < 0) - priority = THREAD_PRIORITY_LOWEST + - (1.0 + attr->priority) * - (THREAD_PRIORITY_NORMAL - THREAD_PRIORITY_LOWEST); - else - priority = THREAD_PRIORITY_NORMAL + - attr->priority * - (THREAD_PRIORITY_HIGHEST - THREAD_PRIORITY_NORMAL); - - if (!SetThreadPriority(*thread, priority)) - return false; - } - - return true; -} - -bool -of_thread_join(of_thread_t thread) -{ - if (WaitForSingleObject(thread, INFINITE)) - return false; - - CloseHandle(thread); - - return true; -} - -bool -of_thread_detach(of_thread_t thread) -{ - /* FIXME */ - return true; -} - -void -of_thread_set_name(const char *name) -{ -} - -bool -of_tlskey_new(of_tlskey_t *key) -{ - return ((*key = TlsAlloc()) != TLS_OUT_OF_INDEXES); -} - -bool -of_tlskey_free(of_tlskey_t key) -{ - return TlsFree(key); -} - -bool -of_mutex_new(of_mutex_t *mutex) -{ - InitializeCriticalSection(mutex); - - return true; -} - -bool -of_mutex_lock(of_mutex_t *mutex) -{ - EnterCriticalSection(mutex); - - return true; -} - -bool -of_mutex_trylock(of_mutex_t *mutex) -{ - return TryEnterCriticalSection(mutex); -} - -bool -of_mutex_unlock(of_mutex_t *mutex) -{ - LeaveCriticalSection(mutex); - - return true; -} - -bool -of_mutex_free(of_mutex_t *mutex) -{ - DeleteCriticalSection(mutex); - - return true; -} - -bool -of_rmutex_new(of_rmutex_t *rmutex) -{ - return of_mutex_new(rmutex); -} - -bool -of_rmutex_lock(of_rmutex_t *rmutex) -{ - return of_mutex_lock(rmutex); -} - -bool -of_rmutex_trylock(of_rmutex_t *rmutex) -{ - return of_mutex_trylock(rmutex); -} - -bool -of_rmutex_unlock(of_rmutex_t *rmutex) -{ - return of_mutex_unlock(rmutex); -} - -bool -of_rmutex_free(of_rmutex_t *rmutex) -{ - return of_mutex_free(rmutex); -} - -bool -of_condition_new(of_condition_t *condition) -{ - condition->count = 0; - - if ((condition->event = CreateEvent(NULL, FALSE, 0, NULL)) == NULL) - return false; - - return true; -} - -bool -of_condition_signal(of_condition_t *condition) -{ - return SetEvent(condition->event); -} - -bool -of_condition_broadcast(of_condition_t *condition) -{ - for (int i = 0; i < condition->count; i++) - if (!SetEvent(condition->event)) - return false; - - return true; -} - -bool -of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) -{ - DWORD status; - - if (!of_mutex_unlock(mutex)) - return false; - - of_atomic_int_inc(&condition->count); - status = WaitForSingleObject(condition->event, INFINITE); - of_atomic_int_dec(&condition->count); - - if (!of_mutex_lock(mutex)) - return false; - - return (status == WAIT_OBJECT_0); -} - -bool -of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, - of_time_interval_t timeout) -{ - DWORD status; - - if (!of_mutex_unlock(mutex)) - return false; - - of_atomic_int_inc(&condition->count); - status = WaitForSingleObject(condition->event, timeout * 1000); - of_atomic_int_dec(&condition->count); - - if (!of_mutex_lock(mutex)) - return false; - - return (status == WAIT_OBJECT_0); -} - -bool -of_condition_free(of_condition_t *condition) -{ - if (condition->count != 0) - return false; - - return CloseHandle(condition->event); -} ADDED src/tlskey.h Index: src/tlskey.h ================================================================== --- /dev/null +++ src/tlskey.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "objfw-defs.h" + +#include "platform.h" + +#if !defined(OF_HAVE_THREADS) || \ + (!defined(OF_HAVE_PTHREADS) && !defined(OF_WINDOWS)) +# error No thread-local storage available! +#endif + +#import "macros.h" + +#if defined(OF_HAVE_PTHREADS) +# include +typedef pthread_key_t of_tlskey_t; +#elif defined(OF_WINDOWS) +# include +typedef DWORD of_tlskey_t; +#endif +extern bool of_tlskey_new(of_tlskey_t *key); +extern bool of_tlskey_free(of_tlskey_t key); + +/* TLS keys are inlined for performance. */ + +#if defined(OF_HAVE_PTHREADS) +static OF_INLINE void * +of_tlskey_get(of_tlskey_t key) +{ + return pthread_getspecific(key); +} + +static OF_INLINE bool +of_tlskey_set(of_tlskey_t key, void *ptr) +{ + return (pthread_setspecific(key, ptr) == 0); +} +#elif defined(OF_WINDOWS) +static OF_INLINE void * +of_tlskey_get(of_tlskey_t key) +{ + return TlsGetValue(key); +} + +static OF_INLINE bool +of_tlskey_set(of_tlskey_t key, void *ptr) +{ + return TlsSetValue(key, ptr); +} +#endif ADDED src/tlskey.m Index: src/tlskey.m ================================================================== --- /dev/null +++ src/tlskey.m @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "tlskey.h" + +bool +of_tlskey_new(of_tlskey_t *key) +{ +#if defined(OF_HAVE_PTHREADS) + return (pthread_key_create(key, NULL) == 0); +#elif defined(OF_WINDOWS) + return ((*key = TlsAlloc()) != TLS_OUT_OF_INDEXES); +#endif +} + +bool +of_tlskey_free(of_tlskey_t key) +{ +#if defined(OF_HAVE_PTHREADS) + return (pthread_key_delete(key) == 0); +#elif defined(OF_WINDOWS) + return TlsFree(key); +#endif +}