Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -31,11 +31,12 @@ INCLUDESTMP := ${SRCS:.c=.h} INCLUDES := ${INCLUDESTMP:.m=.h} \ OFMacros.h \ asprintf.h \ - objfw.h + objfw.h \ + threading.h SRCS += ${OBJC_SYNC_M} \ ${ASPRINTF_C} include ../buildsys.mk Index: src/OFAutoreleasePool.m ================================================================== --- src/OFAutoreleasePool.m +++ src/OFAutoreleasePool.m @@ -11,146 +11,100 @@ #include "config.h" #include -#ifndef _WIN32 -#include -#else -#include -#endif - #import "OFAutoreleasePool.h" #import "OFList.h" #import "OFThread.h" #import "OFExceptions.h" + +#import "threading.h" /* * Pay special attention to NULL and nil in this file, they might be different! * Use NULL for TLS values and nil for instance variables. */ -#ifndef _WIN32 -static pthread_key_t first_key, last_key; -#else -static DWORD first_key, last_key; -#endif +static of_tlskey_t first_key, last_key; #ifndef _WIN32 /* Not used on Win32 yet */ static void -release_all(void *list) -{ -#ifndef _WIN32 - void *first = pthread_getspecific(first_key); -#else - void *first = TlsGetValue(first_key); -#endif - - if (first != NULL) - [(OFAutoreleasePool*)first release]; +release_all(id obj) +{ + [of_tlskey_get(first_key) release]; } #endif @implementation OFAutoreleasePool + (void)initialize { if (self != [OFAutoreleasePool class]) return; -#ifndef _WIN32 - if (pthread_key_create(&first_key, release_all) || - pthread_key_create(&last_key, NULL)) -#else - /* FIXME: Call destructor */ - if ((first_key = TlsAlloc()) == TLS_OUT_OF_INDEXES || - (last_key = TlsAlloc()) == TLS_OUT_OF_INDEXES) -#endif + if (!of_tlskey_new(&first_key, release_all) || + !of_tlskey_new(&last_key, NULL)) @throw [OFInitializationFailedException newWithClass: self]; } + (void)addObjectToTopmostPool: (OFObject*)obj { -#ifndef _WIN32 - void *last = pthread_getspecific(last_key); -#else - void *last = TlsGetValue(last_key); -#endif + id last = of_tlskey_get(last_key); - if (last == NULL) { + if (last == nil) { @try { [[self alloc] init]; } @catch (OFException *e) { [obj release]; @throw e; } -#ifndef _WIN32 - last = pthread_getspecific(last_key); -#else - last = TlsGetValue(last_key); -#endif + last = of_tlskey_get(last_key); } - if (last == NULL) { + if (last == nil) { [obj release]; @throw [OFInitializationFailedException newWithClass: self]; } @try { - [(OFAutoreleasePool*)last addObject: obj]; + [last addObject: obj]; } @catch (OFException *e) { [obj release]; @throw e; } } - init { + id first; + self = [super init]; -#ifndef _WIN32 - void *first = pthread_getspecific(first_key); - void *last = pthread_getspecific(last_key); -#else - void *first = TlsGetValue(first_key); - void *last = TlsGetValue(last_key); -#endif - -#ifndef _WIN32 - if (pthread_setspecific(last_key, self)) { -#else - if (!TlsSetValue(last_key, self)) { -#endif + first = of_tlskey_get(first_key); + prev = of_tlskey_get(last_key); + + if (!of_tlskey_set(last_key, self)) { Class c = isa; [super dealloc]; @throw [OFInitializationFailedException newWithClass: c]; } - if (first == NULL) { -#ifndef _WIN32 - if (pthread_setspecific(first_key, self)) { -#else - if (!TlsSetValue(first_key, self)) { -#endif + if (first == nil) { + if (!of_tlskey_set(first_key, self)) { Class c = isa; -#ifndef _WIN32 - pthread_setspecific(last_key, last); -#else - TlsSetValue(last_key, last); -#endif + of_tlskey_set(last_key, prev); [super dealloc]; @throw [OFInitializationFailedException newWithClass: c]; } } - if (last != NULL) { - prev = (OFAutoreleasePool*)last; + if (prev != nil) prev->next = self; - } return self; } - (void)dealloc @@ -157,19 +111,15 @@ { [next dealloc]; if (prev != nil) prev->next = nil; -#ifndef _WIN32 - pthread_setspecific(last_key, (prev != nil ? prev : NULL)); - if (pthread_getspecific(first_key) == self) - pthread_setspecific(first_key, NULL); -#else - TlsSetValue(last_key, (prev != nil ? prev : NULL)); - if (TlsGetValue(first_key) == self) - TlsSetValue(first_key, NULL); -#endif + + /* FIXME: Add exception? */ + of_tlskey_set(last_key, prev); + if (of_tlskey_get(first_key) == self) + of_tlskey_set(first_key, nil); [objects release]; [super dealloc]; } Index: src/OFExceptions.m ================================================================== --- src/OFExceptions.m +++ src/OFExceptions.m @@ -983,10 +983,11 @@ return string; } @end +/* FIXME: Not needed anymore? */ @implementation OFThreadCanceledException - (OFString*)string { if (string != nil) return string; Index: src/OFThread.h ================================================================== --- src/OFThread.h +++ src/OFThread.h @@ -7,42 +7,34 @@ * This file is part of libobjfw. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ -#ifndef _WIN32 -#include -#else -#include -#endif - #import "OFObject.h" + +#import "threading.h" /** * A Thread Local Storage key. */ @interface OFTLSKey: OFObject { @public -#ifndef _WIN32 - pthread_key_t key; -#else - DWORD key; -#endif + of_tlskey_t key; } /** * \param destructor A destructor that is called when the thread is terminated * \return A new autoreleased Thread Local Storage key */ -+ tlsKeyWithDestructor: (void(*)(void*))destructor; ++ tlsKeyWithDestructor: (void(*)(id))destructor; /** * \param destructor A destructor that is called when the thread is terminated * \return An initialized Thread Local Storage key */ -- initWithDestructor: (void(*)(void*))destructor; +- initWithDestructor: (void(*)(id))destructor; @end /** * The OFThread class provides portable threads. * @@ -50,18 +42,14 @@ * main. */ @interface OFThread: OFObject { id object; -#ifndef _WIN32 - pthread_t thread; -#else - HANDLE thread; + of_thread_t thread; @public id retval; -#endif } /** * \param obj An object that is passed to the main method as a copy or nil * \return A new autoreleased thread @@ -115,15 +103,11 @@ /** * A class for creating mutual exclusions. */ @interface OFMutex: OFObject { -#ifndef _WIN32 - pthread_mutex_t mutex; -#else - CRITICAL_SECTION mutex; -#endif + of_mutex_t mutex; } /** * \return A new autoreleased mutex. */ Index: src/OFThread.m ================================================================== --- src/OFThread.m +++ src/OFThread.m @@ -12,26 +12,23 @@ #include "config.h" #import "OFThread.h" #import "OFExceptions.h" -#ifndef _WIN32 -static void* -call_main(void *obj) -{ - return [(OFThread*)obj main]; -} -#else -static DWORD WINAPI -call_main(LPVOID obj) -{ - /* Windows does not support returning a pointer. Nasty workaround. */ - ((OFThread*)obj)->retval = [(OFThread*)obj main]; +#import "threading.h" + +static id +call_main(id obj) +{ + /* + * Nasty workaround for thread implementations which can't return a + * value on join. + */ + ((OFThread*)obj)->retval = [obj main]; return 0; } -#endif @implementation OFThread + threadWithObject: (id)obj { return [[[self alloc] initWithObject: obj] autorelease]; @@ -38,46 +35,25 @@ } + setObject: (id)obj forTLSKey: (OFTLSKey*)key { - id old = [self objectForTLSKey: key]; + id old = of_tlskey_get(key->key); -#ifndef _WIN32 - if (pthread_setspecific(key->key, obj)) -#else - if (!TlsSetValue(key->key, obj)) -#endif + if (!of_tlskey_set(key->key, [obj retain])) /* FIXME: Maybe another exception would be better */ @throw [OFInvalidArgumentException newWithClass: self selector: _cmd]; - [obj retain]; [old release]; return self; } + (id)objectForTLSKey: (OFTLSKey*)key { - void *ret; - -#ifndef _WIN32 - ret = pthread_getspecific(key->key); -#else - ret = TlsGetValue(key->key); -#endif - - /* - * NULL and nil might be different on some platforms. NULL is returned - * if the key is missing, nil can be returned if it was explicitly set - * to nil to release the old object. - */ - if (ret == NULL) - return nil; - - return (id)ret; + return [[of_tlskey_get(key->key) retain] autorelease]; } - init { @throw [OFNotImplementedException newWithClass: isa @@ -84,22 +60,15 @@ selector: _cmd]; } - initWithObject: (id)obj { - Class c; - self = [super init]; object = [obj copy]; -#ifndef _WIN32 - if (pthread_create(&thread, NULL, call_main, self)) { -#else - if ((thread = - CreateThread(NULL, 0, call_main, self, 0, NULL)) == NULL) { -#endif - c = isa; + if (!of_thread_new(&thread, call_main, self)) { + Class c = isa; [super dealloc]; @throw [OFInitializationFailedException newWithClass: c]; } return self; @@ -110,77 +79,50 @@ return nil; } - join { -#ifndef _WIN32 - void *ret; - - if (pthread_join(thread, &ret)) - @throw [OFThreadJoinFailedException newWithClass: isa]; - - if (ret == PTHREAD_CANCELED) - @throw [OFThreadCanceledException newWithClass: isa]; - - return (id)ret; -#else - if (WaitForSingleObject(thread, INFINITE)) - @throw [OFThreadJoinFailedException newWithClass: isa]; - - CloseHandle(thread); - thread = INVALID_HANDLE_VALUE; + of_thread_join(thread); return retval; -#endif } - (void)dealloc { /* * No need to handle errors - if canceling the thread fails, we can't * do anything anyway. Most likely, it finished already or was already * canceled. */ -#ifndef _WIN32 - pthread_cancel(thread); -#else - if (thread != INVALID_HANDLE_VALUE) { - TerminateThread(thread, 1); - CloseHandle(thread); - } -#endif + of_thread_cancel(thread); [object release]; [super dealloc]; } @end @implementation OFTLSKey -+ tlsKeyWithDestructor: (void(*)(void*))destructor ++ tlsKeyWithDestructor: (void(*)(id))destructor { return [[[self alloc] initWithDestructor: destructor] autorelease]; } -- initWithDestructor: (void(*)(void*))destructor +- initWithDestructor: (void(*)(id))destructor { - Class c; - self = [super init]; /* FIXME: Call destructor on Win32 */ -#ifndef _WIN32 - if (pthread_key_create(&key, destructor)) { -#else - if ((key = TlsAlloc()) == TLS_OUT_OF_INDEXES) { -#endif - c = isa; + if (!of_tlskey_new(&key, destructor)) { + Class c = isa; [super dealloc]; @throw [OFInitializationFailedException newWithClass: c]; } return self; } + +/* FIXME: Add dealloc! */ @end @implementation OFMutex + mutex { @@ -189,54 +131,38 @@ - init { self = [super init]; -#ifndef _WIN32 - if (pthread_mutex_init(&mutex, NULL)) { + if (!of_mutex_new(&mutex)) { Class c = isa; [self dealloc]; @throw [OFInitializationFailedException newWithClass: c]; } -#else - InitializeCriticalSection(&mutex); -#endif return self; } - lock { -#ifndef _WIN32 /* FIXME: Add error-handling */ - pthread_mutex_lock(&mutex); -#else - EnterCriticalSection(&mutex); -#endif + of_mutex_lock(&mutex); return self; } - unlock { -#ifndef _WIN32 /* FIXME: Add error-handling */ - pthread_mutex_unlock(&mutex); -#else - LeaveCriticalSection(&mutex); -#endif + of_mutex_unlock(&mutex); return self; } - (void)dealloc { -#ifndef _WIN32 /* FIXME: Add error-handling */ - pthread_mutex_destroy(&mutex); -#else - DeleteCriticalSection(&mutex); -#endif + of_mutex_free(&mutex); [super dealloc]; } @end Index: src/objc_sync.m ================================================================== --- src/objc_sync.m +++ src/objc_sync.m @@ -13,103 +13,26 @@ #include #include #include -#ifndef _WIN32 -#include -#endif - #import -#ifdef _WIN32 -#include -#endif - -#import "OFMacros.h" +#import "threading.h" struct locks_s { id obj; size_t count; size_t recursion; -#ifndef _WIN32 - pthread_t thread; - pthread_mutex_t mutex; -#else - DWORD thread; - CRITICAL_SECTION mutex; -#endif + of_thread_t thread; + of_mutex_t mutex; }; -#ifndef _WIN32 -static pthread_mutex_t mutex; -#else -static CRITICAL_SECTION mutex; -#endif +static of_mutex_t mutex; static struct locks_s *locks = NULL; static size_t num_locks = 0; -#ifndef _WIN32 -static OF_INLINE BOOL -mutex_new(pthread_mutex_t *m) -{ - return (pthread_mutex_init(m, NULL) ? NO : YES); -} - -static OF_INLINE BOOL -mutex_free(pthread_mutex_t *m) -{ - return (pthread_mutex_destroy(m) ? NO : YES); -} - -static OF_INLINE BOOL -mutex_lock(pthread_mutex_t *m) -{ - return (pthread_mutex_lock(m) ? NO : YES); -} - -static OF_INLINE BOOL -mutex_unlock(pthread_mutex_t *m) -{ - return (pthread_mutex_unlock(m) ? NO : YES); -} - -#define thread_is_current(t) pthread_equal(t, pthread_self()) -#define thread_current() pthread_self() -#else -static OF_INLINE BOOL -mutex_new(CRITICAL_SECTION *m) -{ - InitializeCriticalSection(m); - return YES; -} - -static OF_INLINE BOOL -mutex_free(CRITICAL_SECTION *m) -{ - DeleteCriticalSection(m); - return YES; -} - -static OF_INLINE BOOL -mutex_lock(CRITICAL_SECTION *m) -{ - EnterCriticalSection(m); - return YES; -} - -static OF_INLINE BOOL -mutex_unlock(CRITICAL_SECTION *m) -{ - LeaveCriticalSection(m); - return YES; -} - -#define thread_is_current(t) (t == GetCurrentThreadId()) -#define thread_current() GetCurrentThreadId() -#endif - #define SYNC_ERR(f) \ { \ fprintf(stderr, "WARNING: %s failed in line %d!\n" \ "WARNING: This might result in a race " \ "condition!\n", f, __LINE__); \ @@ -117,11 +40,11 @@ } BOOL objc_sync_init() { - return (mutex_new(&mutex) ? YES : NO); + return (of_mutex_new(&mutex) ? YES : NO); } int objc_sync_enter(id obj) { @@ -128,82 +51,83 @@ int i; if (obj == nil) return 0; - if (!mutex_lock(&mutex)) - SYNC_ERR("mutex_lock(&mutex)"); + if (!of_mutex_lock(&mutex)) + SYNC_ERR("of_mutex_lock(&mutex)"); for (i = num_locks - 1; i >= 0; i--) { if (locks[i].obj == obj) { - if (thread_is_current(locks[i].thread)) + if (of_thread_is_current(locks[i].thread)) locks[i].recursion++; else { /* Make sure objc_sync_exit doesn't free it */ locks[i].count++; /* Unlock so objc_sync_exit can return */ - if (!mutex_unlock(&mutex)) - SYNC_ERR("mutex_unlock(&mutex)"); + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); - if (!mutex_lock(&locks[i].mutex)) { - mutex_unlock(&mutex); - SYNC_ERR("mutex_lock(&locks[i].mutex"); + if (!of_mutex_lock(&locks[i].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR( + "of_mutex_lock(&locks[i].mutex"); } - if (!mutex_lock(&mutex)) - SYNC_ERR("mutex_lock(&mutex)"); + if (!of_mutex_lock(&mutex)) + SYNC_ERR("of_mutex_lock(&mutex)"); assert(locks[i].recursion == 0); /* Update lock's active thread */ - locks[i].thread = thread_current(); + locks[i].thread = of_thread_current(); } - if (!mutex_unlock(&mutex)) - SYNC_ERR("mutex_unlock(&mutex)"); + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); return 0; } } if (locks == NULL) { if ((locks = malloc(sizeof(struct locks_s))) == NULL) { - mutex_unlock(&mutex); + of_mutex_unlock(&mutex); SYNC_ERR("malloc(...)"); } } else { struct locks_s *new_locks; if ((new_locks = realloc(locks, (num_locks + 1) * sizeof(struct locks_s))) == NULL) { - mutex_unlock(&mutex); + of_mutex_unlock(&mutex); SYNC_ERR("realloc(...)"); } locks = new_locks; } locks[num_locks].obj = obj; locks[num_locks].count = 1; locks[num_locks].recursion = 0; - locks[num_locks].thread = thread_current(); + locks[num_locks].thread = of_thread_current(); - if (!mutex_new(&locks[num_locks].mutex)) { - mutex_unlock(&mutex); - SYNC_ERR("mutex_new(&locks[num_locks].mutex"); + if (!of_mutex_new(&locks[num_locks].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR("of_mutex_new(&locks[num_locks].mutex"); } - if (!mutex_lock(&locks[num_locks].mutex)) { - mutex_unlock(&mutex); - SYNC_ERR("mutex_lock(&locks[num_locks].mutex"); + if (!of_mutex_lock(&locks[num_locks].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR("of_mutex_lock(&locks[num_locks].mutex"); } num_locks++; - if (!mutex_unlock(&mutex)) - SYNC_ERR("mutex_unlock(&mutex)"); + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); return 0; } int @@ -212,38 +136,39 @@ int i; if (obj == nil) return 0; - if (!mutex_lock(&mutex)) - SYNC_ERR("mutex_lock(&mutex)"); + if (!of_mutex_lock(&mutex)) + SYNC_ERR("of_mutex_lock(&mutex)"); for (i = num_locks - 1; i >= 0; i--) { if (locks[i].obj == obj) { if (locks[i].recursion > 0 && - thread_is_current(locks[i].thread)) { + of_thread_is_current(locks[i].thread)) { locks[i].recursion--; - if (!mutex_unlock(&mutex)) - SYNC_ERR("mutex_unlock(&mutex)"); + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); return 0; } - if (!mutex_unlock(&locks[i].mutex)) { - mutex_unlock(&mutex); - SYNC_ERR("mutex_unlock(&locks[i].mutex)"); + if (!of_mutex_unlock(&locks[i].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR("of_mutex_unlock(&locks[i].mutex)"); } locks[i].count--; if (locks[i].count == 0) { struct locks_s *new_locks = NULL; - if (!mutex_free(&locks[i].mutex)) { - mutex_unlock(&mutex); - SYNC_ERR("mutex_free(&locks[i].mutex"); + if (!of_mutex_free(&locks[i].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR( + "of_mutex_free(&locks[i].mutex"); } num_locks--; locks[i] = locks[num_locks]; @@ -251,22 +176,22 @@ free(locks); new_locks = NULL; } else if ((new_locks = realloc(locks, num_locks * sizeof(struct locks_s))) == NULL) { - mutex_unlock(&mutex); + of_mutex_unlock(&mutex); SYNC_ERR("realloc(...)"); } locks = new_locks; } - if (!mutex_unlock(&mutex)) - SYNC_ERR("mutex_unlock(&mutex)"); + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); return 0; } } - mutex_unlock(&mutex); + of_mutex_unlock(&mutex); SYNC_ERR("objc_sync_exit()"); } ADDED src/threading.h Index: src/threading.h ================================================================== --- src/threading.h +++ src/threading.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2008 - 2009 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of libobjfw. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE included in + * the packaging of this file. + */ + +#import "OFMacros.h" + +#ifndef _WIN32 +#include +typedef pthread_t of_thread_t; +typedef pthread_mutex_t of_mutex_t; +typedef pthread_key_t of_tlskey_t; +#else +#include +typedef HANDLE of_thread_t; +typedef CRITICAL_SECTION of_mutex_t; +typedef DWORD of_tlskey_t; +#endif + +#ifndef _WIN32 +#define of_thread_is_current(t) pthread_equal(t, pthread_self()) +#define of_thread_current() pthread_self() +#else +#define of_thread_is_current(t) (t == GetCurrentThreadId()) +#define of_thread_current() GetCurrentThreadId() +#endif + +static OF_INLINE BOOL +of_thread_new(of_thread_t *thread, id (*main)(id), id data) +{ +#ifndef _WIN32 + return (pthread_create(thread, NULL, (void*(*)(void*))main, + (void*)data) ? NO : YES); +#else + *thread = CreateThread(NULL, 0, (void*(*)(void*))main, + (void*)data, 0, NULL); + + return (thread == NULL ? NO : YES); +#endif +} + +static OF_INLINE BOOL +of_thread_join(of_thread_t thread) +{ +#ifndef _WIN32 + void *ret; + + if (pthread_join(thread, &ret)) + return NO; + + /* FIXME: Do we need a way to differentiate? */ + return (ret != PTHREAD_CANCELED ? YES : NO); +#else + if (WaitForSingleObject(thread, INFINITE)) + return NO; + + CloseHandle(thread); + + return YES; +#endif +} + +static OF_INLINE BOOL +of_thread_cancel(of_thread_t thread) +{ +#ifndef _WIN32 + return (pthread_cancel(thread) ? NO : YES); +#else + if (thread != INVALID_HANDLE_VALUE) { + TerminateThread(thread, 1); + CloseHandle(thread); + } + + return YES; +#endif +} + +static OF_INLINE BOOL +of_mutex_new(of_mutex_t *mutex) +{ +#ifndef _WIN32 + return (pthread_mutex_init(mutex, NULL) ? NO : YES); +#else + InitializeCriticalSection(mutex); + return YES; +#endif +} + +static OF_INLINE BOOL +of_mutex_free(of_mutex_t *mutex) +{ +#ifndef _WIN32 + return (pthread_mutex_destroy(mutex) ? NO : YES); +#else + DeleteCriticalSection(mutex); + return YES; +#endif +} + +static OF_INLINE BOOL +of_mutex_lock(of_mutex_t *mutex) +{ +#ifndef _WIN32 + return (pthread_mutex_lock(mutex) ? NO : YES); +#else + EnterCriticalSection(mutex); + return YES; +#endif +} + +static OF_INLINE BOOL +of_mutex_unlock(of_mutex_t *mutex) +{ +#ifndef _WIN32 + return (pthread_mutex_unlock(mutex) ? NO : YES); +#else + LeaveCriticalSection(mutex); + return YES; +#endif +} + +static OF_INLINE BOOL +of_tlskey_new(of_tlskey_t *key, void (*destructor)(id)) +{ +#ifndef _WIN32 + return (pthread_key_create(key, (void(*)(void*))destructor) ? NO : YES); +#else + /* FIXME: Call destructor */ + return ((*key = TlsAlloc()) == TLS_OUT_OF_INDEXES ? NO : YES); +#endif +} + +static OF_INLINE id +of_tlskey_get(of_tlskey_t key) +{ +#ifndef _WIN32 + void *ret = pthread_getspecific(key); +#else + void *ret = TlsGetValue(key); +#endif + + /* NULL and nil might be different! */ + if (ret == NULL) + return nil; + + return (id)ret; +} + +static OF_INLINE BOOL +of_tlskey_set(of_tlskey_t key, id obj) +{ + void *p = (obj != nil ? (void*)obj : NULL); + +#ifndef _WIN32 + return (pthread_setspecific(key, p) ? NO : YES); +#else + return (TlsSetValue(key, p) ? YES : NO); +#endif +}