Artifact ed37db867bb256d7d0f9e4016522664d5b2fe959048ba7b3dca82d3b06f0f316:
- File
src/OFThread.m
— part of check-in
[48980f2297]
at
2015-11-29 11:43:05
on branch trunk
— Make properties a requirement and clean up code
This increases the required GCC version from 4.0 to 4.6 (exception:
Apple GCC, which already supports this with >= 4.0 starting with OS X
10.5). Since even GCC 4.6 is really old by now, there is no point in
still supporting something even older and making the code ugly because
of that. While some hardware and OS support was dropped from GCC 4.6
compared to GCC 4.0, there is nothing in there that would be an
interesting target with the exception of BeOS maybe - but a port to BeOS
can also be achieved using the Haiku support. The other dropped OSes are
mostly old versions of OSes while newer ones are still being supported
(and those newer versions of those OSes still support the same
hardware). (user: js, size: 8707) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 * 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. */ #define OF_THREAD_M #define _POSIX_TIMERS #define __NO_EXT_QNX #include "config.h" #include <stdlib.h> #include <math.h> #include <time.h> /* Work around __block being used by glibc */ #ifdef __GLIBC__ # undef __USE_XOPEN #endif #include "platform.h" #ifndef OF_WINDOWS # include <unistd.h> #endif #ifdef OF_HAVE_SCHED_YIELD # include <sched.h> #endif #ifdef OF_WII # define BOOL OGC_BOOL # define nanosleep ogc_nanosleep # include <ogcsys.h> # undef BOOL # undef nanosleep #endif #import "OFThread.h" #import "OFThread+Private.h" #import "OFRunLoop.h" #import "OFList.h" #import "OFDate.h" #import "OFDictionary.h" #import "OFAutoreleasePool.h" #import "OFAutoreleasePool+Private.h" #ifdef OF_WINDOWS # include <windows.h> #endif #ifdef OF_NINTENDO_DS # define asm __asm__ # include <nds.h> # undef asm #endif #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFNotImplementedException.h" #import "OFOutOfRangeException.h" #ifdef OF_HAVE_THREADS # import "OFThreadJoinFailedException.h" # import "OFThreadStartFailedException.h" # import "OFThreadStillRunningException.h" #endif #ifdef OF_HAVE_ATOMIC_OPS # import "atomic.h" #endif #ifdef __DJGPP__ # define lrint(x) rint(x) # define useconds_t unsigned int #endif #ifdef OF_HAVE_THREADS # import "threading.h" static of_tlskey_t threadSelfKey; static OFThread *mainThread; static void callMain(id object) { OFThread *thread = (OFThread*)object; if (!of_tlskey_set(threadSelfKey, thread)) @throw [OFInitializationFailedException exceptionWithClass: [thread class]]; thread->_pool = objc_autoreleasePoolPush(); /* * Nasty workaround for thread implementations which can't return a * pointer on join. */ # ifdef OF_HAVE_BLOCKS if (thread->_threadBlock != NULL) thread->_returnValue = [thread->_threadBlock() retain]; else # endif thread->_returnValue = [[thread main] retain]; [thread handleTermination]; thread->_running = OF_THREAD_WAITING_FOR_JOIN; objc_autoreleasePoolPop(thread->_pool); [OFAutoreleasePool OF_handleThreadTermination]; [thread release]; } #endif @implementation OFThread #ifdef OF_HAVE_THREADS # ifdef OF_HAVE_BLOCKS @synthesize threadBlock = _threadBlock; # 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)threadWithThreadBlock: (of_thread_block_t)threadBlock { return [[[self alloc] initWithThreadBlock: threadBlock] autorelease]; } # endif + (OFThread*)currentThread { return of_tlskey_get(threadSelfKey); } + (OFThread*)mainThread { return mainThread; } + (OFMutableDictionary*)threadDictionary { OFThread *thread = of_tlskey_get(threadSelfKey); if (thread->_threadDictionary == nil) thread->_threadDictionary = [[OFMutableDictionary alloc] init]; return thread->_threadDictionary; } #endif + (void)sleepForTimeInterval: (of_time_interval_t)timeInterval { if (timeInterval < 0) return; #if defined(OF_WINDOWS) if (timeInterval * 1000 > UINT_MAX) @throw [OFOutOfRangeException exception]; Sleep((unsigned int)(timeInterval * 1000)); #elif defined(HAVE_NANOSLEEP) struct timespec rqtp; rqtp.tv_sec = (time_t)timeInterval; rqtp.tv_nsec = lrint((timeInterval - rqtp.tv_sec) * 1000000000); if (rqtp.tv_sec != floor(timeInterval)) @throw [OFOutOfRangeException exception]; nanosleep(&rqtp, NULL); #elif defined(OF_NINTENDO_DS) uint64_t counter; if (timeInterval > UINT64_MAX / 60) @throw [OFOutOfRangeException exception]; counter = timeInterval * 60; while (counter--) swiWaitForVBlank(); #else if (timeInterval > UINT_MAX) @throw [OFOutOfRangeException exception]; sleep((unsigned int)timeInterval); usleep((useconds_t)lrint( (timeInterval - floor(timeInterval)) * 1000000)); #endif } + (void)sleepUntilDate: (OFDate*)date { [self sleepForTimeInterval: [date timeIntervalSinceNow]]; } + (void)yield { #ifdef OF_HAVE_SCHED_YIELD sched_yield(); #else [self sleepForTimeInterval: 0]; #endif } #ifdef OF_HAVE_THREADS + (void)terminate { [self terminateWithObject: nil]; /* * For some reason, Clang thinks terminateWithObject: can return - even * though it is declared OF_NO_RETURN - and warns that terminate * returns while being declared OF_NO_RETURN. */ OF_UNREACHABLE } + (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; objc_autoreleasePoolPop(thread->_pool); } [OFAutoreleasePool OF_handleThreadTermination]; [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]; } - init { self = [super init]; @try { if (!of_thread_attr_init(&_attr)) @throw [OFInitializationFailedException exceptionWithClass: [self class]]; } @catch (id e) { [self release]; @throw e; } return self; } # ifdef OF_HAVE_BLOCKS - initWithThreadBlock: (of_thread_block_t)threadBlock { self = [self init]; @try { _threadBlock = [threadBlock 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]; [_threadDictionary release]; _threadDictionary = nil; } - (void)start { if (_running == OF_THREAD_RUNNING) @throw [OFThreadStillRunningException exceptionWithThread: 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, callMain, self, &_attr)) { [self release]; @throw [OFThreadStartFailedException exceptionWithThread: self]; } if (_name != nil) of_thread_set_name(_thread, [_name UTF8String]); else of_thread_set_name(_thread, class_getName([self class])); } - (id)join { if (_running == OF_THREAD_NOT_RUNNING || !of_thread_join(_thread)) @throw [OFThreadJoinFailedException exceptionWithThread: self]; _running = OF_THREAD_NOT_RUNNING; return _returnValue; } - copy { return [self retain]; } - (OFRunLoop*)runLoop { # ifdef OF_HAVE_ATOMIC_OPS if (_runLoop == nil) { OFRunLoop *tmp = [[OFRunLoop alloc] init]; if (!of_atomic_ptr_cmpswap((void**)&_runLoop, nil, tmp)) [tmp release]; } # else @synchronized (self) { if (_runLoop == nil) _runLoop = [[OFRunLoop alloc] init]; } # endif return [[_runLoop retain] autorelease]; } - (OFString*)name { return [[_name copy] autorelease]; } - (void)setName: (OFString*)name { OFString *old = name; _name = [name copy]; [old release]; if (_running == OF_THREAD_RUNNING) of_thread_set_name(_thread, (_name != nil ? [_name UTF8String] : class_getName([self class]))); } - (float)priority { return _attr.priority; } - (void)setPriority: (float)priority { if (_running == OF_THREAD_RUNNING) @throw [OFThreadStillRunningException exceptionWithThread: self]; _attr.priority = priority; } - (size_t)stackSize { return _attr.stackSize; } - (void)setStackSize: (size_t)stackSize { if (_running == OF_THREAD_RUNNING) @throw [OFThreadStillRunningException exceptionWithThread: self]; _attr.stackSize = stackSize; } - (void)dealloc { if (_running == OF_THREAD_RUNNING) @throw [OFThreadStillRunningException exceptionWithThread: 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]; # ifdef OF_HAVE_BLOCKS [_threadBlock release]; # endif [super dealloc]; } #else - init { OF_INVALID_INIT_METHOD } #endif @end