Artifact 378318c425e736e372289a30c1a8915ac0b1b9037990efd94d728ee9e9ddb2fc:
- File
src/OFThread.m
— part of check-in
[0639a351db]
at
2012-11-10 00:14:59
on branch trunk
— Remove "object" from OFThread.
Blocks don't allow passing it anymore and without blocks, subclassing is
necessary anyway so that an ivar with the correct type can be added
(instead of id) if required. (user: js, size: 6766) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011, 2012 * Jonathan Schleifer <js@webkeks.org> * * 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" #define OF_THREAD_M #define __NO_EXT_QNX #include <math.h> #ifndef _WIN32 # include <unistd.h> # include <sched.h> #endif #import "OFThread.h" #import "OFList.h" #import "OFDate.h" #import "OFSortedList.h" #import "OFRunLoop.h" #import "OFAutoreleasePool.h" #ifdef _WIN32 # include <windows.h> #endif #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFNotImplementedException.h" #import "OFOutOfRangeException.h" #import "OFThreadJoinFailedException.h" #import "OFThreadStartFailedException.h" #import "OFThreadStillRunningException.h" #import "atomic.h" #import "autorelease.h" #import "threading.h" static of_tlskey_t threadSelfKey; static OFThread *mainThread; static id call_main(id object) { OFThread *thread = (OFThread*)object; if (!of_tlskey_set(threadSelfKey, thread)) @throw [OFInitializationFailedException exceptionWithClass: [thread class]]; objc_autoreleasePoolPush(); /* * Nasty workaround for thread implementations which can't return a * value on join. */ #ifdef OF_HAVE_BLOCKS if (thread->block != NULL) thread->returnValue = [thread->block() retain]; else #endif thread->returnValue = [[thread main] retain]; [thread handleTermination]; thread->running = OF_THREAD_WAITING_FOR_JOIN; [OFTLSKey OF_callAllDestructors]; #ifdef OF_OBJFW_RUNTIME /* * As the values returned by objc_autoreleasePoolPush() in the ObjFW * runtime are not actually pointers, but sequential numbers, 0 means * we pop everything. */ objc_autoreleasePoolPop(0); #endif [thread release]; return 0; } @implementation OFThread #if defined(OF_HAVE_PROPERTIES) && defined(OF_HAVE_BLOCKS) @synthesize block; #endif + (void)initialize { if (self != [OFThread class]) return; if (!of_tlskey_new(&threadSelfKey)) @throw [OFInitializationFailedException exceptionWithClass: self]; } + (instancetype)thread { return [[[self alloc] init] autorelease]; } #ifdef OF_HAVE_BLOCKS + (instancetype)threadWithBlock: (of_thread_block_t)block { return [[[self alloc] initWithBlock: block] autorelease]; } #endif + (void)setObject: (id)object forTLSKey: (OFTLSKey*)key { id oldObject = of_tlskey_get(key->key); if (!of_tlskey_set(key->key, [object retain])) @throw [OFInvalidArgumentException exceptionWithClass: self selector: _cmd]; [oldObject release]; } + (id)objectForTLSKey: (OFTLSKey*)key { return [[(id)of_tlskey_get(key->key) retain] autorelease]; } + (OFThread*)currentThread { return [[(id)of_tlskey_get(threadSelfKey) retain] autorelease]; } + (OFThread*)mainThread { return mainThread; } + (void)sleepForTimeInterval: (double)seconds { if (seconds < 0) @throw [OFOutOfRangeException exceptionWithClass: self]; #ifndef _WIN32 if (seconds > UINT_MAX) @throw [OFOutOfRangeException exceptionWithClass: self]; sleep((unsigned int)seconds); usleep((useconds_t)rint((seconds - floor(seconds)) * 1000000)); #else if (seconds * 1000 > UINT_MAX) @throw [OFOutOfRangeException exceptionWithClass: self]; Sleep((unsigned int)(seconds * 1000)); #endif } + (void)sleepUntilDate: (OFDate*)date { double seconds = [date timeIntervalSinceNow]; #ifndef _WIN32 if (seconds > UINT_MAX) @throw [OFOutOfRangeException exceptionWithClass: self]; sleep((unsigned int)seconds); usleep((useconds_t)rint((seconds - floor(seconds)) * 1000000)); #else if (seconds * 1000 > UINT_MAX) @throw [OFOutOfRangeException exceptionWithClass: self]; Sleep((unsigned int)(seconds * 1000)); #endif } + (void)yield { #ifdef OF_HAVE_SCHED_YIELD sched_yield(); #else [self sleepForTimeInterval: 0]; #endif } + (void)terminate { [self terminateWithObject: nil]; } + (void)terminateWithObject: (id)object { OFThread *thread = of_tlskey_get(threadSelfKey); if (thread != nil) { thread->returnValue = [object retain]; [thread handleTermination]; thread->running = OF_THREAD_WAITING_FOR_JOIN; } [OFTLSKey OF_callAllDestructors]; #ifdef OF_OBJFW_RUNTIME /* * As the values returned by objc_autoreleasePoolPush() in the ObjFW * runtime are not actually pointers, but sequential numbers, 0 means * we pop everything. */ objc_autoreleasePoolPop(0); #endif [thread release]; of_thread_exit(); } + (void)OF_createMainThread { mainThread = [[OFThread alloc] init]; mainThread->thread = of_thread_current(); if (!of_tlskey_set(threadSelfKey, mainThread)) @throw [OFInitializationFailedException exceptionWithClass: self]; } #ifdef OF_HAVE_BLOCKS - initWithBlock: (of_thread_block_t)block_ { self = [super init]; @try { block = [block_ copy]; } @catch (id e) { [self release]; @throw e; } return self; } #endif - (id)main { [[OFRunLoop currentRunLoop] run]; return nil; } - (void)handleTermination { OFRunLoop *oldRunLoop = runLoop; runLoop = nil; [oldRunLoop release]; } - (void)start { if (running == OF_THREAD_RUNNING) @throw [OFThreadStillRunningException exceptionWithClass: [self class] thread: self]; if (running == OF_THREAD_WAITING_FOR_JOIN) { of_thread_detach(thread); [returnValue release]; } [self retain]; running = OF_THREAD_RUNNING; if (!of_thread_new(&thread, call_main, self)) { [self release]; @throw [OFThreadStartFailedException exceptionWithClass: [self class] thread: self]; } } - (id)join { if (running == OF_THREAD_NOT_RUNNING || !of_thread_join(thread)) @throw [OFThreadJoinFailedException exceptionWithClass: [self class] thread: self]; running = OF_THREAD_NOT_RUNNING; return returnValue; } - (OFRunLoop*)runLoop { if (runLoop == nil) { OFRunLoop *tmp = [[OFRunLoop alloc] init]; if (!of_atomic_cmpswap_ptr((void**)&runLoop, nil, tmp)) [tmp release]; } return [[runLoop retain] autorelease]; } - (void)dealloc { if (running == OF_THREAD_RUNNING) @throw [OFThreadStillRunningException exceptionWithClass: [self class] thread: self]; /* * We should not be running anymore, but call detach in order to free * the resources. */ if (running == OF_THREAD_WAITING_FOR_JOIN) of_thread_detach(thread); [returnValue release]; [runLoop release]; [super dealloc]; } - copy { return [self retain]; } @end