From 4c5360c30854fc31363e5247f1e6935bb838a848 Mon Sep 17 00:00:00 2001 From: CaS Date: Wed, 30 Oct 2002 07:45:59 +0000 Subject: [PATCH] Implemented new MacOS-X methods git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@14874 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 6 + Headers/gnustep/base/NSThread.h | 12 ++ Source/NSRunLoop.m | 24 ++-- Source/NSThread.m | 202 +++++++++++++++++++++++++++++++- 4 files changed, 223 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3230059f5..a05966084 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2002-10-30 Richard Frith-Macdonald + + * Source/NSThread.m: Implemented and documented new MacOS-X methods - + ([-performSelectorOnMainThread:withObject:waitUntilDone:modes:]) + and ([-performSelectorOnMainThread:withObject:waitUntilDone:]) + Wed Oct 30 03:14:34 2002 Nicola Pero * Headers/gnustep/base/objc-load.h: Added copyright notice. diff --git a/Headers/gnustep/base/NSThread.h b/Headers/gnustep/base/NSThread.h index b02f57c03..2cec3f65b 100644 --- a/Headers/gnustep/base/NSThread.h +++ b/Headers/gnustep/base/NSThread.h @@ -60,6 +60,18 @@ @end +#ifndef STRICT_OPENSTEP +@interface NSObject(NSMainThreadPerformAdditions) +- (void) performSelectorOnMainThread: (SEL)aSelector + withObject: (id)anObject + waitUntilDone: (BOOL)aFlag + modes: (NSArray*)anArray; +- (void) performSelectorOnMainThread: (SEL)aSelector + withObject: (id)anObject + waitUntilDone: (BOOL)aFlag; +@end +#endif + #ifndef NO_GNUSTEP /* * Don't use the following functions unless you really know what you are diff --git a/Source/NSRunLoop.m b/Source/NSRunLoop.m index b28dbdc3d..c9ed8e615 100644 --- a/Source/NSRunLoop.m +++ b/Source/NSRunLoop.m @@ -1628,22 +1628,9 @@ if (0) { + (NSRunLoop*) currentRunLoop { - static NSString *key = @"NSRunLoopThreadKey"; - NSMutableDictionary *d; - NSRunLoop *r; + extern NSRunLoop *GSRunLoopForThread(); - d = GSCurrentThreadDictionary(); - r = [d objectForKey: key]; - if (r == nil) - { - if (d != nil) - { - r = [self new]; - [d setObject: r forKey: key]; - RELEASE(r); - } - } - return r; + return GSRunLoopForThread(nil); } /* This is the designated initializer. */ @@ -2206,6 +2193,13 @@ if (0) { /* Nothing to do here */ } +/** + * Sets up repeated sending of aSelector to target with argument.
+ * The selector is sent in each runloop iteration until cancelled.
+ * The target and argument objects are not retained.
+ * The order value is used to determine the order in which messages + * are sent if multiple messages have been set up. + */ - (void) performSelector: (SEL)aSelector target: (id)target argument: (id)argument diff --git a/Source/NSThread.m b/Source/NSThread.m index 0759fbfd7..59373fed1 100644 --- a/Source/NSThread.m +++ b/Source/NSThread.m @@ -39,6 +39,8 @@ #include #include #include +#include +#include static Class threadClass = Nil; static NSNotificationCenter *nc = nil; @@ -133,26 +135,62 @@ GSCurrentThread() * Fast access function for thread dictionary of current thread. */ NSMutableDictionary* -GSCurrentThreadDictionary() +GSDictionaryForThread(NSThread *t) { - NSThread *thread = GSCurrentThread(); - - if (thread == nil) + if (t == nil) + { + t = GSCurrentThread(); + } + if (t == nil) { return nil; } else { - NSMutableDictionary *dict = thread->_thread_dictionary; + NSMutableDictionary *dict = t->_thread_dictionary; if (dict == nil) { - dict = [thread threadDictionary]; + dict = [t threadDictionary]; } return dict; } } +/** + * Fast access function for thread dictionary of current thread. + */ +NSMutableDictionary* +GSCurrentThreadDictionary() +{ + return GSDictionaryForThread(nil); +} + +/** + * Returns the run loop for the specified thread (or, if t is nil, + * for the current thread). Creates a new run loop if necessary.
+ * Returns nil on failure. + */ +NSRunLoop* +GSRunLoopForThread(NSThread *t) +{ + static NSString *key = @"NSRunLoopThreadKey"; + NSMutableDictionary *d = GSDictionaryForThread(t); + NSRunLoop *r; + + r = [d objectForKey: key]; + if (r == nil) + { + if (d != nil) + { + r = [NSRunLoop new]; + [d setObject: r forKey: key]; + RELEASE(r); + } + } + return r; +} + /* * Callback function so send notifications on becoming multi-threaded. */ @@ -514,6 +552,158 @@ gnustep_base_thread_callback() @end + + +@interface GSPerformHolder : NSObject +{ + id receiver; + id argument; + SEL selector; + NSLock *lock; +} ++ (GSPerformHolder*) newForReceiver: (id)r + argument: (id)a + selector: (SEL)s + lock: (NSConditionLock*)l; +- (void) fire; +@end + +@implementation GSPerformHolder + ++ (GSPerformHolder*) newForReceiver: (id)r + argument: (id)a + selector: (SEL)s + lock: (NSConditionLock*)l +{ + GSPerformHolder *h; + + h = (GSPerformHolder*)NSAllocateObject(self, 0, NSDefaultMallocZone()); + h->receiver = RETAIN(r); + h->argument = RETAIN(a); + h->selector = s; + if (l != nil) + { + h->lock = RETAIN(l); + [h->lock lock]; // Lock until fire. + } + return h; +} + +- (void) dealloc +{ + DESTROY(receiver); + DESTROY(argument); + if (lock != nil) + { + [lock unlock]; + DESTROY(lock); + } + NSDeallocateObject(self); +} + +- (void) fire +{ + [GSRunLoopForThread(defaultThread) cancelPerformSelectorsWithTarget: self]; + [receiver performSelector: selector withObject: argument]; + if (lock == nil) + { + RELEASE(self); + } + else + { + [lock unlock]; + DESTROY(lock); + } +} +@end + +@implementation NSObject(NSMainThreadPerformAdditions) + +/** + *

This method performs aSelector on the receiver, passing anObject as + * an argument, but does so in the main thread of the program. The receiver + * and anObject are both retained until the method is performed. + *

+ *

The selector is performed when the runloop of the main thread is in + * one of the modes specified in anArray, or if there are no modes in + * anArray, the method has no effect and simply returns immediately. + *

+ *

The argument aFlag specifies whether the method should wait until + * the selector has been performed before returning.
+ * NB. This method does not cause the run loop of + * the main thread to be run ... so if the run loop is not executed by some + * code in the main thread, the thread waiting for the perform to complete + * will block forever. + *

+ *

As a special case, if aFlag == YES and the current thread is the main + * thread, the modes array is ignord and the selector is performed immediately. + * This behavior is necessary to avoid the main thread being blocked by + * waiting for a perform which will never happen because the run loop is + * not executing. + *

+ */ +- (void) performSelectorOnMainThread: (SEL)aSelector + withObject: (id)anObject + waitUntilDone: (BOOL)aFlag + modes: (NSArray*)anArray +{ + NSThread *t = GSCurrentThread(); + + if (aFlag == YES && (t == defaultThread)) + { + [self performSelector: aSelector withObject: anObject]; + } + else + { + NSRunLoop *r = GSRunLoopForThread(defaultThread); + NSConditionLock *l = nil; + GSPerformHolder *h; + + if (aFlag == YES) + { + l = [NSConditionLock new]; + } + + h = [GSPerformHolder newForReceiver: self + argument: anObject + selector: aSelector + lock: l]; + + [r performSelector: @selector(fire) + target: h + argument: nil + order: 0 + modes: anArray]; + + if (aFlag == YES) + { + [l lockBeforeDate: [NSDate distantFuture]]; + } + RELEASE(h); + } +} + +/** + * Invokes -performSelectorOnMainThread:withObject:waitUntilDone:modes: + * using the supplied arguments and an array containing common modes. + */ +- (void) performSelectorOnMainThread: (SEL)aSelector + withObject: (id)anObject + waitUntilDone: (BOOL)aFlag +{ + static NSArray *commonModes = nil; + + if (commonModes == nil) + { + commonModes = [[NSArray alloc] initWithObjects: + NSDefaultRunLoopMode, NSConnectionReplyMode, nil]; + } + [self performSelectorOnMainThread: aSelector + withObject: anObject + waitUntilDone: aFlag + modes: commonModes]; +} +@end typedef struct { @defs(NSThread) } NSThread_ivars;