mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
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:
parent
27a50601c5
commit
5d8174ac84
9 changed files with 454 additions and 300 deletions
15
ChangeLog
15
ChangeLog
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
/*
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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];}
|
||||
|
|
|
@ -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: ¬ 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
|
||||
|
||||
|
|
|
@ -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: ¬ 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
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in a new issue