2002-01-30 15:28:50 +00:00
|
|
|
|
/** Implementation of object for waiting on several input sources
|
|
|
|
|
NSRunLoop.m
|
|
|
|
|
|
1999-04-20 16:28:04 +00:00
|
|
|
|
Copyright (C) 1996-1999 Free Software Foundation, Inc.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
Original by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
|
|
|
|
Created: March 1996
|
|
|
|
|
OPENSTEP version by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
|
Created: August 1997
|
|
|
|
|
|
1996-05-12 00:56:10 +00:00
|
|
|
|
This file is part of the GNUstep Base Library.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1996-04-10 18:20:36 +00:00
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Library General Public License for more details.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1996-04-10 18:20:36 +00:00
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
|
License along with this library; if not, write to the Free
|
1999-09-09 02:56:20 +00:00
|
|
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
|
|
|
|
|
<title>NSRunLoop class reference</title>
|
|
|
|
|
$Date$ $Revision$
|
1997-09-01 21:59:51 +00:00
|
|
|
|
*/
|
|
|
|
|
|
1997-11-06 00:51:23 +00:00
|
|
|
|
#include <config.h>
|
1998-12-20 21:27:47 +00:00
|
|
|
|
#include <base/preface.h>
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#include <Foundation/NSMapTable.h>
|
|
|
|
|
#include <Foundation/NSDate.h>
|
|
|
|
|
#include <Foundation/NSValue.h>
|
|
|
|
|
#include <Foundation/NSAutoreleasePool.h>
|
|
|
|
|
#include <Foundation/NSPort.h>
|
|
|
|
|
#include <Foundation/NSTimer.h>
|
|
|
|
|
#include <Foundation/NSNotificationQueue.h>
|
1996-04-10 18:20:36 +00:00
|
|
|
|
#include <Foundation/NSRunLoop.h>
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#include <Foundation/NSThread.h>
|
1998-11-06 19:46:23 +00:00
|
|
|
|
#include <Foundation/NSDebug.h>
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#include <sys/types.h>
|
2000-06-06 16:50:52 +00:00
|
|
|
|
#endif
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_SYS_TIME_H
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#include <sys/time.h>
|
2000-06-06 16:50:52 +00:00
|
|
|
|
#endif
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_POLL_H
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#include <poll.h>
|
|
|
|
|
#endif
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_UNISTD_H
|
2002-02-20 06:42:05 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#endif
|
2000-06-06 16:50:52 +00:00
|
|
|
|
#include <time.h>
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <string.h> /* for memset() */
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
2002-06-06 14:02:59 +00:00
|
|
|
|
NSString * const NSDefaultRunLoopMode = @"NSDefaultRunLoopMode";
|
|
|
|
|
|
1999-04-20 16:28:04 +00:00
|
|
|
|
static NSDate *theFuture = nil;
|
1999-04-19 14:29:52 +00:00
|
|
|
|
|
2001-06-21 04:49:20 +00:00
|
|
|
|
extern BOOL GSCheckTasks();
|
1999-04-19 14:29:52 +00:00
|
|
|
|
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_POLL
|
2002-01-30 15:28:50 +00:00
|
|
|
|
typedef struct {
|
|
|
|
|
int limit;
|
|
|
|
|
short *index;
|
|
|
|
|
} pollextra;
|
|
|
|
|
#endif
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
/*
|
1999-09-14 13:50:24 +00:00
|
|
|
|
* The 'GSRunLoopWatcher' class was written to permit the (relatively)
|
1997-09-01 21:59:51 +00:00
|
|
|
|
* easy addition of new events to be watched for in the runloop.
|
|
|
|
|
*
|
|
|
|
|
* To add a new type of event, the 'RunLoopEventType' enumeration must be
|
|
|
|
|
* extended, and the methods must be modified to handle the new type.
|
|
|
|
|
*
|
1999-09-14 13:50:24 +00:00
|
|
|
|
* The internal variables if the GSRunLoopWatcher are used as follows -
|
1999-04-20 16:28:04 +00:00
|
|
|
|
*
|
|
|
|
|
* The '_date' variable contains a date after which the event is useless
|
|
|
|
|
* and the watcher can be removed from the runloop.
|
|
|
|
|
*
|
|
|
|
|
* If '_invalidated' is set, the watcher should be disabled and should
|
1997-09-01 21:59:51 +00:00
|
|
|
|
* be removed from the runloop when next encountered.
|
|
|
|
|
*
|
|
|
|
|
* The 'data' variable is used to identify the resource/event that the
|
|
|
|
|
* watcher is interested in.
|
|
|
|
|
*
|
|
|
|
|
* The 'receiver' is the object which should be told when the event
|
|
|
|
|
* occurs. This object is retained so that we know it will continue
|
|
|
|
|
* to exist and can handle a callback.
|
|
|
|
|
*
|
|
|
|
|
* The 'type' variable indentifies the type of event watched for.
|
1999-04-19 14:29:52 +00:00
|
|
|
|
* NSRunLoops [-acceptInputForMode: beforeDate: ] method MUST contain
|
1997-09-01 21:59:51 +00:00
|
|
|
|
* code to watch for events of each type.
|
|
|
|
|
*
|
1999-09-14 13:50:24 +00:00
|
|
|
|
* To set this variable, the method adding the GSRunLoopWatcher to the
|
1997-09-01 21:59:51 +00:00
|
|
|
|
* runloop must ask the 'receiver' (or its delegate) to supply a date
|
1999-04-19 14:29:52 +00:00
|
|
|
|
* using the '[-limitDateForMode: ]' message.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
*
|
1998-11-12 08:41:44 +00:00
|
|
|
|
* NB. This class is private to NSRunLoop and must not be subclassed.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2000-10-30 18:00:27 +00:00
|
|
|
|
static SEL eventSel; /* Initialized in [NSRunLoop +initialize] */
|
1999-04-20 16:28:04 +00:00
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@interface GSRunLoopWatcher: NSObject
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-11-12 08:41:44 +00:00
|
|
|
|
@public
|
1999-04-22 11:24:57 +00:00
|
|
|
|
NSDate *_date; /* First to match layout of NSTimer */
|
1999-04-20 16:28:04 +00:00
|
|
|
|
BOOL _invalidated; /* 2nd to match layout of NSTimer */
|
|
|
|
|
IMP handleEvent; /* New-style event handling */
|
1998-10-29 08:46:30 +00:00
|
|
|
|
void *data;
|
|
|
|
|
id receiver;
|
|
|
|
|
RunLoopEventType type;
|
|
|
|
|
unsigned count;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1999-09-14 13:50:24 +00:00
|
|
|
|
- (id) initWithType: (RunLoopEventType)type
|
|
|
|
|
receiver: (id)anObj
|
|
|
|
|
data: (void*)data;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
@end
|
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@implementation GSRunLoopWatcher
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
1999-04-22 11:24:57 +00:00
|
|
|
|
RELEASE(_date);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
[super dealloc];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-03-02 08:58:30 +00:00
|
|
|
|
- (id) initWithType: (RunLoopEventType)aType
|
|
|
|
|
receiver: (id)anObj
|
|
|
|
|
data: (void*)item
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1999-04-20 16:28:04 +00:00
|
|
|
|
_invalidated = NO;
|
1999-03-02 08:58:30 +00:00
|
|
|
|
|
1998-11-12 08:41:44 +00:00
|
|
|
|
switch (aType)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2000-03-28 13:02:01 +00:00
|
|
|
|
case ET_EDESC: type = aType; break;
|
1999-04-19 14:29:52 +00:00
|
|
|
|
case ET_RDESC: type = aType; break;
|
|
|
|
|
case ET_WDESC: type = aType; break;
|
|
|
|
|
case ET_RPORT: type = aType; break;
|
|
|
|
|
default:
|
1998-11-12 08:41:44 +00:00
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"NSRunLoop - unknown event type"];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1999-05-20 09:29:02 +00:00
|
|
|
|
receiver = anObj;
|
1999-04-20 16:28:04 +00:00
|
|
|
|
if ([receiver respondsToSelector: eventSel] == YES)
|
|
|
|
|
handleEvent = [receiver methodForSelector: eventSel];
|
1999-03-02 08:58:30 +00:00
|
|
|
|
else
|
2000-03-28 13:02:01 +00:00
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"RunLoop listener has no event handling method"];
|
1998-11-12 08:41:44 +00:00
|
|
|
|
data = item;
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return self;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-04-20 16:28:04 +00:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Two optimisation functions that depend on a hack that the layout of
|
1999-09-14 13:50:24 +00:00
|
|
|
|
* the NSTimer class is known to be the same as GSRunLoopWatcher for the
|
1999-04-20 16:28:04 +00:00
|
|
|
|
* first two elements.
|
|
|
|
|
*/
|
1999-04-22 11:24:57 +00:00
|
|
|
|
static inline NSDate* timerDate(NSTimer* timer)
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
return ((GSRunLoopWatcher*)timer)->_date;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-04-20 16:28:04 +00:00
|
|
|
|
static inline BOOL timerInvalidated(NSTimer* timer)
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
return ((GSRunLoopWatcher*)timer)->_invalidated;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-04-20 16:28:04 +00:00
|
|
|
|
|
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
/*
|
|
|
|
|
* The GSRunLoopPerformer class is used to hold information about
|
|
|
|
|
* messages which are due to be sent to objects once each runloop
|
|
|
|
|
* iteration has passed.
|
|
|
|
|
*/
|
|
|
|
|
@interface GSRunLoopPerformer: NSObject
|
|
|
|
|
{
|
|
|
|
|
@public
|
|
|
|
|
SEL selector;
|
|
|
|
|
id target;
|
|
|
|
|
id argument;
|
|
|
|
|
unsigned order;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) fire;
|
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)target
|
|
|
|
|
argument: (id)argument
|
|
|
|
|
order: (unsigned int)order;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation GSRunLoopPerformer
|
|
|
|
|
|
|
|
|
|
- (void) fire
|
|
|
|
|
{
|
|
|
|
|
[target performSelector: selector withObject: argument];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)aTarget
|
|
|
|
|
argument: (id)anArgument
|
|
|
|
|
order: (unsigned int)theOrder
|
|
|
|
|
{
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self)
|
|
|
|
|
{
|
|
|
|
|
selector = aSelector;
|
|
|
|
|
target = aTarget;
|
|
|
|
|
argument = anArgument;
|
|
|
|
|
order = theOrder;
|
|
|
|
|
}
|
|
|
|
|
return self;
|
1999-04-28 23:02:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@end
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-04-02 14:27:40 +00:00
|
|
|
|
|
|
|
|
|
@interface NSRunLoop (TimedPerformers)
|
|
|
|
|
- (NSMutableArray*) _timedPerformers;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSRunLoop (TimedPerformers)
|
|
|
|
|
- (NSMutableArray*) _timedPerformers
|
|
|
|
|
{
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return _timedPerformers;
|
1998-04-02 14:27:40 +00:00
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
/*
|
2002-01-16 14:00:59 +00:00
|
|
|
|
* The GSTimedPerformer class is used to hold information about
|
|
|
|
|
* messages which are due to be sent to objects at a particular time.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
*/
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@interface GSTimedPerformer: NSObject <GCFinalization>
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1999-09-14 10:03:02 +00:00
|
|
|
|
@public
|
1998-10-29 08:46:30 +00:00
|
|
|
|
SEL selector;
|
|
|
|
|
id target;
|
|
|
|
|
id argument;
|
|
|
|
|
NSTimer *timer;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) fire;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)target
|
|
|
|
|
argument: (id)argument
|
|
|
|
|
delay: (NSTimeInterval)delay;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
@end
|
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@implementation GSTimedPerformer
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
1999-03-02 08:58:30 +00:00
|
|
|
|
[self gcFinalize];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
TEST_RELEASE(timer);
|
1999-03-02 08:58:30 +00:00
|
|
|
|
RELEASE(target);
|
|
|
|
|
RELEASE(argument);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
[super dealloc];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
- (void) fire
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
DESTROY(timer);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
[target performSelector: selector withObject: argument];
|
2001-06-21 13:36:13 +00:00
|
|
|
|
[[[NSRunLoop currentRunLoop] _timedPerformers]
|
2000-09-08 09:14:29 +00:00
|
|
|
|
removeObjectIdenticalTo: self];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-03-02 08:58:30 +00:00
|
|
|
|
- (void) gcFinalize
|
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
if (timer != nil)
|
2002-01-16 14:00:59 +00:00
|
|
|
|
{
|
|
|
|
|
[timer invalidate];
|
|
|
|
|
}
|
1999-03-02 08:58:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-09-14 10:03:02 +00:00
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)aTarget
|
|
|
|
|
argument: (id)anArgument
|
1999-09-14 13:50:24 +00:00
|
|
|
|
delay: (NSTimeInterval)delay
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-10-29 08:46:30 +00:00
|
|
|
|
self = [super init];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
if (self != nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
|
|
|
|
selector = aSelector;
|
1999-03-02 08:58:30 +00:00
|
|
|
|
target = RETAIN(aTarget);
|
|
|
|
|
argument = RETAIN(anArgument);
|
2002-01-16 14:00:59 +00:00
|
|
|
|
timer = [[NSTimer allocWithZone: NSDefaultMallocZone()]
|
2002-08-27 13:40:42 +00:00
|
|
|
|
initWithFireDate: nil
|
|
|
|
|
interval: delay
|
|
|
|
|
target: self
|
|
|
|
|
selector: @selector(fire)
|
|
|
|
|
userInfo: nil
|
|
|
|
|
repeats: NO];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return self;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1998-04-02 14:27:40 +00:00
|
|
|
|
@end
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
1998-04-02 14:27:40 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/*
|
|
|
|
|
* Setup for inline operation of arrays.
|
|
|
|
|
*/
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#define GSI_ARRAY_TYPES GSUNION_OBJ
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#if GS_WITH_GC == 0
|
2002-01-31 07:20:16 +00:00
|
|
|
|
#define GSI_ARRAY_RELEASE(A, X) [(X).obj release]
|
|
|
|
|
#define GSI_ARRAY_RETAIN(A, X) [(X).obj retain]
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#else
|
2002-01-31 07:20:16 +00:00
|
|
|
|
#define GSI_ARRAY_RELEASE(A, X)
|
|
|
|
|
#define GSI_ARRAY_RETAIN(A, X)
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#endif
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#include <base/GSIArray.h>
|
|
|
|
|
|
|
|
|
|
static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1)
|
|
|
|
|
{
|
|
|
|
|
return [((GSRunLoopWatcher *)(i0.obj))->_date
|
|
|
|
|
compare: ((GSRunLoopWatcher *)(i1.obj))->_date];
|
1998-04-02 14:27:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#if GS_WITH_GC == 0
|
|
|
|
|
static SEL wRelSel;
|
|
|
|
|
static SEL wRetSel;
|
|
|
|
|
static IMP wRelImp;
|
|
|
|
|
static IMP wRetImp;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wRelease(NSMapTable* t, const void* w)
|
1998-04-02 14:27:40 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
(*wRelImp)((id)w, wRelSel);
|
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
static void
|
|
|
|
|
wRetain(NSMapTable* t, const void* w)
|
|
|
|
|
{
|
|
|
|
|
(*wRetImp)((id)w, wRetSel);
|
1998-04-02 14:27:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-06-06 14:02:59 +00:00
|
|
|
|
static const NSMapTableValueCallBacks WatcherMapValueCallBacks =
|
1998-04-02 14:27:40 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
wRetain,
|
|
|
|
|
wRelease,
|
|
|
|
|
0
|
|
|
|
|
};
|
|
|
|
|
#else
|
|
|
|
|
#define WatcherMapValueCallBacks NSOwnedPointerMapValueCallBacks
|
|
|
|
|
#endif
|
1998-10-29 08:46:30 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
static void
|
|
|
|
|
aRetain(NSMapTable* t, const void* a)
|
|
|
|
|
{
|
|
|
|
|
}
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
static void
|
|
|
|
|
aRelease(NSMapTable* t, const void* a)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayEmpty((GSIArray)a);
|
|
|
|
|
NSZoneFree(((GSIArray)a)->zone, (void*)a);
|
1998-04-02 14:27:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-06-06 14:02:59 +00:00
|
|
|
|
static const NSMapTableValueCallBacks ArrayMapValueCallBacks =
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
|
|
|
|
aRetain,
|
|
|
|
|
aRelease,
|
|
|
|
|
0
|
|
|
|
|
};
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* The GSRunLoopCtxt stores context information to handle polling for
|
|
|
|
|
* events. This information is associated with a particular runloop
|
|
|
|
|
* mode, and persists throughout the life of the runloop instance.
|
|
|
|
|
*
|
|
|
|
|
* NB. This class is private to NSRunLoop and must not be subclassed.
|
|
|
|
|
*/
|
|
|
|
|
@interface GSRunLoopCtxt : NSObject
|
|
|
|
|
{
|
|
|
|
|
@public
|
|
|
|
|
void *extra; /** Copy of the RunLoop ivar. */
|
|
|
|
|
NSString *mode; /** The mode for this context. */
|
|
|
|
|
GSIArray performers; /** The actions to perform regularly. */
|
|
|
|
|
GSIArray timers; /** The timers set for the runloop mode */
|
|
|
|
|
GSIArray watchers; /** The inputs set for the runloop mode */
|
|
|
|
|
@private
|
|
|
|
|
NSMapTable *_efdMap;
|
|
|
|
|
NSMapTable *_rfdMap;
|
|
|
|
|
NSMapTable *_wfdMap;
|
|
|
|
|
int fairStart; // For trying to ensure fair handling.
|
|
|
|
|
BOOL completed; // To mark operation as completed.
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_POLL
|
2002-01-30 15:28:50 +00:00
|
|
|
|
int pollfds_capacity;
|
|
|
|
|
int pollfds_count;
|
|
|
|
|
struct pollfd *pollfds;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
- (void) endEvent: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type;
|
|
|
|
|
- (void) endPoll;
|
|
|
|
|
- (id) initWithMode: (NSString*)theMode extra: (void*)e;
|
|
|
|
|
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts;
|
|
|
|
|
@end
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
@implementation GSRunLoopCtxt
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
RELEASE(mode);
|
|
|
|
|
GSIArrayEmpty(performers);
|
|
|
|
|
NSZoneFree(performers->zone, (void*)performers);
|
|
|
|
|
GSIArrayEmpty(timers);
|
|
|
|
|
NSZoneFree(timers->zone, (void*)timers);
|
|
|
|
|
GSIArrayEmpty(watchers);
|
|
|
|
|
NSZoneFree(watchers->zone, (void*)watchers);
|
2002-03-20 12:13:03 +00:00
|
|
|
|
if (_efdMap != 0)
|
|
|
|
|
{
|
|
|
|
|
NSFreeMapTable(_efdMap);
|
|
|
|
|
}
|
|
|
|
|
if (_rfdMap != 0)
|
|
|
|
|
{
|
|
|
|
|
NSFreeMapTable(_rfdMap);
|
|
|
|
|
}
|
|
|
|
|
if (_wfdMap != 0)
|
|
|
|
|
{
|
|
|
|
|
NSFreeMapTable(_wfdMap);
|
|
|
|
|
}
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_POLL
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (pollfds != 0)
|
|
|
|
|
{
|
|
|
|
|
objc_free(pollfds);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* Remove any callback for the specified event which is set for an
|
|
|
|
|
* uncompleted poll operation.<br />
|
|
|
|
|
* This is called by nested event loops on contexts in outer loops
|
|
|
|
|
* when they handle an event ... removing the event from the outer
|
|
|
|
|
* loop ensures that it won't get handled twice, once by the inner
|
|
|
|
|
* loop and once by the outer one.
|
|
|
|
|
*/
|
|
|
|
|
- (void) endEvent: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
{
|
|
|
|
|
if (completed == NO)
|
|
|
|
|
{
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case ET_RDESC:
|
|
|
|
|
NSMapRemove(_rfdMap, data);
|
|
|
|
|
break;
|
|
|
|
|
case ET_WDESC:
|
|
|
|
|
NSMapRemove(_wfdMap, data);
|
|
|
|
|
break;
|
|
|
|
|
case ET_EDESC:
|
|
|
|
|
NSMapRemove(_efdMap, data);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
NSLog(@"Ending an event of unkown type (%d)", type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* Mark this poll conext as having completed, so that if we are
|
|
|
|
|
* executing a re-entrant poll, the enclosing poll operations
|
|
|
|
|
* know they can stop what they are doing because an inner
|
|
|
|
|
* operation has done the job.
|
|
|
|
|
*/
|
|
|
|
|
- (void) endPoll
|
|
|
|
|
{
|
|
|
|
|
completed = YES;
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
- (id) init
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"-init may not be called for GSRunLoopCtxt"];
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
- (id) initWithMode: (NSString*)theMode extra: (void*)e
|
|
|
|
|
{
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self != nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 08:57:00 +00:00
|
|
|
|
NSZone *z = [self zone];
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
mode = [theMode copy];
|
|
|
|
|
extra = e;
|
|
|
|
|
performers = NSZoneMalloc(z, sizeof(GSIArray_t));
|
|
|
|
|
GSIArrayInitWithZoneAndCapacity(performers, z, 8);
|
|
|
|
|
timers = NSZoneMalloc(z, sizeof(GSIArray_t));
|
|
|
|
|
GSIArrayInitWithZoneAndCapacity(timers, z, 8);
|
2002-01-30 08:57:00 +00:00
|
|
|
|
watchers = NSZoneMalloc(z, sizeof(GSIArray_t));
|
|
|
|
|
GSIArrayInitWithZoneAndCapacity(watchers, z, 8);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
|
|
|
|
_efdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
|
|
|
|
WatcherMapValueCallBacks, 0);
|
|
|
|
|
_rfdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
|
|
|
|
WatcherMapValueCallBacks, 0);
|
|
|
|
|
_wfdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
|
|
|
|
WatcherMapValueCallBacks, 0);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
return self;
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_POLL
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
|
|
|
|
static void setPollfd(int fd, int event, GSRunLoopCtxt *ctxt)
|
|
|
|
|
{
|
|
|
|
|
int index;
|
|
|
|
|
struct pollfd *pollfds = ctxt->pollfds;
|
|
|
|
|
pollextra *pe = (pollextra*)ctxt->extra;
|
|
|
|
|
|
|
|
|
|
if (fd >= pe->limit)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
int oldfd_limit = pe->limit;
|
1998-11-12 08:41:44 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
pe->limit = fd + 1;
|
|
|
|
|
if (pe->index == 0)
|
|
|
|
|
{
|
|
|
|
|
pe->index = objc_malloc(pe->limit * sizeof(*(pe->index)));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pe->index = objc_realloc(pe->index, pe->limit * sizeof(*(pe->index)));
|
|
|
|
|
}
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
pe->index[oldfd_limit++] = -1;
|
|
|
|
|
}
|
|
|
|
|
while (oldfd_limit < pe->limit);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
index = pe->index[fd];
|
|
|
|
|
if (index == -1)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (ctxt->pollfds_count >= ctxt->pollfds_capacity)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
ctxt->pollfds_capacity += 8;
|
|
|
|
|
pollfds =
|
|
|
|
|
objc_realloc(pollfds, ctxt->pollfds_capacity * sizeof (*pollfds));
|
|
|
|
|
ctxt->pollfds = pollfds;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
index = ctxt->pollfds_count++;
|
|
|
|
|
pe->index[fd] = index;
|
|
|
|
|
pollfds[index].fd = fd;
|
|
|
|
|
pollfds[index].events = 0;
|
|
|
|
|
pollfds[index].revents = 0;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
pollfds[index].events |= event;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* Perform a poll for the specified runloop context.
|
|
|
|
|
* If the method has been called re-entrantly, the contexts stack
|
|
|
|
|
* will list all the contexts with polls in progress
|
|
|
|
|
* and this method must tell those outer contexts not to handle events
|
|
|
|
|
* which are handled by this context.
|
|
|
|
|
*/
|
|
|
|
|
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
int poll_return;
|
|
|
|
|
int fdEnd = 0; /* Number of descriptors being monitored. */
|
|
|
|
|
int fdIndex;
|
|
|
|
|
int fdFinish;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
i = GSIArrayCount(watchers);
|
1999-09-14 10:03:02 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/*
|
|
|
|
|
* Get ready to listen to file descriptors.
|
|
|
|
|
* The maps will not have been emptied by any previous call.
|
|
|
|
|
*/
|
|
|
|
|
NSResetMapTable(_efdMap);
|
|
|
|
|
NSResetMapTable(_rfdMap);
|
|
|
|
|
NSResetMapTable(_wfdMap);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Do the pre-listening set-up for the file descriptors of this mode.
|
|
|
|
|
*/
|
|
|
|
|
if (pollfds_capacity < i + 1)
|
|
|
|
|
{
|
|
|
|
|
pollfds_capacity = i + 1;
|
|
|
|
|
if (pollfds == 0)
|
2000-09-08 09:14:29 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
pollfds = objc_malloc(pollfds_capacity * sizeof(*pollfds));
|
2002-01-30 08:57:00 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pollfds = objc_realloc(pollfds, pollfds_capacity * sizeof(*pollfds));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pollfds_count = 0;
|
|
|
|
|
((pollextra*)extra)->limit = 0;
|
|
|
|
|
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *info;
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
info = GSIArrayItemAtIndex(watchers, i).obj;
|
|
|
|
|
if (info->_invalidated == YES)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, i);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (info->type)
|
|
|
|
|
{
|
|
|
|
|
case ET_EDESC:
|
|
|
|
|
fd = (int)info->data;
|
|
|
|
|
setPollfd(fd, POLLPRI, self);
|
|
|
|
|
NSMapInsert(_efdMap, (void*)fd, info);
|
|
|
|
|
fdEnd++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ET_RDESC:
|
|
|
|
|
fd = (int)info->data;
|
|
|
|
|
setPollfd(fd, POLLIN, self);
|
|
|
|
|
NSMapInsert(_rfdMap, (void*)fd, info);
|
|
|
|
|
fdEnd++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ET_WDESC:
|
|
|
|
|
fd = (int)info->data;
|
|
|
|
|
setPollfd(fd, POLLOUT, self);
|
|
|
|
|
NSMapInsert(_wfdMap, (void*)fd, info);
|
|
|
|
|
fdEnd++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ET_RPORT:
|
|
|
|
|
if ([info->receiver isValid] == NO)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* We must remove an invalidated port.
|
|
|
|
|
*/
|
|
|
|
|
info->_invalidated = YES;
|
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, i);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
id port = info->receiver;
|
|
|
|
|
int port_fd_count = 128; // FIXME
|
|
|
|
|
int port_fd_array[port_fd_count];
|
|
|
|
|
|
|
|
|
|
if ([port respondsToSelector:
|
|
|
|
|
@selector(getFds:count:)])
|
|
|
|
|
{
|
|
|
|
|
[port getFds: port_fd_array
|
|
|
|
|
count: &port_fd_count];
|
|
|
|
|
}
|
|
|
|
|
NSDebugMLLog(@"NSRunLoop",
|
|
|
|
|
@"listening to %d port handles\n", port_fd_count);
|
|
|
|
|
while (port_fd_count--)
|
|
|
|
|
{
|
|
|
|
|
fd = port_fd_array[port_fd_count];
|
|
|
|
|
setPollfd(fd, POLLIN, self);
|
|
|
|
|
NSMapInsert(_rfdMap,
|
|
|
|
|
(void*)port_fd_array[port_fd_count], info);
|
|
|
|
|
fdEnd++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If there are notifications in the 'idle' queue, we try an
|
|
|
|
|
* instantaneous select so that, if there is no input pending,
|
|
|
|
|
* we can service the queue. Similarly, if a task has completed,
|
|
|
|
|
* we need to deliver its notifications.
|
|
|
|
|
*/
|
|
|
|
|
if (GSCheckTasks() || GSNotifyMore())
|
|
|
|
|
{
|
|
|
|
|
milliseconds = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (0) {
|
|
|
|
|
int i;
|
|
|
|
|
fprintf(stderr, "poll %d %d:", milliseconds, pollfds_count);
|
|
|
|
|
for (i = 0; i < pollfds_count; i++)
|
|
|
|
|
fprintf(stderr, " %d,%x", pollfds[i].fd, pollfds[i].events);
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
}
|
|
|
|
|
poll_return = poll (pollfds, pollfds_count, milliseconds);
|
|
|
|
|
if (0) {
|
|
|
|
|
int i;
|
|
|
|
|
fprintf(stderr, "ret %d %d:", poll_return, pollfds_count);
|
|
|
|
|
for (i = 0; i < pollfds_count; i++)
|
|
|
|
|
fprintf(stderr, " %d,%x", pollfds[i].fd, pollfds[i].revents);
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"poll returned %d\n", poll_return);
|
|
|
|
|
|
|
|
|
|
if (poll_return < 0)
|
|
|
|
|
{
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
{
|
|
|
|
|
GSCheckTasks();
|
|
|
|
|
poll_return = 0;
|
|
|
|
|
}
|
|
|
|
|
#ifdef __MINGW__
|
|
|
|
|
else if (errno == 0)
|
2002-01-30 08:57:00 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/* MinGW often returns an errno == 0. Not sure why */
|
|
|
|
|
poll_return = 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Some exceptional condition happened. */
|
|
|
|
|
/* xxx We can do something with exception_fds, instead of
|
|
|
|
|
aborting here. */
|
|
|
|
|
NSLog (@"poll() error in -acceptInputForMode:beforeDate: '%s'",
|
|
|
|
|
GSLastErrorStr(errno));
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (poll_return == 0)
|
|
|
|
|
{
|
|
|
|
|
completed = YES;
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Look at all the file descriptors select() says are ready for action;
|
|
|
|
|
* notify the corresponding object for each of the ready fd's.
|
|
|
|
|
* NB. It is possible for a watcher to be missing from the map - if
|
|
|
|
|
* the event handler of a previous watcher has 'run' the loop again
|
|
|
|
|
* before returning.
|
|
|
|
|
* NB. Each time this loop is entered, the starting position (fairStart)
|
|
|
|
|
* is incremented - this is to ensure a fair distribion over all
|
|
|
|
|
* inputs where multiple inputs are in use. Note - fairStart can be
|
|
|
|
|
* modified while we are in the loop (by recursive calls).
|
|
|
|
|
*/
|
|
|
|
|
if (++fairStart >= fdEnd)
|
|
|
|
|
{
|
|
|
|
|
fairStart = 0;
|
|
|
|
|
fdIndex = 0;
|
|
|
|
|
fdFinish = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fdIndex = fairStart;
|
|
|
|
|
fdFinish = fairStart;
|
|
|
|
|
}
|
|
|
|
|
completed = NO;
|
|
|
|
|
while (completed == NO)
|
|
|
|
|
{
|
|
|
|
|
if (pollfds[fdIndex].revents != 0)
|
|
|
|
|
{
|
|
|
|
|
int fd = pollfds[fdIndex].fd;
|
|
|
|
|
GSRunLoopWatcher *watcher;
|
|
|
|
|
BOOL found = NO;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The poll() call supports various error conditions - all
|
|
|
|
|
* errors should be handled by any available handler.
|
|
|
|
|
* The ET_EDSEC handler is the primary handler for exceptions
|
|
|
|
|
* though it is more generally used to deal with out-of-band data.
|
|
|
|
|
*/
|
|
|
|
|
if (pollfds[fdIndex].revents & (POLLPRI|POLLERR|POLLHUP|POLLNVAL))
|
2002-01-28 15:16:33 +00:00
|
|
|
|
{
|
2002-06-14 08:02:53 +00:00
|
|
|
|
watcher = (GSRunLoopWatcher*)NSMapGet(_efdMap, (void*)fd);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (watcher != nil && watcher->_invalidated == NO)
|
|
|
|
|
{
|
|
|
|
|
i = [contexts count];
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
2002-01-28 15:16:33 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (c != self) [c endEvent: (void*)fd type: ET_EDESC];
|
|
|
|
|
}
|
2002-06-14 08:02:53 +00:00
|
|
|
|
/*
|
|
|
|
|
* The watcher is still valid - so call its
|
|
|
|
|
* receivers event handling method.
|
|
|
|
|
*/
|
|
|
|
|
(*watcher->handleEvent)(watcher->receiver,
|
|
|
|
|
eventSel, watcher->data, watcher->type,
|
|
|
|
|
(void*)(gsaddr)fd, mode);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
if (completed == YES)
|
|
|
|
|
{
|
|
|
|
|
break; // A nested poll has done the job.
|
|
|
|
|
}
|
|
|
|
|
found = YES;
|
|
|
|
|
}
|
|
|
|
|
if (pollfds[fdIndex].revents & (POLLOUT|POLLERR|POLLHUP|POLLNVAL))
|
|
|
|
|
{
|
2002-06-14 08:02:53 +00:00
|
|
|
|
watcher = (GSRunLoopWatcher*)NSMapGet(_wfdMap, (void*)fd);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (watcher != nil && watcher->_invalidated == NO)
|
2002-01-30 08:57:00 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
i = [contexts count];
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
|
|
|
|
|
|
|
|
|
if (c != self) [c endEvent: (void*)fd type: ET_WDESC];
|
|
|
|
|
}
|
2002-06-14 08:02:53 +00:00
|
|
|
|
/*
|
|
|
|
|
* The watcher is still valid - so call its
|
|
|
|
|
* receivers event handling method.
|
|
|
|
|
*/
|
|
|
|
|
(*watcher->handleEvent)(watcher->receiver,
|
|
|
|
|
eventSel, watcher->data, watcher->type,
|
|
|
|
|
(void*)(gsaddr)fd, mode);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
if (completed == YES)
|
|
|
|
|
{
|
|
|
|
|
break; // A nested poll has done the job.
|
|
|
|
|
}
|
|
|
|
|
found = YES;
|
|
|
|
|
}
|
|
|
|
|
if (pollfds[fdIndex].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL))
|
|
|
|
|
{
|
2002-06-14 08:02:53 +00:00
|
|
|
|
watcher = (GSRunLoopWatcher*)NSMapGet(_rfdMap, (void*)fd);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (watcher != nil && watcher->_invalidated == NO)
|
|
|
|
|
{
|
|
|
|
|
i = [contexts count];
|
|
|
|
|
while (i-- > 0)
|
2000-09-08 09:14:29 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
|
|
|
|
|
|
|
|
|
if (c != self) [c endEvent: (void*)fd type: ET_RDESC];
|
2000-09-08 09:14:29 +00:00
|
|
|
|
}
|
2002-06-14 08:02:53 +00:00
|
|
|
|
/*
|
|
|
|
|
* The watcher is still valid - so call its
|
|
|
|
|
* receivers event handling method.
|
|
|
|
|
*/
|
|
|
|
|
(*watcher->handleEvent)(watcher->receiver,
|
|
|
|
|
eventSel, watcher->data, watcher->type,
|
|
|
|
|
(void*)(gsaddr)fd, mode);
|
2000-09-08 09:14:29 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
if (completed == YES)
|
|
|
|
|
{
|
|
|
|
|
break; // A nested poll has done the job.
|
|
|
|
|
}
|
|
|
|
|
found = YES;
|
2002-01-28 15:16:33 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (found == YES && --poll_return == 0)
|
|
|
|
|
{
|
|
|
|
|
completed = YES;
|
|
|
|
|
}
|
2002-01-30 08:57:00 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (++fdIndex >= fdEnd)
|
|
|
|
|
{
|
|
|
|
|
fdIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
if (fdIndex == fdFinish)
|
|
|
|
|
{
|
|
|
|
|
completed = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
completed = YES;
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
2002-01-28 15:16:33 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
- (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts
|
|
|
|
|
{
|
|
|
|
|
struct timeval timeout;
|
|
|
|
|
void *select_timeout;
|
|
|
|
|
int select_return;
|
|
|
|
|
int fdIndex;
|
|
|
|
|
int fdFinish;
|
|
|
|
|
fd_set read_fds; // Mask for read-ready fds.
|
|
|
|
|
fd_set exception_fds; // Mask for exception fds.
|
|
|
|
|
fd_set write_fds; // Mask for write-ready fds.
|
|
|
|
|
int num_inputs = 0;
|
2002-02-20 06:42:05 +00:00
|
|
|
|
int fdEnd = -1;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
i = GSIArrayCount(watchers);
|
|
|
|
|
|
|
|
|
|
/* Find out how much time we should wait, and set SELECT_TIMEOUT. */
|
|
|
|
|
if (milliseconds == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Don't wait at all. */
|
|
|
|
|
timeout.tv_sec = 0;
|
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
select_timeout = &timeout;
|
|
|
|
|
}
|
|
|
|
|
else if (milliseconds > 0)
|
|
|
|
|
{
|
|
|
|
|
timeout.tv_sec = milliseconds/1000;
|
|
|
|
|
timeout.tv_usec = (milliseconds - 1000 * timeout.tv_sec) * 1000;
|
|
|
|
|
select_timeout = &timeout;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-03-25 08:13:46 +00:00
|
|
|
|
timeout.tv_sec = -1;
|
|
|
|
|
timeout.tv_usec = -1;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
select_timeout = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Get ready to listen to file descriptors.
|
|
|
|
|
* Initialize the set of FDS we'll pass to select(), and make sure we
|
|
|
|
|
* have empty maps for keeping track of which watcher is associated
|
|
|
|
|
* with which file descriptor.
|
|
|
|
|
* The maps may not have been emptied if a previous call to this
|
|
|
|
|
* method was terminated by an exception.
|
|
|
|
|
*/
|
|
|
|
|
memset(&exception_fds, '\0', sizeof(exception_fds));
|
|
|
|
|
memset(&read_fds, '\0', sizeof(read_fds));
|
|
|
|
|
memset(&write_fds, '\0', sizeof(write_fds));
|
|
|
|
|
NSResetMapTable(_efdMap);
|
|
|
|
|
NSResetMapTable(_rfdMap);
|
|
|
|
|
NSResetMapTable(_wfdMap);
|
|
|
|
|
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *info;
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
info = GSIArrayItemAtIndex(watchers, i).obj;
|
|
|
|
|
if (info->_invalidated == YES)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, i);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (info->type)
|
|
|
|
|
{
|
|
|
|
|
case ET_EDESC:
|
|
|
|
|
fd = (int)info->data;
|
|
|
|
|
if (fd > fdEnd)
|
|
|
|
|
fdEnd = fd;
|
|
|
|
|
FD_SET (fd, &exception_fds);
|
|
|
|
|
NSMapInsert(_efdMap, (void*)fd, info);
|
|
|
|
|
num_inputs++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ET_RDESC:
|
|
|
|
|
fd = (int)info->data;
|
|
|
|
|
if (fd > fdEnd)
|
|
|
|
|
fdEnd = fd;
|
|
|
|
|
FD_SET (fd, &read_fds);
|
|
|
|
|
NSMapInsert(_rfdMap, (void*)fd, info);
|
|
|
|
|
num_inputs++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ET_WDESC:
|
|
|
|
|
fd = (int)info->data;
|
|
|
|
|
if (fd > fdEnd)
|
|
|
|
|
fdEnd = fd;
|
|
|
|
|
FD_SET (fd, &write_fds);
|
|
|
|
|
NSMapInsert(_wfdMap, (void*)fd, info);
|
|
|
|
|
num_inputs++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ET_RPORT:
|
|
|
|
|
if ([info->receiver isValid] == NO)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* We must remove an invalidated port.
|
|
|
|
|
*/
|
|
|
|
|
info->_invalidated = YES;
|
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, i);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
id port = info->receiver;
|
|
|
|
|
int port_fd_count = 128; // xxx #define this constant
|
|
|
|
|
int port_fd_array[port_fd_count];
|
|
|
|
|
|
|
|
|
|
if ([port respondsToSelector:
|
|
|
|
|
@selector(getFds:count:)])
|
|
|
|
|
{
|
|
|
|
|
[port getFds: port_fd_array
|
|
|
|
|
count: &port_fd_count];
|
|
|
|
|
}
|
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"listening to %d port sockets",
|
|
|
|
|
port_fd_count);
|
|
|
|
|
while (port_fd_count--)
|
|
|
|
|
{
|
|
|
|
|
fd = port_fd_array[port_fd_count];
|
|
|
|
|
FD_SET (port_fd_array[port_fd_count], &read_fds);
|
|
|
|
|
if (fd > fdEnd)
|
|
|
|
|
fdEnd = fd;
|
|
|
|
|
NSMapInsert(_rfdMap,
|
|
|
|
|
(void*)port_fd_array[port_fd_count], info);
|
|
|
|
|
num_inputs++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fdEnd++;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If there are notifications in the 'idle' queue, we try an
|
|
|
|
|
* instantaneous select so that, if there is no input pending,
|
|
|
|
|
* we can service the queue. Similarly, if a task has completed,
|
|
|
|
|
* we need to deliver its notifications.
|
|
|
|
|
*/
|
|
|
|
|
if (GSCheckTasks() || GSNotifyMore())
|
|
|
|
|
{
|
|
|
|
|
timeout.tv_sec = 0;
|
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
select_timeout = &timeout;
|
|
|
|
|
}
|
2002-02-20 06:42:05 +00:00
|
|
|
|
|
2002-03-25 08:13:46 +00:00
|
|
|
|
// NSDebugMLLog(@"NSRunLoop", @"select timeout %d,%d", timeout.tv_sec, timeout.tv_usec);
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
select_return = select (fdEnd, &read_fds, &write_fds,
|
|
|
|
|
&exception_fds, select_timeout);
|
|
|
|
|
|
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"select returned %d", select_return);
|
|
|
|
|
|
|
|
|
|
if (select_return < 0)
|
|
|
|
|
{
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
{
|
|
|
|
|
GSCheckTasks();
|
|
|
|
|
select_return = 0;
|
|
|
|
|
}
|
|
|
|
|
#ifdef __MINGW__
|
|
|
|
|
else if (errno == 0)
|
|
|
|
|
{
|
|
|
|
|
/* MinGW often returns an errno == 0. Not sure why */
|
|
|
|
|
select_return = 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Some exceptional condition happened. */
|
|
|
|
|
/* xxx We can do something with exception_fds, instead of
|
|
|
|
|
aborting here. */
|
|
|
|
|
NSLog (@"select() error in -acceptInputForMode:beforeDate: '%s'",
|
|
|
|
|
GSLastErrorStr(errno));
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (select_return == 0)
|
|
|
|
|
{
|
|
|
|
|
completed = YES;
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Look at all the file descriptors select() says are ready for action;
|
|
|
|
|
* notify the corresponding object for each of the ready fd's.
|
|
|
|
|
* NB. Each time this roop is entered, the starting position (fairStart)
|
|
|
|
|
* is incremented - this is to ensure a fair distribtion over all
|
|
|
|
|
* inputs where multiple inputs are in use. Note - fairStart can be
|
|
|
|
|
* modified while we are in the loop (by recursive calls).
|
|
|
|
|
*/
|
|
|
|
|
if (++fairStart >= fdEnd)
|
|
|
|
|
{
|
|
|
|
|
fairStart = 0;
|
|
|
|
|
fdIndex = 0;
|
|
|
|
|
fdFinish = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fdIndex = fairStart;
|
|
|
|
|
fdFinish = fairStart;
|
|
|
|
|
}
|
|
|
|
|
completed = NO;
|
|
|
|
|
while (completed == NO)
|
|
|
|
|
{
|
|
|
|
|
BOOL found = NO;
|
|
|
|
|
|
|
|
|
|
if (FD_ISSET (fdIndex, &exception_fds))
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *watcher;
|
|
|
|
|
|
2002-06-14 08:02:53 +00:00
|
|
|
|
watcher = (GSRunLoopWatcher*)NSMapGet(_efdMap, (void*)fdIndex);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (watcher != nil && watcher->_invalidated == NO)
|
|
|
|
|
{
|
|
|
|
|
i = [contexts count];
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
|
|
|
|
|
|
|
|
|
if (c != self) [c endEvent: (void*)fdIndex type: ET_EDESC];
|
|
|
|
|
}
|
2002-06-14 08:02:53 +00:00
|
|
|
|
/*
|
|
|
|
|
* The watcher is still valid - so call its receivers
|
|
|
|
|
* event handling method.
|
|
|
|
|
*/
|
|
|
|
|
(*watcher->handleEvent)(watcher->receiver,
|
|
|
|
|
eventSel, watcher->data, watcher->type,
|
|
|
|
|
(void*)(gsaddr)fdIndex, mode);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
if (completed == YES)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
found = YES;
|
|
|
|
|
}
|
|
|
|
|
if (FD_ISSET (fdIndex, &write_fds))
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *watcher;
|
|
|
|
|
|
2002-06-14 08:02:53 +00:00
|
|
|
|
watcher = (GSRunLoopWatcher*)NSMapGet(_wfdMap, (void*)fdIndex);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (watcher != nil && watcher->_invalidated == NO)
|
|
|
|
|
{
|
|
|
|
|
i = [contexts count];
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
|
|
|
|
|
|
|
|
|
if (c != self) [c endEvent: (void*)fdIndex type: ET_WDESC];
|
|
|
|
|
}
|
2002-06-14 08:02:53 +00:00
|
|
|
|
/*
|
|
|
|
|
* The watcher is still valid - so call its receivers
|
|
|
|
|
* event handling method.
|
|
|
|
|
*/
|
|
|
|
|
(*watcher->handleEvent)(watcher->receiver,
|
|
|
|
|
eventSel, watcher->data, watcher->type,
|
|
|
|
|
(void*)(gsaddr)fdIndex, mode);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
if (completed == YES)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
found = YES;
|
|
|
|
|
}
|
|
|
|
|
if (FD_ISSET (fdIndex, &read_fds))
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *watcher;
|
|
|
|
|
|
|
|
|
|
watcher = (GSRunLoopWatcher*)NSMapGet(_rfdMap, (void*)fdIndex);
|
|
|
|
|
if (watcher != nil && watcher->_invalidated == NO)
|
|
|
|
|
{
|
|
|
|
|
i = [contexts count];
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopCtxt *c = [contexts objectAtIndex: i];
|
|
|
|
|
|
|
|
|
|
if (c != self) [c endEvent: (void*)fdIndex type: ET_RDESC];
|
|
|
|
|
}
|
2002-06-14 08:02:53 +00:00
|
|
|
|
/*
|
|
|
|
|
* The watcher is still valid - so call its receivers
|
|
|
|
|
* event handling method.
|
|
|
|
|
*/
|
|
|
|
|
(*watcher->handleEvent)(watcher->receiver,
|
|
|
|
|
eventSel, watcher->data, watcher->type,
|
|
|
|
|
(void*)(gsaddr)fdIndex, mode);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
if (completed == YES)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
found = YES;
|
|
|
|
|
}
|
|
|
|
|
if (found == YES && --select_return == 0)
|
|
|
|
|
{
|
|
|
|
|
completed = YES;
|
|
|
|
|
}
|
|
|
|
|
if (++fdIndex >= fdEnd)
|
|
|
|
|
{
|
|
|
|
|
fdIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
if (fdIndex == fdFinish)
|
|
|
|
|
{
|
|
|
|
|
completed = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
completed = YES;
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSObject (TimedPerformers)
|
|
|
|
|
|
2002-08-27 14:24:54 +00:00
|
|
|
|
/**
|
|
|
|
|
* Cancels any perform operations set up for the specified target
|
|
|
|
|
* in the current run loop.
|
|
|
|
|
*/
|
|
|
|
|
+ (void) cancelPreviousPerformRequestsWithTarget: (id)target
|
|
|
|
|
{
|
|
|
|
|
NSMutableArray *perf = [[NSRunLoop currentRunLoop] _timedPerformers];
|
|
|
|
|
unsigned count = [perf count];
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
GSTimedPerformer *array[count];
|
|
|
|
|
|
|
|
|
|
IF_NO_GC(RETAIN(target));
|
|
|
|
|
[perf getObjects: array];
|
|
|
|
|
while (count-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSTimedPerformer *p = array[count];
|
|
|
|
|
|
|
|
|
|
if (p->target == target)
|
|
|
|
|
{
|
|
|
|
|
[perf removeObjectAtIndex: count];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RELEASE(target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cancels any perform operations set up for the specified target
|
2002-08-27 15:04:44 +00:00
|
|
|
|
* in the current loop, but only if the value of aSelector and argument
|
2002-08-27 14:24:54 +00:00
|
|
|
|
* with which the performs were set up exactly match those supplied.
|
|
|
|
|
*/
|
2002-01-30 15:28:50 +00:00
|
|
|
|
+ (void) cancelPreviousPerformRequestsWithTarget: (id)target
|
|
|
|
|
selector: (SEL)aSelector
|
|
|
|
|
object: (id)arg
|
|
|
|
|
{
|
|
|
|
|
NSMutableArray *perf = [[NSRunLoop currentRunLoop] _timedPerformers];
|
|
|
|
|
unsigned count = [perf count];
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
GSTimedPerformer *array[count];
|
|
|
|
|
|
|
|
|
|
IF_NO_GC(RETAIN(target));
|
|
|
|
|
IF_NO_GC(RETAIN(arg));
|
|
|
|
|
[perf getObjects: array];
|
|
|
|
|
while (count-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSTimedPerformer *p = array[count];
|
|
|
|
|
|
|
|
|
|
if (p->target == target && sel_eq(p->selector, aSelector)
|
|
|
|
|
&& [p->argument isEqual: arg])
|
|
|
|
|
{
|
|
|
|
|
[perf removeObjectAtIndex: count];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RELEASE(arg);
|
|
|
|
|
RELEASE(target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) performSelector: (SEL)aSelector
|
|
|
|
|
withObject: (id)argument
|
|
|
|
|
afterDelay: (NSTimeInterval)seconds
|
|
|
|
|
{
|
|
|
|
|
NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
|
|
|
|
GSTimedPerformer *item;
|
|
|
|
|
|
|
|
|
|
item = [[GSTimedPerformer alloc] initWithSelector: aSelector
|
|
|
|
|
target: self
|
|
|
|
|
argument: argument
|
|
|
|
|
delay: seconds];
|
|
|
|
|
[[loop _timedPerformers] addObject: item];
|
|
|
|
|
RELEASE(item);
|
|
|
|
|
[loop addTimer: item->timer forMode: NSDefaultRunLoopMode];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) performSelector: (SEL)aSelector
|
|
|
|
|
withObject: (id)argument
|
|
|
|
|
afterDelay: (NSTimeInterval)seconds
|
|
|
|
|
inModes: (NSArray*)modes
|
|
|
|
|
{
|
|
|
|
|
unsigned count = [modes count];
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
|
|
|
|
NSString *marray[count];
|
|
|
|
|
GSTimedPerformer *item;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
item = [[GSTimedPerformer alloc] initWithSelector: aSelector
|
|
|
|
|
target: self
|
|
|
|
|
argument: argument
|
|
|
|
|
delay: seconds];
|
|
|
|
|
[[loop _timedPerformers] addObject: item];
|
|
|
|
|
RELEASE(item);
|
|
|
|
|
[modes getObjects: marray];
|
2002-01-30 08:57:00 +00:00
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[loop addTimer: item->timer forMode: marray[i]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@interface NSRunLoop (Private)
|
|
|
|
|
|
|
|
|
|
- (void) _addWatcher: (GSRunLoopWatcher*)item
|
|
|
|
|
forMode: (NSString*)mode;
|
|
|
|
|
- (void) _checkPerformers: (GSRunLoopCtxt*)context;
|
|
|
|
|
- (GSRunLoopWatcher*) _getWatcher: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode;
|
|
|
|
|
- (void) _removeWatcher: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode;
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSRunLoop (Private)
|
|
|
|
|
|
|
|
|
|
/* Add a watcher to the list for the specified mode. Keep the list in
|
|
|
|
|
limit-date order. */
|
|
|
|
|
- (void) _addWatcher: (GSRunLoopWatcher*) item forMode: (NSString*)mode
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
GSIArray watchers;
|
|
|
|
|
id obj;
|
|
|
|
|
|
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context == nil)
|
|
|
|
|
{
|
|
|
|
|
context = [[GSRunLoopCtxt alloc] initWithMode: mode extra: _extra];
|
|
|
|
|
NSMapInsert(_contextMap, context->mode, context);
|
|
|
|
|
RELEASE(context);
|
|
|
|
|
}
|
|
|
|
|
watchers = context->watchers;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the receiver or its delegate (if any) respond to
|
|
|
|
|
* 'limitDateForMode: ' then we ask them for the limit date for
|
|
|
|
|
* this watcher.
|
|
|
|
|
*/
|
|
|
|
|
obj = item->receiver;
|
|
|
|
|
if ([obj respondsToSelector: @selector(limitDateForMode:)])
|
|
|
|
|
{
|
|
|
|
|
NSDate *d = [obj limitDateForMode: mode];
|
|
|
|
|
|
|
|
|
|
item->_date = RETAIN(d);
|
|
|
|
|
}
|
|
|
|
|
else if ([obj respondsToSelector: @selector(delegate)])
|
|
|
|
|
{
|
|
|
|
|
obj = [obj delegate];
|
|
|
|
|
if (obj != nil && [obj respondsToSelector: @selector(limitDateForMode:)])
|
|
|
|
|
{
|
|
|
|
|
NSDate *d = [obj limitDateForMode: mode];
|
|
|
|
|
|
|
|
|
|
item->_date = RETAIN(d);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
item->_date = RETAIN(theFuture);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
item->_date = RETAIN(theFuture);
|
|
|
|
|
GSIArrayInsertSorted(watchers, (GSIArrayItem)item, aSort);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) _checkPerformers: (GSRunLoopCtxt*)context
|
|
|
|
|
{
|
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
GSIArray performers = context->performers;
|
|
|
|
|
unsigned count = GSIArrayCount(performers);
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopPerformer *array[count];
|
|
|
|
|
NSMapEnumerator enumerator;
|
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
void *mode;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copy the array - because we have to cancel the requests
|
|
|
|
|
* before firing.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
array[i] = RETAIN(GSIArrayItemAtIndex(performers, i).obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remove the requests that we are about to fire from all modes.
|
|
|
|
|
*/
|
|
|
|
|
enumerator = NSEnumerateMapTable(_contextMap);
|
|
|
|
|
while (NSNextMapEnumeratorPair(&enumerator, &mode, (void**)&context))
|
|
|
|
|
{
|
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
GSIArray performers = context->performers;
|
|
|
|
|
unsigned tmpCount = GSIArrayCount(performers);
|
|
|
|
|
|
|
|
|
|
while (tmpCount--)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopPerformer *p;
|
|
|
|
|
|
|
|
|
|
p = GSIArrayItemAtIndex(performers, tmpCount).obj;
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (p == array[i])
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(performers, tmpCount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-05-28 05:23:36 +00:00
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Finally, fire the requests.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
[array[i] fire];
|
|
|
|
|
RELEASE(array[i]);
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Locates a runloop watcher matching the specified data and type in this
|
|
|
|
|
* runloop. If the mode is nil, either the currentMode is used (if the
|
|
|
|
|
* loop is running) or NSDefaultRunLoopMode is used.
|
|
|
|
|
*/
|
2001-05-10 10:15:38 +00:00
|
|
|
|
- (GSRunLoopWatcher*) _getWatcher: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
2001-05-10 10:15:38 +00:00
|
|
|
|
|
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
mode = [self currentMode];
|
2002-01-16 11:38:46 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
2001-05-10 10:15:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context != nil)
|
2001-05-10 10:15:38 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray watchers = context->watchers;
|
|
|
|
|
unsigned i = GSIArrayCount(watchers);
|
2001-05-10 10:15:38 +00:00
|
|
|
|
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *info;
|
|
|
|
|
|
|
|
|
|
info = GSIArrayItemAtIndex(watchers, i).obj;
|
|
|
|
|
if (info->type == type && info->data == data)
|
|
|
|
|
{
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Removes a runloop watcher matching the specified data and type in this
|
|
|
|
|
* runloop. If the mode is nil, either the currentMode is used (if the
|
|
|
|
|
* loop is running) or NSDefaultRunLoopMode is used.
|
|
|
|
|
*/
|
2001-05-10 10:15:38 +00:00
|
|
|
|
- (void) _removeWatcher: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
2001-05-10 10:15:38 +00:00
|
|
|
|
|
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
mode = [self currentMode];
|
2002-01-16 11:38:46 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
2001-05-10 10:15:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context != nil)
|
2001-05-10 10:15:38 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray watchers = context->watchers;
|
2001-05-10 10:15:38 +00:00
|
|
|
|
unsigned i = GSIArrayCount(watchers);
|
|
|
|
|
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *info;
|
|
|
|
|
|
|
|
|
|
info = GSIArrayItemAtIndex(watchers, i).obj;
|
|
|
|
|
if (info->type == type && info->data == data)
|
|
|
|
|
{
|
|
|
|
|
info->_invalidated = YES;
|
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
@implementation NSRunLoop(GNUstepExtensions)
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Adds a runloop watcher matching the specified data and type in this
|
|
|
|
|
* runloop. If the mode is nil, either the currentMode is used (if the
|
|
|
|
|
* loop is running) or NSDefaultRunLoopMode is used.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) addEvent: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
watcher: (id<RunLoopEvents>)watcher
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
GSRunLoopWatcher *info;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
mode = [self currentMode];
|
2002-01-16 11:38:46 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
info = [self _getWatcher: data type: type forMode: mode];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-08-15 09:12:01 +00:00
|
|
|
|
if (info != nil && info->receiver == (id)watcher)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
|
|
|
|
/* Increment usage count for this watcher. */
|
1998-11-12 08:41:44 +00:00
|
|
|
|
info->count++;
|
1997-09-09 15:30:24 +00:00
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Remove any existing handler for another watcher. */
|
|
|
|
|
[self _removeWatcher: data type: type forMode: mode];
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/* Create new object to hold information. */
|
|
|
|
|
info = [[GSRunLoopWatcher alloc] initWithType: type
|
|
|
|
|
receiver: watcher
|
|
|
|
|
data: data];
|
|
|
|
|
/* Add the object to the array for the mode. */
|
|
|
|
|
[self _addWatcher: info forMode: mode];
|
|
|
|
|
RELEASE(info); /* Now held in array. */
|
|
|
|
|
}
|
2002-01-30 08:57:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/**
|
|
|
|
|
* Removes a runloop watcher matching the specified data and type in this
|
|
|
|
|
* runloop. If the mode is nil, either the currentMode is used (if the
|
|
|
|
|
* loop is running) or NSDefaultRunLoopMode is used.
|
|
|
|
|
* The additional removeAll flag may be used to remove all instances of
|
|
|
|
|
* the watcher rather than just a single one.
|
|
|
|
|
*/
|
|
|
|
|
- (void) removeEvent: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
all: (BOOL)removeAll
|
2002-01-30 08:57:00 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = [self currentMode];
|
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (removeAll)
|
|
|
|
|
{
|
|
|
|
|
[self _removeWatcher: data type: type forMode: mode];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *info;
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
info = [self _getWatcher: data type: type forMode: mode];
|
|
|
|
|
|
|
|
|
|
if (info)
|
|
|
|
|
{
|
|
|
|
|
if (info->count == 0)
|
|
|
|
|
{
|
|
|
|
|
[self _removeWatcher: data type: type forMode: mode];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info->count--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-01-30 08:57:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
@end
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
@implementation NSRunLoop
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
1999-04-19 14:29:52 +00:00
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if (self == [NSRunLoop class])
|
|
|
|
|
{
|
|
|
|
|
[self currentRunLoop];
|
|
|
|
|
theFuture = RETAIN([NSDate distantFuture]);
|
2000-10-30 18:00:27 +00:00
|
|
|
|
eventSel = @selector(receivedEvent:type:extra:forMode:);
|
1999-04-19 14:29:52 +00:00
|
|
|
|
#if GS_WITH_GC == 0
|
2000-10-30 18:00:27 +00:00
|
|
|
|
wRelSel = @selector(release);
|
|
|
|
|
wRetSel = @selector(retain);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
wRelImp = [[GSRunLoopWatcher class] instanceMethodForSelector: wRelSel];
|
|
|
|
|
wRetImp = [[GSRunLoopWatcher class] instanceMethodForSelector: wRetSel];
|
1999-04-19 14:29:52 +00:00
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-03-02 08:58:30 +00:00
|
|
|
|
+ (NSRunLoop*) currentRunLoop
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-10-29 08:46:30 +00:00
|
|
|
|
static NSString *key = @"NSRunLoopThreadKey";
|
1999-04-19 14:29:52 +00:00
|
|
|
|
NSMutableDictionary *d;
|
1999-09-14 10:03:02 +00:00
|
|
|
|
NSRunLoop *r;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1999-04-19 14:29:52 +00:00
|
|
|
|
d = GSCurrentThreadDictionary();
|
|
|
|
|
r = [d objectForKey: key];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
if (r == nil)
|
|
|
|
|
{
|
2001-03-17 11:55:05 +00:00
|
|
|
|
if (d != nil)
|
|
|
|
|
{
|
|
|
|
|
r = [self new];
|
|
|
|
|
[d setObject: r forKey: key];
|
|
|
|
|
RELEASE(r);
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
1996-04-10 18:20:36 +00:00
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
/* This is the designated initializer. */
|
1999-03-02 08:58:30 +00:00
|
|
|
|
- (id) init
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
self = [super init];
|
|
|
|
|
if (self != nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_contextStack = [NSMutableArray new];
|
|
|
|
|
_contextMap = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
|
|
|
|
NSObjectMapValueCallBacks, 0);
|
2002-01-16 14:00:59 +00:00
|
|
|
|
_timedPerformers = [[NSMutableArray alloc] initWithCapacity: 8];
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_POLL
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_extra = objc_malloc(sizeof(pollextra));
|
|
|
|
|
memset(_extra, '\0', sizeof(pollextra));
|
|
|
|
|
#endif
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
return self;
|
1996-04-10 18:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) dealloc
|
1999-03-02 08:58:30 +00:00
|
|
|
|
{
|
|
|
|
|
[self gcFinalize];
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) gcFinalize
|
1996-04-10 18:20:36 +00:00
|
|
|
|
{
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_POLL
|
2002-01-31 22:28:27 +00:00
|
|
|
|
if (_extra != 0)
|
|
|
|
|
{
|
|
|
|
|
pollextra *e = (pollextra*)_extra;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
2002-01-31 22:28:27 +00:00
|
|
|
|
if (e->index != 0)
|
|
|
|
|
objc_free(e->index);
|
|
|
|
|
objc_free(e);
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#endif
|
|
|
|
|
RELEASE(_contextStack);
|
2002-01-31 22:28:27 +00:00
|
|
|
|
if (_contextMap != 0)
|
|
|
|
|
{
|
|
|
|
|
NSFreeMapTable(_contextMap);
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
RELEASE(_timedPerformers);
|
1996-04-10 18:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns the current mode of this runloop. If the runloop is not running
|
|
|
|
|
* then this method returns nil.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (NSString*) currentMode
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
return _currentMode;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-08-27 13:40:42 +00:00
|
|
|
|
/**
|
|
|
|
|
* Adds a timer to the loop in the specified mode.<br />
|
|
|
|
|
* Timers are removed automatically when they are invalid.<br />
|
|
|
|
|
*/
|
2001-12-17 14:31:42 +00:00
|
|
|
|
- (void) addTimer: (NSTimer*)timer
|
1997-09-01 21:59:51 +00:00
|
|
|
|
forMode: (NSString*)mode
|
1996-04-10 18:20:36 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
GSIArray timers;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context == nil)
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = [[GSRunLoopCtxt alloc] initWithMode: mode extra: _extra];
|
|
|
|
|
NSMapInsert(_contextMap, context->mode, context);
|
|
|
|
|
RELEASE(context);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
timers = context->timers;
|
1999-06-21 08:30:26 +00:00
|
|
|
|
GSIArrayInsertSorted(timers, (GSIArrayItem)timer, aSort);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
/**
|
|
|
|
|
* Fire appropriate timers and determine the earliest time that anything
|
|
|
|
|
* watched for becomes useless.
|
|
|
|
|
*/
|
1999-04-21 20:16:25 +00:00
|
|
|
|
- (NSDate*) limitDateForMode: (NSString*)mode
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context = NSMapGet(_contextMap, mode);
|
|
|
|
|
NSDate *when = nil;
|
2002-01-28 15:16:33 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (context != nil)
|
2002-01-30 08:57:00 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSTimer *min_timer = nil;
|
|
|
|
|
GSRunLoopWatcher *min_watcher = nil;
|
|
|
|
|
NSString *savedMode = _currentMode;
|
|
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
|
|
|
|
|
|
|
|
|
_currentMode = mode;
|
|
|
|
|
NS_DURING
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray timers = context->timers;
|
|
|
|
|
GSIArray watchers = context->watchers;
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
while (GSIArrayCount(timers) != 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
min_timer = GSIArrayItemAtIndex(timers, 0).obj;
|
|
|
|
|
if (timerInvalidated(min_timer) == YES)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(timers, 0);
|
|
|
|
|
min_timer = nil;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
if ([timerDate(min_timer) timeIntervalSinceNow] > 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSIArrayRemoveItemAtIndexNoRelease(timers, 0);
|
|
|
|
|
/* Firing will also increment its fireDate, if it is repeating. */
|
|
|
|
|
[min_timer fire];
|
|
|
|
|
if (timerInvalidated(min_timer) == NO)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayInsertSortedNoRetain(timers,
|
|
|
|
|
(GSIArrayItem)min_timer, aSort);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RELEASE(min_timer);
|
|
|
|
|
}
|
|
|
|
|
min_timer = nil;
|
|
|
|
|
GSNotifyASAP(); /* Post notifications. */
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/* Is this right? At the moment we invalidate and discard watchers
|
|
|
|
|
whose limit-dates have passed. */
|
2002-01-16 14:00:59 +00:00
|
|
|
|
while (GSIArrayCount(watchers) != 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
min_watcher = GSIArrayItemAtIndex(watchers, 0).obj;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
if (min_watcher->_invalidated == YES)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, 0);
|
|
|
|
|
min_watcher = nil;
|
|
|
|
|
continue;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-16 14:00:59 +00:00
|
|
|
|
|
|
|
|
|
if ([min_watcher->_date timeIntervalSinceNow] > 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
break;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
id obj;
|
|
|
|
|
NSDate *nxt = nil;
|
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
/*
|
2002-01-16 14:00:59 +00:00
|
|
|
|
* If the receiver or its delegate wants to know about
|
|
|
|
|
* timeouts - inform it and give it a chance to set a
|
|
|
|
|
* revised limit date.
|
1998-10-29 08:46:30 +00:00
|
|
|
|
*/
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSIArrayRemoveItemAtIndexNoRelease(watchers, 0);
|
|
|
|
|
obj = min_watcher->receiver;
|
|
|
|
|
if ([obj respondsToSelector:
|
|
|
|
|
@selector(timedOutEvent:type:forMode:)])
|
|
|
|
|
{
|
|
|
|
|
nxt = [obj timedOutEvent: min_watcher->data
|
|
|
|
|
type: min_watcher->type
|
2002-01-30 15:28:50 +00:00
|
|
|
|
forMode: mode];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
else if ([obj respondsToSelector: @selector(delegate)])
|
|
|
|
|
{
|
|
|
|
|
obj = [obj delegate];
|
|
|
|
|
if (obj != nil && [obj respondsToSelector:
|
|
|
|
|
@selector(timedOutEvent:type:forMode:)])
|
|
|
|
|
{
|
|
|
|
|
nxt = [obj timedOutEvent: min_watcher->data
|
|
|
|
|
type: min_watcher->type
|
2002-01-30 15:28:50 +00:00
|
|
|
|
forMode: mode];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nxt && [nxt timeIntervalSinceNow] > 0.0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If the watcher has been given a revised limit date -
|
|
|
|
|
* re-insert it into the queue in the correct place.
|
|
|
|
|
*/
|
|
|
|
|
ASSIGN(min_watcher->_date, nxt);
|
|
|
|
|
GSIArrayInsertSortedNoRetain(watchers,
|
|
|
|
|
(GSIArrayItem)min_watcher, aSort);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If the watcher is now useless - invalidate and
|
|
|
|
|
* release it.
|
|
|
|
|
*/
|
|
|
|
|
min_watcher->_invalidated = YES;
|
|
|
|
|
RELEASE(min_watcher);
|
|
|
|
|
}
|
|
|
|
|
min_watcher = nil;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_currentMode = savedMode;
|
2002-01-29 10:27:41 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
|
|
|
|
_currentMode = savedMode;
|
|
|
|
|
[localException raise];
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
RELEASE(arp);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/*
|
|
|
|
|
* If there are timers - set limit date to the earliest of them.
|
|
|
|
|
* If there are watchers, set the limit date to that of the earliest
|
|
|
|
|
* watcher (or leave it as the date of the earliest timer if that is
|
|
|
|
|
* before the watchers limit).
|
|
|
|
|
*/
|
|
|
|
|
if (min_timer != nil)
|
|
|
|
|
{
|
|
|
|
|
when = timerDate(min_timer);
|
|
|
|
|
if (min_watcher != nil
|
|
|
|
|
&& [min_watcher->_date compare: when] == NSOrderedAscending)
|
|
|
|
|
{
|
|
|
|
|
when = min_watcher->_date;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (min_watcher != nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
1999-04-20 16:28:04 +00:00
|
|
|
|
when = min_watcher->_date;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return nil; /* Nothing waiting to be done. */
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"limit date %f",
|
2002-01-30 08:57:00 +00:00
|
|
|
|
[when timeIntervalSinceReferenceDate]);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1999-04-22 11:24:57 +00:00
|
|
|
|
return when;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Listen to input sources.<br />
|
2002-02-20 06:42:05 +00:00
|
|
|
|
* If limit_date is nil or in the past, then don't wait;
|
|
|
|
|
* just poll inputs and return,
|
|
|
|
|
* otherwise block until input is available or until the
|
|
|
|
|
* earliest limit date has passed (whichever comes first).<br />
|
2002-01-16 11:38:46 +00:00
|
|
|
|
* If the supplied mode is nil, uses NSDefaultRunLoopMode.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) acceptInputForMode: (NSString*)mode
|
2001-12-17 14:31:42 +00:00
|
|
|
|
beforeDate: (NSDate*)limit_date
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
NSTimeInterval ti;
|
|
|
|
|
int timeout_ms;
|
|
|
|
|
NSString *savedMode = _currentMode;
|
1999-09-28 19:35:09 +00:00
|
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 21:26:27 +00:00
|
|
|
|
NSAssert(mode, NSInvalidArgumentException);
|
2002-01-16 11:38:46 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_currentMode = mode;
|
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-03-07 16:49:32 +00:00
|
|
|
|
[self _checkPerformers: context];
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
NS_DURING
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray watchers;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
if (context == nil || (watchers = context->watchers) == 0
|
|
|
|
|
|| (i = GSIArrayCount(watchers)) == 0)
|
|
|
|
|
{
|
2002-02-20 06:42:05 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"no inputs in mode %@", mode);
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
GSNotifyIdle();
|
|
|
|
|
ti = [limit_date timeIntervalSinceNow];
|
|
|
|
|
/*
|
|
|
|
|
* Pause for as long as possible (up to the limit date)
|
|
|
|
|
*/
|
|
|
|
|
if (ti > 0.0)
|
|
|
|
|
{
|
|
|
|
|
#if defined(HAVE_USLEEP)
|
|
|
|
|
if (ti >= INT_MAX / 1000000)
|
|
|
|
|
{
|
|
|
|
|
ti = INT_MAX;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ti *= 1000000;
|
|
|
|
|
}
|
|
|
|
|
usleep (ti);
|
|
|
|
|
#elif defined(__MINGW__)
|
|
|
|
|
if (ti >= INT_MAX / 1000)
|
|
|
|
|
{
|
|
|
|
|
ti = INT_MAX;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ti *= 1000;
|
|
|
|
|
}
|
|
|
|
|
Sleep (ti);
|
|
|
|
|
#else
|
|
|
|
|
sleep (ti);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSCheckTasks();
|
2002-02-20 06:42:05 +00:00
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
[self _checkPerformers: context];
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
_currentMode = savedMode;
|
|
|
|
|
RELEASE(arp);
|
|
|
|
|
NS_VOIDRETURN;
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
/* Find out how much time we should wait, and set SELECT_TIMEOUT. */
|
|
|
|
|
if (!limit_date)
|
|
|
|
|
{
|
|
|
|
|
/* Don't wait at all. */
|
2002-01-30 15:28:50 +00:00
|
|
|
|
timeout_ms = 0;
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
else if ((ti = [limit_date timeIntervalSinceNow]) > 0.0)
|
2002-01-16 14:00:59 +00:00
|
|
|
|
{
|
|
|
|
|
/* Wait until the LIMIT_DATE. */
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"accept I/P before %f (sec from now %f)",
|
|
|
|
|
[limit_date timeIntervalSinceReferenceDate], ti);
|
|
|
|
|
if (ti >= INT_MAX / 1000)
|
|
|
|
|
{
|
|
|
|
|
timeout_ms = INT_MAX; // Far future.
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
timeout_ms = ti * 1000;
|
|
|
|
|
}
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
else if (ti <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
/* The LIMIT_DATE has already past; return immediately without
|
|
|
|
|
polling any inputs. */
|
|
|
|
|
GSCheckTasks();
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[self _checkPerformers: context];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSNotifyASAP();
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"limit date past, returning");
|
|
|
|
|
_currentMode = savedMode;
|
2002-01-16 14:00:59 +00:00
|
|
|
|
RELEASE(arp);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NS_VOIDRETURN;
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Wait forever. */
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"accept input waiting forever");
|
|
|
|
|
timeout_ms = -1;
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
2000-03-28 13:02:01 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if ([_contextStack indexOfObjectIdenticalTo: context] == NSNotFound)
|
2002-01-29 10:27:41 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[_contextStack addObject: context];
|
2002-01-29 10:27:41 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if ([context pollUntil: timeout_ms within: _contextStack] == NO)
|
2001-01-18 11:58:19 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSNotifyIdle();
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[self _checkPerformers: context];
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
_currentMode = savedMode;
|
2002-01-29 10:27:41 +00:00
|
|
|
|
/*
|
2002-01-30 15:28:50 +00:00
|
|
|
|
* Once a poll has been completed on a context, we can remove that
|
|
|
|
|
* context from the stack even if it actually polling at an outer
|
|
|
|
|
* level of re-entrancy ... since the poll we have just done will
|
|
|
|
|
* have handled any events that the outer levels would have wanted
|
|
|
|
|
* to handle, and the polling for this context will be marked as ended.
|
2002-01-29 10:27:41 +00:00
|
|
|
|
*/
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[context endPoll];
|
|
|
|
|
[_contextStack removeObjectIdenticalTo: context];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_currentMode = savedMode;
|
|
|
|
|
[context endPoll];
|
|
|
|
|
[_contextStack removeObjectIdenticalTo: context];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
[localException raise];
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
1999-06-25 10:22:55 +00:00
|
|
|
|
RELEASE(arp);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Calls -acceptInputForMode:beforeDate: to run the loop once.<br />
|
|
|
|
|
* If the limit dates for all of mode's input sources have passed,
|
|
|
|
|
* returns NO without running the loop, otherwise returns YES.
|
|
|
|
|
*/
|
2001-06-21 13:36:13 +00:00
|
|
|
|
- (BOOL) runMode: (NSString*)mode beforeDate: (NSDate*)date
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-10-29 08:46:30 +00:00
|
|
|
|
id d;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1999-04-22 11:24:57 +00:00
|
|
|
|
NSAssert(mode && date, NSInvalidArgumentException);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
/* If date has already passed, simply return. */
|
|
|
|
|
if ([date timeIntervalSinceNow] < 0)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"run mode with date already past");
|
2001-06-21 04:49:20 +00:00
|
|
|
|
/*
|
|
|
|
|
* Notify if any tasks have completed.
|
|
|
|
|
*/
|
|
|
|
|
if (GSCheckTasks() == YES)
|
|
|
|
|
{
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return NO;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
/* Find out how long we can wait before first limit date. */
|
|
|
|
|
d = [self limitDateForMode: mode];
|
|
|
|
|
if (d == nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"run mode with nothing to do");
|
2001-06-21 04:49:20 +00:00
|
|
|
|
/*
|
|
|
|
|
* Notify if any tasks have completed.
|
|
|
|
|
*/
|
|
|
|
|
if (GSCheckTasks() == YES)
|
|
|
|
|
{
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return NO;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-25 10:22:55 +00:00
|
|
|
|
/*
|
|
|
|
|
* Use the earlier of the two dates we have.
|
|
|
|
|
* Retain the date in case the firing of a timer (or some other event)
|
|
|
|
|
* releases it.
|
|
|
|
|
*/
|
1999-04-21 20:16:25 +00:00
|
|
|
|
d = [d earlierDate: date];
|
1999-09-28 19:35:09 +00:00
|
|
|
|
IF_NO_GC(RETAIN(d));
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
/* Wait, listening to our input sources. */
|
|
|
|
|
[self acceptInputForMode: mode beforeDate: d];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1999-04-22 11:24:57 +00:00
|
|
|
|
RELEASE(d);
|
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return YES;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) run
|
|
|
|
|
{
|
1999-04-19 14:29:52 +00:00
|
|
|
|
[self runUntilDate: theFuture];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-06-21 13:36:13 +00:00
|
|
|
|
- (void) runUntilDate: (NSDate*)date
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2001-12-17 14:31:42 +00:00
|
|
|
|
double ti = [date timeIntervalSinceNow];
|
|
|
|
|
BOOL mayDoMore = YES;
|
|
|
|
|
|
|
|
|
|
/* Positive values are in the future. */
|
|
|
|
|
while (ti > 0 && mayDoMore == YES)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"run until date %f seconds from now", ti);
|
2001-12-17 14:31:42 +00:00
|
|
|
|
mayDoMore = [self runMode: NSDefaultRunLoopMode beforeDate: date];
|
|
|
|
|
ti = [date timeIntervalSinceNow];
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSRunLoop (OPENSTEP)
|
|
|
|
|
|
|
|
|
|
- (void) addPort: (NSPort*)port
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return [self addEvent: (void*)port
|
|
|
|
|
type: ET_RPORT
|
|
|
|
|
watcher: (id<RunLoopEvents>)port
|
|
|
|
|
forMode: (NSString*)mode];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-08-27 14:24:54 +00:00
|
|
|
|
/**
|
|
|
|
|
* Cancels any perform operations set up for the specified target
|
|
|
|
|
* in the receiver.
|
|
|
|
|
*/
|
|
|
|
|
- (void) cancelPerformSelectorsWithTarget: (id) target
|
|
|
|
|
{
|
|
|
|
|
NSMapEnumerator enumerator;
|
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
void *mode;
|
|
|
|
|
|
|
|
|
|
enumerator = NSEnumerateMapTable(_contextMap);
|
|
|
|
|
|
|
|
|
|
while (NSNextMapEnumeratorPair(&enumerator, &mode, (void**)&context))
|
|
|
|
|
{
|
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
GSIArray performers = context->performers;
|
|
|
|
|
unsigned count = GSIArrayCount(performers);
|
|
|
|
|
|
|
|
|
|
while (count--)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopPerformer *p;
|
|
|
|
|
|
|
|
|
|
p = GSIArrayItemAtIndex(performers, count).obj;
|
|
|
|
|
if (p->target == target)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(performers, count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cancels any perform operations set up for the specified target
|
2002-08-27 15:04:44 +00:00
|
|
|
|
* in the receiver, but only if the value of aSelector and argument
|
2002-08-27 14:24:54 +00:00
|
|
|
|
* with which the performs were set up exactly match those supplied.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) cancelPerformSelector: (SEL)aSelector
|
1999-09-14 10:03:02 +00:00
|
|
|
|
target: (id) target
|
|
|
|
|
argument: (id) argument
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
NSMapEnumerator enumerator;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
void *mode;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
enumerator = NSEnumerateMapTable(_contextMap);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
while (NSNextMapEnumeratorPair(&enumerator, &mode, (void**)&context))
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (context != nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray performers = context->performers;
|
|
|
|
|
unsigned count = GSIArrayCount(performers);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
while (count--)
|
1999-09-14 10:03:02 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopPerformer *p;
|
|
|
|
|
|
|
|
|
|
p = GSIArrayItemAtIndex(performers, count).obj;
|
|
|
|
|
if (p->target == target && sel_eq(p->selector, aSelector)
|
|
|
|
|
&& p->argument == argument)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(performers, count);
|
|
|
|
|
}
|
1999-09-14 10:03:02 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-05-28 05:23:36 +00:00
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) configureAsServer
|
|
|
|
|
{
|
|
|
|
|
/* Nothing to do here */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) performSelector: (SEL)aSelector
|
1999-09-14 10:03:02 +00:00
|
|
|
|
target: (id)target
|
|
|
|
|
argument: (id)argument
|
1997-09-01 21:59:51 +00:00
|
|
|
|
order: (unsigned int)order
|
|
|
|
|
modes: (NSArray*)modes
|
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
unsigned count = [modes count];
|
1998-10-29 08:46:30 +00:00
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
if (count > 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
NSString *array[count];
|
|
|
|
|
GSRunLoopPerformer *item;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
item = [[GSRunLoopPerformer alloc] initWithSelector: aSelector
|
|
|
|
|
target: target
|
|
|
|
|
argument: argument
|
|
|
|
|
order: order];
|
|
|
|
|
|
|
|
|
|
[modes getObjects: array];
|
|
|
|
|
while (count-- > 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
NSString *mode = array[count];
|
|
|
|
|
unsigned end;
|
|
|
|
|
unsigned i;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
GSIArray performers;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context == nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = [[GSRunLoopCtxt alloc] initWithMode: mode
|
|
|
|
|
extra: _extra];
|
|
|
|
|
NSMapInsert(_contextMap, context->mode, context);
|
|
|
|
|
RELEASE(context);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
performers = context->performers;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
|
|
|
|
end = GSIArrayCount(performers);
|
|
|
|
|
for (i = 0; i < end; i++)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopPerformer *p;
|
|
|
|
|
|
|
|
|
|
p = GSIArrayItemAtIndex(performers, i).obj;
|
|
|
|
|
if (p->order <= order)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayInsertItem(performers, (GSIArrayItem)item, i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i == end)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayInsertItem(performers, (GSIArrayItem)item, i);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-09-14 13:50:24 +00:00
|
|
|
|
RELEASE(item);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) removePort: (NSPort*)port
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
1999-04-19 14:29:52 +00:00
|
|
|
|
return [self removeEvent: (void*)port type: ET_RPORT forMode: mode all: NO];
|
1996-04-10 18:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|