Complete update of NSThread for MacOS-X 10.5 compatibility. Needs testing.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@26332 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2008-03-17 15:23:11 +00:00
parent 27a50601c5
commit 5d8174ac84
9 changed files with 454 additions and 300 deletions

View file

@ -1,3 +1,18 @@
2008-03-17 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSRunLoop.m:
* Source/unix/GSRunLoopCtxt.m:
* Source/GSRunLoopCtxt.h:
* Source/GSPrivate.h:
* Source/win32/GSRunLoopCtxt.m:
* Source/NSConnection.m:
* Source/NSThread.m:
* Headers/Foundation/NSThread.h:
Got round to committing the thread changes held back for last release.
Beware ... this code is not properly tested yet. The idea is that it
should provide the new (in MacOS-X 10.5) methods to support performing
of selectors in other threads.
2008-03-17 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSPort.m:

View file

@ -66,12 +66,14 @@ extern "C" {
- (NSMutableDictionary*) threadDictionary;
#if OS_API_VERSION(100200,GS_API_LATEST) && GS_API_VERSION(010200,GS_API_LATEST)
#if OS_API_VERSION(MAC_OS_X_VERSION_10_2,GS_API_LATEST) \
&& GS_API_VERSION(010200,GS_API_LATEST)
+ (void) setThreadPriority: (double)pri;
+ (double) threadPriority;
#endif
#if OS_API_VERSION(100500,GS_API_LATEST) && GS_API_VERSION(011501,GS_API_LATEST)
#if OS_API_VERSION(MAC_OS_X_VERSION_10_5,GS_API_LATEST) \
&& GS_API_VERSION(011501,GS_API_LATEST)
/** Returns an array of the call stack return addresses.
*/
@ -144,17 +146,105 @@ extern "C" {
@end
#if GS_API_VERSION(GS_API_MACOSX, GS_API_LATEST)
@interface NSObject(NSMainThreadPerformAdditions)
/**
* Extra methods to permit messages to be sent to an object such that they
* are executed in <em>another</em> thread.<br />
* The main thread is the thread in which the GNUstep system is started,
* and where the GNUstep gui is used, it is the thread in which gui
* drawing operations <strong>must</strong> be performed.
*/
@interface NSObject(NSThreadPerformAdditions)
#if GS_API_VERSION(MAC_OS_X_VERSION_10_2, GS_API_LATEST)
/**
* <p>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.
* </p>
* <p>The selector is performed when the runloop of the main thread next
* runs in one of the modes specified in anArray.<br />
* Where this method has been called more than once before the runloop
* of the main thread runs in the required mode, the order in which the
* operations in the main thread is done is the same as that in which
* they were added using this method.
* </p>
* <p>If there are no modes in anArray,
* the method has no effect and simply returns immediately.
* </p>
* <p>The argument aFlag specifies whether the method should wait until
* the selector has been performed before returning.<br />
* <strong>NB.</strong> This method does <em>not</em> cause the runloop of
* the main thread to be run ... so if the runloop is not executed by some
* code in the main thread, the thread waiting for the perform to complete
* will block forever.
* </p>
* <p>As a special case, if aFlag == YES and the current thread is the main
* thread, the modes array is ignored 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 runloop is
* not executing.
* </p>
*/
- (void) performSelectorOnMainThread: (SEL)aSelector
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
modes: (NSArray*)anArray;
/**
* Invokes -performSelectorOnMainThread:withObject:waitUntilDone:modes:
* using the supplied arguments and an array containing common modes.<br />
* These modes consist of NSRunLoopMode, NSConnectionreplyMode, and if
* in an application, the NSApplication modes.
*/
- (void) performSelectorOnMainThread: (SEL)aSelector
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag;
@end
#endif
#if GS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST)
/**
* <p>This method performs aSelector on the receiver, passing anObject as
* an argument, but does so in the specified thread. The receiver
* and anObject are both retained until the method is performed.
* </p>
* <p>The selector is performed when the runloop of aThread next
* runs in one of the modes specified in anArray.<br />
* Where this method has been called more than once before the runloop
* of the thread runs in the required mode, the order in which the
* operations in the thread is done is the same as that in which
* they were added using this method.
* </p>
* <p>If there are no modes in anArray,
* the method has no effect and simply returns immediately.
* </p>
* <p>The argument aFlag specifies whether the method should wait until
* the selector has been performed before returning.<br />
* <strong>NB.</strong> This method does <em>not</em> cause the runloop of
* aThread to be run ... so if the runloop is not executed by some
* code in aThread, the thread waiting for the perform to complete
* will block forever.
* </p>
* <p>As a special case, if aFlag == YES and the current thread is aThread,
* the modes array is ignored and the selector is performed immediately.
* This behavior is necessary to avoid the current thread being blocked by
* waiting for a perform which will never happen because the runloop is
* not executing.
* </p>
*/
- (void) performSelector: (SEL)aSelector
onThread: (NSThread*)aThread
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
modes: (NSArray*)anArray;
/**
* Invokes -performSelector:onThread:withObject:waitUntilDone:modes:
* using the supplied arguments and an array containing common modes.<br />
* These modes consist of NSRunLoopMode, NSConnectionreplyMode, and if
* in an application, the NSApplication modes.
*/
- (void) performSelector: (SEL)aSelector
onThread: (NSThread*)aThread
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag;
#endif
@end
#if GS_API_VERSION(GS_API_NONE, GS_API_NONE)
/*

View file

@ -252,6 +252,43 @@ typedef enum {
+ (NSError*) _systemError: (long)number;
@end
@class NSRunLoop;
@class NSLock;
@class NSThread;
/* Used to handle events performed in one thread from another.
*/
@interface GSRunLoopThreadInfo : NSObject
{
@public
NSRunLoop *loop;
NSLock *lock;
NSMutableArray *performers;
#ifdef __MINGW32__
HANDLE event;
#else
int inputFd;
int outputFd;
#endif
}
/* Add a performer to be run in the loop's thread. May be called from
* any thread.
*/
- (void) addPerformer: (id)performer;
/* Fire all pending performers in the current thread. May only be called
* from the runloop when the event/descriptor is triggered.
*/
- (void) fire;
@end
/* Return (and optionally create) GSRunLoopThreadInfo for the specified
* thread (or the current thread if aThread is nil).<br />
* If aThread is nil and no value is set for the current thread, create
* a GSRunLoopThreadInfo and set it for the current thread.
*/
GSRunLoopThreadInfo *
GSRunLoopInfoForThread(NSThread *aThread) GS_ATTRIB_PRIVATE;
/* Used by NSException uncaught exception handler - must not call any
* methods/functions which might cause a recursive exception.
*/

View file

@ -66,8 +66,4 @@ typedef struct{
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts;
@end
@interface NSRunLoop (Housekeeper)
- (void) _setHousekeeper: (NSTimer*)timer;
@end
#endif /* __GSRunLoopCtxt_h_GNUSTEP_BASE_INCLUDE */

View file

@ -84,6 +84,24 @@
#include "Foundation/NSDebug.h"
#include "GSInvocation.h"
#include "GSPortPrivate.h"
#include "GSPrivate.h"
static inline NSRunLoop *
GSRunLoopForThread(NSThread *aThread)
{
GSRunLoopThreadInfo *info = GSRunLoopInfoForThread(aThread);
if (info == nil || info->loop == nil)
{
if (aThread == nil)
{
return [NSRunLoop currentRunLoop];
}
return nil;
}
return info->loop;
}
@interface NSPortCoder (Private)
- (NSMutableArray*) _components;
@ -102,8 +120,6 @@
- (const char *) typeForSelector: (SEL)sel remoteTarget: (unsigned)target;
@end
extern NSRunLoop *GSRunLoopForThread(NSThread*);
#define F_LOCK(X) {NSDebugFLLog(@"GSConnection",@"Lock %@",X);[X lock];}
#define F_UNLOCK(X) {NSDebugFLLog(@"GSConnection",@"Unlock %@",X);[X unlock];}
#define M_LOCK(X) {NSDebugMLLog(@"GSConnection",@"Lock %@",X);[X lock];}

View file

@ -43,6 +43,7 @@
#include "Foundation/NSStream.h"
#include "Foundation/NSThread.h"
#include "Foundation/NSDebug.h"
#include "Foundation/NSInvocation.h"
#include "GSRunLoopCtxt.h"
#include "GSRunLoopWatcher.h"
#include "GSStream.h"
@ -667,9 +668,61 @@ static inline BOOL timerInvalidated(NSTimer *t)
*/
+ (NSRunLoop*) currentRunLoop
{
extern NSRunLoop *GSRunLoopForThread();
GSRunLoopThreadInfo *info = GSRunLoopInfoForThread(nil);
NSRunLoop *current = info->loop;
return GSRunLoopForThread(nil);
if (current == nil)
{
current = info->loop = [self new];
/* If this is the main thread, set up a housekeeping timer.
*/
if ([GSCurrentThread() isMainThread] == YES)
{
CREATE_AUTORELEASE_POOL (arp);
GSRunLoopCtxt *context;
NSNotificationCenter *ctr;
NSNotification *not;
NSInvocation *inv;
NSTimer *timer;
SEL sel;
ctr = [NSNotificationCenter defaultCenter];
not = [NSNotification notificationWithName: @"GSHousekeeping"
object: nil
userInfo: nil];
sel = @selector(postNotification:);
inv = [NSInvocation invocationWithMethodSignature:
[ctr methodSignatureForSelector: sel]];
[inv setTarget: ctr];
[inv setSelector: sel];
[inv setArgument: &not atIndex: 2];
[inv retainArguments];
context = NSMapGet(current->_contextMap, NSDefaultRunLoopMode);
if (context == nil)
{
context = [GSRunLoopCtxt alloc];
context = [context initWithMode: NSDefaultRunLoopMode
extra: current->_extra];
NSMapInsert(current->_contextMap, context->mode, context);
RELEASE(context);
}
if (context->housekeeper != timer)
{
[context->housekeeper invalidate];
DESTROY(context->housekeeper);
}
timer = [[NSTimer alloc] initWithFireDate: nil
interval: 30.0
target: inv
selector: NULL
userInfo: nil
repeats: YES];
context->housekeeper = timer;
RELEASE(arp);
}
}
return current;
}
/* This is the designated initializer. */
@ -1404,28 +1457,3 @@ static inline BOOL timerInvalidated(NSTimer *t)
@end
@implementation NSRunLoop (Housekeeper)
- (void) _setHousekeeper: (NSTimer*)timer
{
GSRunLoopCtxt *context;
context = NSMapGet(_contextMap, NSDefaultRunLoopMode);
if (context == nil)
{
context = [[GSRunLoopCtxt alloc] initWithMode: NSDefaultRunLoopMode
extra: _extra];
NSMapInsert(_contextMap, context->mode, context);
RELEASE(context);
}
if (context->housekeeper != timer)
{
[context->housekeeper invalidate];
DESTROY(context->housekeeper);
}
if (timer != nil)
{
context->housekeeper = RETAIN(timer);
}
}
@end

View file

@ -101,20 +101,16 @@ static NSNotificationCenter *nc = nil;
id receiver;
id argument;
SEL selector;
NSArray *modes;
NSConditionLock *lock; // Not retained.
NSArray *modes;
}
+ (BOOL) isValid;
+ (GSPerformHolder*) newForReceiver: (id)r
argument: (id)a
selector: (SEL)s
modes: (NSArray*)m
lock: (NSConditionLock*)l;
+ (void) receivedEvent: (void*)data
type: (RunLoopEventType)type
extra: (void*)extra
forMode: (NSString*)mode;
- (void) fire;
- (NSArray*) modes;
@end
/**
@ -354,75 +350,6 @@ GSCurrentThreadDictionary(void)
return GSDictionaryForThread(nil);
}
/*
* The special timer which we set up in the run loop of the main thread
* to perform housekeeping duties. NSRunLoop needs to call this private
* function so it knows about the housekeeping timer and won't keep the
* loop running just to do housekeeping.
*
* The NSUserDefaults system registers as an observer of GSHousekeeping
* notifications in order to synchronise the in-memory cache and the
* on-disk database.
*/
static NSTimer *housekeeper = nil;
/**
* Returns the runloop for the specified thread (or, if t is nil,
* for the current thread).<br />
* Creates a new runloop if necessary,
* as long as the thread dictionary exists.<br />
* 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);
if (housekeeper == nil && (t == nil || t == defaultThread))
{
CREATE_AUTORELEASE_POOL (arp);
NSNotificationCenter *ctr;
NSNotification *not;
NSInvocation *inv;
SEL sel;
ctr = [NSNotificationCenter defaultCenter];
not = [NSNotification notificationWithName: @"GSHousekeeping"
object: nil
userInfo: nil];
sel = @selector(postNotification:);
inv = [NSInvocation invocationWithMethodSignature:
[ctr methodSignatureForSelector: sel]];
[inv setTarget: ctr];
[inv setSelector: sel];
[inv setArgument: &not atIndex: 2];
[inv retainArguments];
housekeeper = [[NSTimer alloc] initWithFireDate: nil
interval: 30.0
target: inv
selector: NULL
userInfo: nil
repeats: YES];
[r _setHousekeeper: housekeeper];
RELEASE(housekeeper);
RELEASE(arp);
}
}
}
return r;
}
/*
* Callback function so send notifications on becoming multi-threaded.
*/
@ -722,6 +649,13 @@ gnustep_base_thread_callback(void)
[NSException raise: NSInternalInconsistencyException
format: @"Deallocating an active thread without [+exit]!"];
}
if (_runLoopInfo != 0)
{
GSRunLoopThreadInfo *info = (GSRunLoopThreadInfo*)_runLoopInfo;
_runLoopInfo = 0;
[info release];
}
DESTROY(_thread_dictionary);
DESTROY(_target);
DESTROY(_arg);
@ -943,73 +877,135 @@ pthread_detach(pthread_self());
@implementation GSPerformHolder
@implementation GSRunLoopThreadInfo
- (void) addPerformer: (id)performer
{
[lock lock];
[performers addObject: performer];
[lock unlock];
#if defined(__MINGW32__)
if (SetEvent(event) == 0)
{
NSLog(@"Set event failed - %@", [NSError _last]);
}
#else
if (write(outputFd, "0", 1) != 1)
{
NSLog(@"Write to pipe failed - %@", [NSError _last]);
}
#endif
}
static NSLock *subthreadsLock = nil;
- (void) dealloc
{
DESTROY(lock);
DESTROY(loop);
[performers makeObjectsPerformSelector: @selector(invalidate)];
DESTROY(performers);
#ifdef __MINGW32__
static HANDLE event;
if (event != INVALID_HANDLE_VALUE)
{
CloseHandle(event);
}
#else
static int inputFd = -1;
static int outputFd = -1;
#endif
static NSMutableArray *perfArray = nil;
static NSDate *theFuture;
if (inputFd >= 0)
{
close(inputFd);
inputFd = -1;
}
if (outputFd >= 0)
{
close(outputFd);
outputFd = -1;
}
#endif
[super dealloc];
}
+ (void) initialize
- (id) init
{
NSRunLoop *loop = GSRunLoopForThread(defaultThread);
NSArray *m = commonModes();
unsigned count = [m count];
unsigned i;
theFuture = RETAIN([NSDate distantFuture]);
subthreadsLock = [[NSLock alloc] init];
perfArray = [[NSMutableArray alloc] initWithCapacity: 10];
#ifndef __MINGW32__
{
int fd[2];
if (pipe(fd) == 0)
{
inputFd = fd[0];
outputFd = fd[1];
}
else
{
[NSException raise: NSInternalInconsistencyException
format: @"Failed to create pipe to handle perform in main thread"];
}
for (i = 0; i < count; i++)
{
[loop addEvent: (void*)(intptr_t)inputFd
type: ET_RDESC
watcher: (id<RunLoopEvents>)self
forMode: [m objectAtIndex: i]];
}
}
#ifdef __MINGW32__
if ((event = CreateEvent(NULL, TRUE, FALSE, NULL)) == INVALID_HANDLE_VALUE)
{
RELEASE(self);
[NSException raise: NSInternalInconsistencyException
format: @"Failed to create event to handle perform in thread"];
}
#else
{
if ((event = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
{
[NSException raise: NSInternalInconsistencyException
format: @"Failed to create event to handle perform in main thread"];
}
for (i = 0; i < count; i++)
{
[loop addEvent: (void*)event
type: ET_HANDLE
watcher: (id<RunLoopEvents>)self
forMode: [m objectAtIndex: i]];
}
}
int fd[2];
if (pipe(fd) == 0)
{
inputFd = fd[0];
outputFd = fd[1];
}
else
{
RELEASE(self);
[NSException raise: NSInternalInconsistencyException
format: @"Failed to create pipe to handle perform in thread"];
}
#endif
lock = [NSLock new];
performers = [NSMutableArray new];
return self;
}
+ (BOOL) isValid
- (void) fire
{
return YES;
NSArray *toDo;
unsigned int i;
unsigned int c;
#if defined(__MINGW32__)
if (ResetEvent(event) == 0)
{
NSLog(@"Reset event failed - %@", [NSError _last]);
}
#else
if (read(inputFd, &c, 1) != 1)
{
NSLog(@"Read pipe failed - %@", [NSError _last]);
}
#endif
[lock lock];
toDo = [NSArray arrayWithArray: performers];
[performers removeAllObjects];
[lock unlock];
c = [toDo count];
for (i = 0; i < c; i++)
{
GSPerformHolder *h = [toDo objectAtIndex: i];
[loop performSelector: @selector(fire)
target: h
argument: nil
order: 0
modes: [h modes]];
}
}
@end
GSRunLoopThreadInfo *
GSRunLoopInfoForThread(NSThread *aThread)
{
GSRunLoopThreadInfo *info;
if (aThread == nil)
{
aThread = GSCurrentThread();
if (((NSThread_ivars*)aThread)->_runLoopInfo == nil)
{
((NSThread_ivars*)aThread)->_runLoopInfo = [GSRunLoopThreadInfo new];
}
}
info = ((NSThread_ivars*)aThread)->_runLoopInfo;
return info;
}
@implementation GSPerformHolder
+ (GSPerformHolder*) newForReceiver: (id)r
argument: (id)a
@ -1026,69 +1022,9 @@ static NSDate *theFuture;
h->modes = RETAIN(m);
h->lock = l;
[subthreadsLock lock];
[perfArray addObject: h];
#if defined(__MINGW32__)
if (SetEvent(event) == 0)
{
NSLog(@"Set event failed - %@", [NSError _last]);
}
#else
if (write(outputFd, "0", 1) != 1)
{
NSLog(@"Write to pipe failed - %@", [NSError _last]);
}
#endif
[subthreadsLock unlock];
return h;
}
+ (void) receivedEvent: (void*)data
type: (RunLoopEventType)type
extra: (void*)extra
forMode: (NSString*)mode
{
NSRunLoop *loop = [NSRunLoop currentRunLoop];
NSArray *toDo;
unsigned int i;
unsigned int c;
[subthreadsLock lock];
#if defined(__MINGW32__)
if (ResetEvent(event) == 0)
{
NSLog(@"Reset event failed - %@", [NSError _last]);
}
#else
if (read(inputFd, &c, 1) != 1)
{
NSLog(@"Read pipe failed - %@", [NSError _last]);
}
#endif
toDo = [[NSArray alloc] initWithArray: perfArray];
[perfArray removeAllObjects];
[subthreadsLock unlock];
c = [toDo count];
for (i = 0; i < c; i++)
{
GSPerformHolder *h = [toDo objectAtIndex: i];
[loop performSelector: @selector(fire)
target: h
argument: nil
order: 0
modes: h->modes];
}
RELEASE(toDo);
}
- (void) dealloc
{
DESTROY(receiver);
@ -1106,11 +1042,14 @@ static NSDate *theFuture;
- (void) fire
{
GSRunLoopThreadInfo *threadInfo;
if (receiver == nil)
{
return; // Already fired!
}
[GSRunLoopForThread(defaultThread) cancelPerformSelectorsWithTarget: self];
threadInfo = GSRunLoopInfoForThread(GSCurrentThread());
[threadInfo->loop cancelPerformSelectorsWithTarget: self];
[receiver performSelector: selector withObject: argument];
DESTROY(receiver);
DESTROY(argument);
@ -1128,52 +1067,45 @@ static NSDate *theFuture;
[l unlockWithCondition: 1];
}
}
- (NSArray*) modes
{
return modes;
}
@end
/**
* Extra methods to permit messages to be sent to an object such that they
* are executed in the <em>main</em> thread.<br />
* The main thread is the thread in which the GNUstep system is started,
* and where the GNUstep gui is used, it is the thread in which gui
* drawing operations <strong>must</strong> be performed.
*/
@implementation NSObject (NSMainThreadPerformAdditions)
@implementation NSObject (NSThreadPerformAdditions)
/**
* <p>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.
* </p>
* <p>The selector is performed when the runloop of the main thread next
* runs in one of the modes specified in anArray.<br />
* Where this method has been called more than once before the runloop
* of the main thread runs in the required mode, the order in which the
* operations in the main thread is done is the same as that in which
* they were added using this method.
* </p>
* <p>If there are no modes in anArray,
* the method has no effect and simply returns immediately.
* </p>
* <p>The argument aFlag specifies whether the method should wait until
* the selector has been performed before returning.<br />
* <strong>NB.</strong> This method does <em>not</em> cause the runloop of
* the main thread to be run ... so if the runloop is not executed by some
* code in the main thread, the thread waiting for the perform to complete
* will block forever.
* </p>
* <p>As a special case, if aFlag == YES and the current thread is the main
* thread, the modes array is ignored 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 runloop is
* not executing.
* </p>
*/
- (void) performSelectorOnMainThread: (SEL)aSelector
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
modes: (NSArray*)anArray
{
NSThread *t;
[self performSelector: aSelector
onThread: defaultThread
withObject: anObject
waitUntilDone: aFlag
modes: anArray];
}
- (void) performSelectorOnMainThread: (SEL)aSelector
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
{
[self performSelectorOnMainThread: aSelector
withObject: anObject
waitUntilDone: aFlag
modes: commonModes()];
}
- (void) performSelector: (SEL)aSelector
onThread: (NSThread*)aThread
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
modes: (NSArray*)anArray
{
GSRunLoopThreadInfo *info;
NSThread *t;
if ([anArray count] == 0)
{
@ -1181,24 +1113,29 @@ static NSDate *theFuture;
}
t = GSCurrentThread();
if (t == defaultThread)
if (aThread == nil)
{
if (aFlag == YES)
aThread = t;
}
info = GSRunLoopInfoForThread(aThread);
if (t == aThread)
{
if (aFlag == YES || info->loop == nil)
{
[self performSelector: aSelector withObject: anObject];
}
else
{
[GSRunLoopForThread(t) performSelector: aSelector
target: self
argument: anObject
order: 0
modes: anArray];
[info->loop performSelector: aSelector
target: self
argument: anObject
order: 0
modes: anArray];
}
}
else
{
GSPerformHolder *h;
GSPerformHolder *h;
NSConditionLock *l = nil;
if (aFlag == YES)
@ -1211,31 +1148,27 @@ static NSDate *theFuture;
selector: aSelector
modes: anArray
lock: l];
if (aFlag == YES)
[info addPerformer: h];
if (l != nil)
{
[l lockWhenCondition: 1];
RELEASE(h);
[l unlock];
RELEASE(l);
}
RELEASE(h);
}
}
/**
* Invokes -performSelectorOnMainThread:withObject:waitUntilDone:modes:
* using the supplied arguments and an array containing common modes.<br />
* These modes consist of NSRunLoopMode, NSConnectionreplyMode, and if
* in an application, the NSApplication modes.
*/
- (void) performSelectorOnMainThread: (SEL)aSelector
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
- (void) performSelector: (SEL)aSelector
onThread: (NSThread*)aThread
withObject: (id)anObject
waitUntilDone: (BOOL)aFlag
{
[self performSelectorOnMainThread: aSelector
withObject: anObject
waitUntilDone: aFlag
modes: commonModes()];
[self performSelector: aSelector
onThread: aThread
withObject: anObject
waitUntilDone: aFlag
modes: commonModes()];
}
@end

View file

@ -253,6 +253,7 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt)
*/
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts
{
GSRunLoopThreadInfo *threadInfo = GSRunLoopInfoForThread(nil);
int poll_return;
int fdEnd; /* Number of descriptors being monitored. */
int fdIndex;
@ -275,9 +276,9 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt)
/*
* Do the pre-listening set-up for the file descriptors of this mode.
*/
if (pollfds_capacity < i + 1)
if (pollfds_capacity < i + 2)
{
pollfds_capacity = i + 1;
pollfds_capacity = i + 2;
if (pollfds == 0)
{
pollfds = objc_malloc(pollfds_capacity * sizeof(*pollfds));
@ -290,6 +291,10 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt)
pollfds_count = 0;
((pollextra*)extra)->limit = 0;
/* Watch for signals from other threads.
*/
setPollfd(threadInfo->inputFd, POLLIN, self);
while (i-- > 0)
{
GSRunLoopWatcher *info;
@ -564,8 +569,16 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt)
}
if (pollfds[fdIndex].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL))
{
watcher
= (GSRunLoopWatcher*)NSMapGet(_rfdMap, (void*)(intptr_t)fd);
if (fd == threadInfo->inputFd)
{
[threadInfo fire];
watcher = nil;
}
else
{
watcher = (GSRunLoopWatcher*)
NSMapGet(_rfdMap, (void*)(intptr_t)fd);
}
if (watcher != nil && watcher->_invalidated == NO)
{
i = [contexts count];
@ -616,6 +629,7 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt)
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts
{
GSRunLoopThreadInfo *threadInfo = GSRunLoopInfoForThread(nil);
struct timeval timeout;
void *select_timeout;
int select_return;
@ -668,6 +682,13 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt)
NSResetMapTable(_wfdMap);
GSIArrayRemoveAllItems(_trigger);
/* Watch for signals from otyher threads.
*/
fd = threadInfo->inputFd;
if (fd > fdEnd)
fdEnd = fd;
FD_SET (fd, &read_fds);
while (i-- > 0)
{
GSRunLoopWatcher *info;
@ -926,8 +947,16 @@ static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt)
{
GSRunLoopWatcher *watcher;
watcher
= (GSRunLoopWatcher*)NSMapGet(_rfdMap, (void*)(intptr_t)fdIndex);
if (fdIndex == threadInfo->inputFd)
{
[threadInfo fire];
watcher = nil;
}
else
{
watcher = (GSRunLoopWatcher*)
NSMapGet(_rfdMap, (void*)(intptr_t)fdIndex);
}
if (watcher != nil && watcher->_invalidated == NO)
{
i = [contexts count];

View file

@ -275,6 +275,7 @@ static const NSMapTableValueCallBacks WatcherMapValueCallBacks =
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts
{
GSRunLoopThreadInfo *threadInfo = GSRunLoopForThread(nil, YES);
NSMapEnumerator hEnum;
GSRunLoopWatcher *watcher;
HANDLE handleArray[MAXIMUM_WAIT_OBJECTS-1];
@ -302,8 +303,9 @@ static const NSMapTableValueCallBacks WatcherMapValueCallBacks =
GSIArrayRemoveAllItems(_trigger);
i = GSIArrayCount(watchers);
num_handles = 0;
num_handles = 1; // One handle for signals from other threads
num_winMsgs = 0;
while (i-- > 0)
{
GSRunLoopWatcher *info;
@ -381,6 +383,7 @@ static const NSMapTableValueCallBacks WatcherMapValueCallBacks =
}
i = 0;
handleArray[i++] = threadInfo->event; // Signal from other thread
hEnum = NSEnumerateMapTable(handleMap);
while (NSNextMapEnumeratorPair(&hEnum, &handle, (void**)&watcher))
{
@ -521,7 +524,14 @@ static const NSMapTableValueCallBacks WatcherMapValueCallBacks =
handle = handleArray[i];
watcher = (GSRunLoopWatcher*)NSMapGet(handleMap, (void*)handle);
if (handle == threadInfo->event)
{
[threadInfo fire];
}
else
{
watcher = (GSRunLoopWatcher*)NSMapGet(handleMap, (void*)handle);
}
if (watcher != nil && watcher->_invalidated == NO)
{
i = [contexts count];