/*
* Copyright (c) 2008 - 2009
* Jonathan Schleifer <js@webkeks.org>
*
* 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 "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];
return 0;
}
#endif
@implementation OFThread
+ threadWithObject: (id)obj
{
/*
* This is one of the rare cases where the convenience method should
* use self instead of the class.
*
* The reason is that you derive from OFThread and reimplement main.
* If OFThread instead of self would be used here, the reimplemented
* main would never be called.
*/
return [[[self alloc] initWithObject: obj] autorelease];
}
+ setObject: (id)obj
forTLSKey: (OFTLSKey*)key
{
id old = [self objectForTLSKey: key];
#ifndef _WIN32
if (pthread_setspecific(key->key, obj))
#else
if (!TlsSetValue(key->key, obj))
#endif
/* FIXME: Maybe another exception would be better */
@throw [OFInvalidArgumentException newWithClass: self
andSelector: _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;
}
- initWithObject: (id)obj
{
Class c;
self = [super init];
object = obj;
#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;
[super dealloc];
@throw [OFInitializationFailedException newWithClass: c];
}
return self;
}
- main
{
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;
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
[super dealloc];
}
@end
@implementation OFTLSKey
+ tlsKeyWithDestructor: (void(*)(void*))destructor
{
return [[[OFTLSKey alloc] initWithDestructor: destructor] autorelease];
}
- initWithDestructor: (void(*)(void*))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;
[super dealloc];
@throw [OFInitializationFailedException newWithClass: c];
}
return self;
}
@end