More thread changes for MacOS-X compatibility

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@26340 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2008-03-18 05:45:05 +00:00
parent 7c710cfbcb
commit b6638d05aa
4 changed files with 140 additions and 51 deletions

View file

@ -1,3 +1,10 @@
2008-03-17 Richard Frith-Macdonald <rfm@gnu.org>
* Headers/Foundation/NSThread.h:
* Source/NSThread.m: Add ([+isMainThread]) and ([-isFinished]).
Also check that we are not trying to perform a selector on an invalid
thread finished.
2008-03-17 Richard Frith-Macdonald <rfm@gnu.org> 2008-03-17 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSRunLoop.m: * Source/NSRunLoop.m:

View file

@ -38,6 +38,17 @@
extern "C" { extern "C" {
#endif #endif
/**
* This class encapsulates OpenStep threading. See [NSLock] and its
* subclasses for handling synchronisation between threads.<br />
* Each process begins with a main thread and additional threads can
* be created using NSThread. The GNUstep implementation of OpenStep
* has been carefully designed so that the internals of the base
* library do not use threading (except for methods which explicitly
* deal with threads of course) so that you can write applications
* without threading. Non-threaded applications are more efficient
* (no locking is required) and are easier to debug during development.
*/
@interface NSThread : NSObject @interface NSThread : NSObject
{ {
@private @private
@ -48,6 +59,7 @@ extern "C" {
unsigned _stackSize; unsigned _stackSize;
BOOL _cancelled; BOOL _cancelled;
BOOL _active; BOOL _active;
BOOL _finished;
NSHandler *_exception_handler; // Not retained. NSHandler *_exception_handler; // Not retained.
NSMutableDictionary *_thread_dictionary; NSMutableDictionary *_thread_dictionary;
struct autorelease_thread_vars _autorelease_vars; struct autorelease_thread_vars _autorelease_vars;
@ -56,11 +68,46 @@ extern "C" {
void *_reserved; // For future expansion void *_reserved; // For future expansion
} }
/**
* <p>
* Returns the NSThread object corresponding to the current thread.
* </p>
* <p>
* NB. In GNUstep the library internals use the GSCurrentThread()
* function as a more efficient mechanism for doing this job - so
* you cannot use a category to override this method and expect
* the library internals to use your implementation.
* </p>
*/
+ (NSThread*) currentThread; + (NSThread*) currentThread;
/**
* <p>Create a new thread - use this method rather than alloc-init. The new
* thread will begin executing the message given by aSelector, aTarget, and
* anArgument. This should have no return value, and must set up an
* autorelease pool if retain/release memory management is used. It should
* free this pool before it finishes execution.</p>
*/
+ (void) detachNewThreadSelector: (SEL)aSelector + (void) detachNewThreadSelector: (SEL)aSelector
toTarget: (id)aTarget toTarget: (id)aTarget
withObject: (id)anArgument; withObject: (id)anArgument;
/**
* Terminates the current thread.<br />
* Normally you don't need to call this method explicitly,
* since exiting the method with which the thread was detached
* causes this method to be called automatically.
*/
+ (void) exit; + (void) exit;
/**
* Returns a flag to say whether the application is multi-threaded or not.<br />
* An application is considered to be multi-threaded if any thread other
* than the main thread has been started, irrespective of whether that
* thread has since terminated.<br />
* NB. This method returns YES if called within a handler processing
* <code>NSWillBecomeMultiThreadedNotification</code>
*/
+ (BOOL) isMultiThreaded; + (BOOL) isMultiThreaded;
+ (void) sleepUntilDate: (NSDate*)date; + (void) sleepUntilDate: (NSDate*)date;
@ -79,6 +126,11 @@ extern "C" {
*/ */
+ (NSArray*) callStackReturnAddresses; + (NSArray*) callStackReturnAddresses;
/** Returns a boolean indicating whether this thread is the main thread of
* the process.
*/
+ (BOOL) isMainThread;
/** Returns the main thread of the process. /** Returns the main thread of the process.
*/ */
+ (NSThread*) mainThread; + (NSThread*) mainThread;
@ -114,6 +166,11 @@ extern "C" {
*/ */
- (BOOL) isExecuting; - (BOOL) isExecuting;
/** Returns a boolean indicating whether the receiving
* thread has completed executing.
*/
- (BOOL) isFinished;
/** Returns a boolean indicating whether this thread is the main thread of /** Returns a boolean indicating whether this thread is the main thread of
* the process. * the process.
*/ */

View file

@ -279,6 +279,9 @@ typedef enum {
* from the runloop when the event/descriptor is triggered. * from the runloop when the event/descriptor is triggered.
*/ */
- (void) fire; - (void) fire;
/* Cancel all pending performers.
*/
- (void) invalidate;
@end @end
/* Return (and optionally create) GSRunLoopThreadInfo for the specified /* Return (and optionally create) GSRunLoopThreadInfo for the specified

View file

@ -103,6 +103,7 @@ static NSNotificationCenter *nc = nil;
SEL selector; SEL selector;
NSConditionLock *lock; // Not retained. NSConditionLock *lock; // Not retained.
NSArray *modes; NSArray *modes;
BOOL invalidated;
} }
+ (GSPerformHolder*) newForReceiver: (id)r + (GSPerformHolder*) newForReceiver: (id)r
argument: (id)a argument: (id)a
@ -110,6 +111,8 @@ static NSNotificationCenter *nc = nil;
modes: (NSArray*)m modes: (NSArray*)m
lock: (NSConditionLock*)l; lock: (NSConditionLock*)l;
- (void) fire; - (void) fire;
- (void) invalidate;
- (BOOL) isInvalidated;
- (NSArray*) modes; - (NSArray*) modes;
@end @end
@ -406,17 +409,6 @@ gnustep_base_thread_callback(void)
} }
/**
* This class encapsulates OpenStep threading. See [NSLock] and its
* subclasses for handling synchronisation between threads.<br />
* Each process begins with a main thread and additional threads can
* be created using NSThread. The GNUstep implementation of OpenStep
* has been carefully designed so that the internals of the base
* library do not use threading (except for methods which explicitly
* deal with threads of course) so that you can write applications
* without threading. Non-threaded applications are more efficient
* (no locking is required) and are easier to debug during development.
*/
@implementation NSThread @implementation NSThread
+ (NSArray*) callStackReturnAddresses + (NSArray*) callStackReturnAddresses
@ -426,17 +418,6 @@ gnustep_base_thread_callback(void)
return stack; return stack;
} }
/**
* <p>
* Returns the NSThread object corresponding to the current thread.
* </p>
* <p>
* NB. In GNUstep the library internals use the GSCurrentThread()
* function as a more efficient mechanism for doing this job - so
* you cannot use a category to override this method and expect
* the library internals to use your implementation.
* </p>
*/
+ (NSThread*) currentThread + (NSThread*) currentThread
{ {
NSThread *t = nil; NSThread *t = nil;
@ -462,13 +443,6 @@ gnustep_base_thread_callback(void)
return t; return t;
} }
/**
* <p>Create a new thread - use this method rather than alloc-init. The new
* thread will begin executing the message given by aSelector, aTarget, and
* anArgument. This should have no return value, and must set up an
* autorelease pool if retain/release memory management is used. It should
* free this pool before it finishes execution.</p>
*/
+ (void) detachNewThreadSelector: (SEL)aSelector + (void) detachNewThreadSelector: (SEL)aSelector
toTarget: (id)aTarget toTarget: (id)aTarget
withObject: (id)anArgument withObject: (id)anArgument
@ -487,13 +461,6 @@ gnustep_base_thread_callback(void)
RELEASE(thread); RELEASE(thread);
} }
/**
* Terminates the current thread.<br />
* Normally you don't need to call this method explicitly,
* since exiting the method with which the thread was detached
* causes this method to be called automatically.
*/
+ (void) exit + (void) exit
{ {
NSThread *t; NSThread *t;
@ -505,6 +472,7 @@ gnustep_base_thread_callback(void)
* Set the thread to be inactive to avoid any possibility of recursion. * Set the thread to be inactive to avoid any possibility of recursion.
*/ */
t->_active = NO; t->_active = NO;
t->_finished = YES;
/* /*
* Let observers know this thread is exiting. * Let observers know this thread is exiting.
@ -517,6 +485,8 @@ gnustep_base_thread_callback(void)
object: t object: t
userInfo: nil]; userInfo: nil];
[(GSRunLoopThreadInfo*)t->_runLoopInfo invalidate];
/* /*
* destroy the thread object. * destroy the thread object.
*/ */
@ -558,14 +528,11 @@ gnustep_base_thread_callback(void)
} }
} }
/** + (BOOL) isMainThread
* Returns a flag to say whether the application is multi-threaded or not.<br /> {
* An application is considered to be multi-threaded if any thread other return (GSCurrentThread() == defaultThread ? YES : NO);
* than the main thread has been started, irrespective of whether that }
* thread has since terminated.<br />
* NB. This method returns YES if called within a handler processing
* <code>NSWillBecomeMultiThreadedNotification</code>
*/
+ (BOOL) isMultiThreaded + (BOOL) isMultiThreaded
{ {
return entered_multi_threaded_state; return entered_multi_threaded_state;
@ -710,6 +677,7 @@ gnustep_base_thread_callback(void)
_exception_handler = NULL; _exception_handler = NULL;
_cancelled = NO; _cancelled = NO;
_active = NO; _active = NO;
_finished = NO;
_name = nil; _name = nil;
init_autorelease_thread_vars(&_autorelease_vars); init_autorelease_thread_vars(&_autorelease_vars);
return self; return self;
@ -725,6 +693,11 @@ gnustep_base_thread_callback(void)
return _active; return _active;
} }
- (BOOL) isFinished
{
return _finished;
}
- (BOOL) isMainThread - (BOOL) isMainThread
{ {
return (self == defaultThread ? YES : NO); return (self == defaultThread ? YES : NO);
@ -833,6 +806,13 @@ pthread_detach(pthread_self());
NSStringFromClass([self class]), NSStringFromClass([self class]),
NSStringFromSelector(_cmd)]; NSStringFromSelector(_cmd)];
} }
if (_finished == YES)
{
[NSException raise: NSInternalInconsistencyException
format: @"[%@-$@] called on finished thread",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd)];
}
/* Make sure the notification is posted BEFORE the new thread starts. /* Make sure the notification is posted BEFORE the new thread starts.
*/ */
@ -898,10 +878,9 @@ pthread_detach(pthread_self());
- (void) dealloc - (void) dealloc
{ {
[self invalidate];
DESTROY(lock); DESTROY(lock);
DESTROY(loop); DESTROY(loop);
[performers makeObjectsPerformSelector: @selector(invalidate)];
DESTROY(performers);
#ifdef __MINGW32__ #ifdef __MINGW32__
if (event != INVALID_HANDLE_VALUE) if (event != INVALID_HANDLE_VALUE)
{ {
@ -951,6 +930,14 @@ pthread_detach(pthread_self());
return self; return self;
} }
- (void) invalidate
{
[lock lock];
[performers makeObjectsPerformSelector: @selector(invalidate)];
[performers removeAllObjects];
[lock unlock];
}
- (void) fire - (void) fire
{ {
NSArray *toDo; NSArray *toDo;
@ -1054,11 +1041,7 @@ GSRunLoopInfoForThread(NSThread *aThread)
DESTROY(receiver); DESTROY(receiver);
DESTROY(argument); DESTROY(argument);
DESTROY(modes); DESTROY(modes);
if (lock == nil) if (lock != nil)
{
RELEASE(self);
}
else
{ {
NSConditionLock *l = lock; NSConditionLock *l = lock;
@ -1068,6 +1051,28 @@ GSRunLoopInfoForThread(NSThread *aThread)
} }
} }
- (void) invalidate
{
if (invalidated == NO)
{
invalidated = YES;
DESTROY(receiver);
if (lock != nil)
{
NSConditionLock *l = lock;
[lock lock];
lock = nil;
[l unlockWithCondition: 1];
}
}
}
- (BOOL) isInvalidated
{
return invalidated;
}
- (NSArray*) modes - (NSArray*) modes
{ {
return modes; return modes;
@ -1120,12 +1125,18 @@ GSRunLoopInfoForThread(NSThread *aThread)
info = GSRunLoopInfoForThread(aThread); info = GSRunLoopInfoForThread(aThread);
if (t == aThread) if (t == aThread)
{ {
/* Perform in current thread.
*/
if (aFlag == YES || info->loop == nil) if (aFlag == YES || info->loop == nil)
{ {
/* Wait until done or no run loop.
*/
[self performSelector: aSelector withObject: anObject]; [self performSelector: aSelector withObject: anObject];
} }
else else
{ {
/* Don't wait ... schedule operation in run loop.
*/
[info->loop performSelector: aSelector [info->loop performSelector: aSelector
target: self target: self
argument: anObject argument: anObject
@ -1138,6 +1149,11 @@ GSRunLoopInfoForThread(NSThread *aThread)
GSPerformHolder *h; GSPerformHolder *h;
NSConditionLock *l = nil; NSConditionLock *l = nil;
if ([t isFinished] == YES)
{
[NSException raise: NSInternalInconsistencyException
format: @"perform on finished thread"];
}
if (aFlag == YES) if (aFlag == YES)
{ {
l = [[NSConditionLock alloc] init]; l = [[NSConditionLock alloc] init];
@ -1154,6 +1170,12 @@ GSRunLoopInfoForThread(NSThread *aThread)
[l lockWhenCondition: 1]; [l lockWhenCondition: 1];
[l unlock]; [l unlock];
RELEASE(l); RELEASE(l);
if ([h isInvalidated] == YES)
{
[NSException raise: NSInternalInconsistencyException
format: @"perform on finished thread"];
RELEASE(h);
}
} }
RELEASE(h); RELEASE(h);
} }