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
|
2007-09-14 11:36:11 +00:00
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
1996-04-10 18:20:36 +00:00
|
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1996-04-10 18:20:36 +00:00
|
|
|
|
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
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
1996-04-10 18:20:36 +00:00
|
|
|
|
License along with this library; if not, write to the Free
|
2006-03-05 07:06:58 +00:00
|
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
|
#import "common.h"
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#define EXPOSE_NSRunLoop_IVARS 1
|
|
|
|
|
#define EXPOSE_NSTimer_IVARS 1
|
|
|
|
|
#import "Foundation/NSMapTable.h"
|
|
|
|
|
#import "Foundation/NSDate.h"
|
|
|
|
|
#import "Foundation/NSValue.h"
|
|
|
|
|
#import "Foundation/NSAutoreleasePool.h"
|
|
|
|
|
#import "Foundation/NSPort.h"
|
|
|
|
|
#import "Foundation/NSTimer.h"
|
|
|
|
|
#import "Foundation/NSNotification.h"
|
|
|
|
|
#import "Foundation/NSNotificationQueue.h"
|
|
|
|
|
#import "Foundation/NSRunLoop.h"
|
|
|
|
|
#import "Foundation/NSStream.h"
|
|
|
|
|
#import "Foundation/NSThread.h"
|
|
|
|
|
#import "Foundation/NSInvocation.h"
|
|
|
|
|
#import "GSRunLoopCtxt.h"
|
|
|
|
|
#import "GSRunLoopWatcher.h"
|
|
|
|
|
#import "GSStream.h"
|
|
|
|
|
|
|
|
|
|
#import "GSPrivate.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
|
2005-02-23 22:17:54 +00:00
|
|
|
|
#ifdef HAVE_POLL_F
|
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
|
2009-09-10 16:41:06 +00:00
|
|
|
|
#include <math.h>
|
2000-06-06 16:50:52 +00:00
|
|
|
|
#include <time.h>
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#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
|
|
|
|
|
2006-03-07 09:14:37 +00:00
|
|
|
|
@interface NSObject (OptionalPortRunLoop)
|
2009-02-23 20:42:32 +00:00
|
|
|
|
- (void) getFds: (NSInteger*)fds count: (NSInteger*)count;
|
2006-03-07 09:14:37 +00:00
|
|
|
|
@end
|
|
|
|
|
|
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
|
2009-02-23 20:42:32 +00:00
|
|
|
|
order: (NSUInteger)order;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation GSRunLoopPerformer
|
|
|
|
|
|
2007-02-19 19:11:50 +00:00
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
RELEASE(target);
|
|
|
|
|
RELEASE(argument);
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
- (void) fire
|
|
|
|
|
{
|
|
|
|
|
[target performSelector: selector withObject: argument];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)aTarget
|
|
|
|
|
argument: (id)anArgument
|
2009-02-23 20:42:32 +00:00
|
|
|
|
order: (NSUInteger)theOrder
|
1999-09-14 13:50:24 +00:00
|
|
|
|
{
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self)
|
|
|
|
|
{
|
|
|
|
|
selector = aSelector;
|
2007-02-19 19:11:50 +00:00
|
|
|
|
target = RETAIN(aTarget);
|
|
|
|
|
argument = RETAIN(anArgument);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
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
|
|
|
|
*/
|
2009-01-12 18:36:37 +00:00
|
|
|
|
@interface GSTimedPerformer: NSObject
|
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;
|
2003-01-31 19:06:51 +00:00
|
|
|
|
- (void) invalidate;
|
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
|
|
|
|
|
{
|
2009-01-12 18:36:37 +00:00
|
|
|
|
[self finalize];
|
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
|
|
|
|
}
|
|
|
|
|
|
2009-01-12 18:36:37 +00:00
|
|
|
|
- (void) finalize
|
1999-03-02 08:58:30 +00:00
|
|
|
|
{
|
2003-01-31 19:06:51 +00:00
|
|
|
|
[self 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
|
|
|
|
}
|
2003-01-31 19:06:51 +00:00
|
|
|
|
|
|
|
|
|
- (void) invalidate
|
|
|
|
|
{
|
|
|
|
|
if (timer != nil)
|
|
|
|
|
{
|
|
|
|
|
[timer invalidate];
|
|
|
|
|
DESTROY(timer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
|
#ifndef GSI_ARRAY_TYPES
|
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
|
|
|
|
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/GSIArray.h"
|
2005-02-23 16:05:09 +00:00
|
|
|
|
#endif
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
|
static inline NSDate *timerDate(NSTimer *t)
|
|
|
|
|
{
|
2010-02-14 10:48:10 +00:00
|
|
|
|
return t->_date;
|
2006-03-21 15:33:05 +00:00
|
|
|
|
}
|
|
|
|
|
static inline BOOL timerInvalidated(NSTimer *t)
|
|
|
|
|
{
|
2010-02-14 10:48:10 +00:00
|
|
|
|
return t->_invalidated;
|
2006-03-21 15:33:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSObject (TimedPerformers)
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/*
|
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)
|
|
|
|
|
{
|
2003-01-31 19:06:51 +00:00
|
|
|
|
[p invalidate];
|
2002-08-27 14:24:54 +00:00
|
|
|
|
[perf removeObjectAtIndex: count];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RELEASE(target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/*
|
2002-08-27 14:24:54 +00:00
|
|
|
|
* 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
|
2003-01-31 19:06:51 +00:00
|
|
|
|
* with which the performs were set up match those supplied.<br />
|
|
|
|
|
* Matching of the argument may be either by pointer equality or by
|
|
|
|
|
* use of the [NSObject-isEqual:] method.
|
2002-08-27 14:24:54 +00:00
|
|
|
|
*/
|
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];
|
|
|
|
|
|
2010-02-22 10:13:20 +00:00
|
|
|
|
if (p->target == target && sel_isEqual(p->selector, aSelector)
|
2003-01-31 19:06:51 +00:00
|
|
|
|
&& (p->argument == arg || [p->argument isEqual: arg]))
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
2003-01-31 19:06:51 +00:00
|
|
|
|
[p invalidate];
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[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);
|
2008-07-06 09:18:30 +00:00
|
|
|
|
if ([modes isProxy])
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
marray[i] = [modes objectAtIndex: i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[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;
|
2009-05-27 10:53:45 +00:00
|
|
|
|
- (id) _init;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
- (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;
|
2009-09-07 09:53:27 +00:00
|
|
|
|
unsigned i;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context == nil)
|
|
|
|
|
{
|
|
|
|
|
context = [[GSRunLoopCtxt alloc] initWithMode: mode extra: _extra];
|
|
|
|
|
NSMapInsert(_contextMap, context->mode, context);
|
|
|
|
|
RELEASE(context);
|
|
|
|
|
}
|
|
|
|
|
watchers = context->watchers;
|
2006-03-21 15:33:05 +00:00
|
|
|
|
GSIArrayAddItem(watchers, (GSIArrayItem)((id)item));
|
2009-09-07 09:53:27 +00:00
|
|
|
|
i = GSIArrayCount(watchers);
|
|
|
|
|
if (i % 1000 == 0 && i > context->maxWatchers)
|
|
|
|
|
{
|
|
|
|
|
context->maxWatchers = i;
|
|
|
|
|
NSLog(@"WARNING ... there are %u watchers scheduled in mode %@ of %@",
|
|
|
|
|
i, mode, self);
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) _checkPerformers: (GSRunLoopCtxt*)context
|
|
|
|
|
{
|
2006-06-17 17:20:22 +00:00
|
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
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]);
|
2006-06-17 17:20:22 +00:00
|
|
|
|
IF_NO_GC([arp emptyPool]);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2006-06-17 17:20:22 +00:00
|
|
|
|
RELEASE(arp);
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-27 10:53:45 +00:00
|
|
|
|
- (id) _init
|
|
|
|
|
{
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self != nil)
|
|
|
|
|
{
|
|
|
|
|
_contextStack = [NSMutableArray new];
|
|
|
|
|
_contextMap = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
|
|
|
|
NSObjectMapValueCallBacks, 0);
|
|
|
|
|
_timedPerformers = [[NSMutableArray alloc] initWithCapacity: 8];
|
|
|
|
|
#ifdef HAVE_POLL_F
|
|
|
|
|
#if GS_WITH_GC
|
|
|
|
|
_extra = NSAllocateCollectable(sizeof(pollextra), NSScannedOption);
|
|
|
|
|
#else
|
|
|
|
|
_extra = NSZoneMalloc(NSDefaultMallocZone(), sizeof(pollextra));
|
|
|
|
|
memset(_extra, '\0', sizeof(pollextra));
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
- (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
|
|
|
|
|
2003-07-28 16:44:24 +00:00
|
|
|
|
if (info != nil && (id)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
|
|
|
|
- (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];
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
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
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p><code>NSRunLoop</code> instances handle various utility tasks that must
|
|
|
|
|
* be performed repetitively in an application, such as processing input
|
|
|
|
|
* events, listening for distributed objects communications, firing
|
|
|
|
|
* [NSTimer]s, and sending notifications and other messages
|
|
|
|
|
* asynchronously.</p>
|
|
|
|
|
*
|
2009-05-27 10:53:45 +00:00
|
|
|
|
* <p>There is one run loop per thread in an application, which
|
2009-05-27 11:03:42 +00:00
|
|
|
|
* may always be obtained through the <code>+currentRunLoop</code> method
|
|
|
|
|
* (you cannot use -init or +new),
|
2004-06-22 22:40:40 +00:00
|
|
|
|
* however unless you are using the AppKit and the [NSApplication] class, the
|
|
|
|
|
* run loop will not be started unless you explicitly send it a
|
|
|
|
|
* <code>-run</code> message.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>At any given point, a run loop operates in a single <em>mode</em>, usually
|
|
|
|
|
* <code>NSDefaultRunLoopMode</code>. Other options include
|
|
|
|
|
* <code>NSConnectionReplyMode</code>, and certain modes used by the AppKit.</p>
|
|
|
|
|
*/
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns the run loop instance for the current thread.
|
|
|
|
|
*/
|
1999-03-02 08:58:30 +00:00
|
|
|
|
+ (NSRunLoop*) currentRunLoop
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2008-03-17 15:23:11 +00:00
|
|
|
|
GSRunLoopThreadInfo *info = GSRunLoopInfoForThread(nil);
|
|
|
|
|
NSRunLoop *current = info->loop;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2008-03-17 15:23:11 +00:00
|
|
|
|
if (current == nil)
|
|
|
|
|
{
|
2009-05-27 10:53:45 +00:00
|
|
|
|
current = info->loop = [[self alloc] _init];
|
2008-03-17 15:23:11 +00:00
|
|
|
|
/* 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);
|
|
|
|
|
}
|
2008-05-21 09:34:47 +00:00
|
|
|
|
if (context->housekeeper != nil)
|
2008-03-17 15:23:11 +00:00
|
|
|
|
{
|
|
|
|
|
[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;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1996-04-10 18:20:36 +00:00
|
|
|
|
|
1999-03-02 08:58:30 +00:00
|
|
|
|
- (id) init
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2010-02-25 18:49:31 +00:00
|
|
|
|
DESTROY(self);
|
2009-05-27 10:53:45 +00:00
|
|
|
|
return nil;
|
1996-04-10 18:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) dealloc
|
1996-04-10 18:20:36 +00:00
|
|
|
|
{
|
2005-02-23 22:17:54 +00:00
|
|
|
|
#ifdef HAVE_POLL_F
|
2002-01-31 22:28:27 +00:00
|
|
|
|
if (_extra != 0)
|
|
|
|
|
{
|
|
|
|
|
pollextra *e = (pollextra*)_extra;
|
|
|
|
|
if (e->index != 0)
|
2009-03-09 15:11:51 +00:00
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), e->index);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), e);
|
2002-01-31 22:28:27 +00:00
|
|
|
|
}
|
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);
|
2009-03-10 11:30:16 +00:00
|
|
|
|
[super dealloc];
|
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;
|
2007-12-07 06:32:04 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
|
2009-09-10 16:41:06 +00:00
|
|
|
|
if ([timer isKindOfClass: [NSTimer class]] == NO
|
|
|
|
|
|| [timer isProxy] == YES)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"[%@-%@] not a valid timer",
|
|
|
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
|
|
|
}
|
|
|
|
|
if ([mode isKindOfClass: [NSString class]] == NO)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"[%@-%@] not a valid mode",
|
|
|
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-07 06:32:04 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"add timer for %f in %@",
|
|
|
|
|
[[timer fireDate] timeIntervalSinceReferenceDate], mode);
|
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;
|
2007-12-07 06:32:04 +00:00
|
|
|
|
i = GSIArrayCount(timers);
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
if (timer == GSIArrayItemAtIndex(timers, i).obj)
|
|
|
|
|
{
|
|
|
|
|
return; /* Timer already present */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* NB. A previous version of the timer code maintained an ordered
|
|
|
|
|
* array on the theory that we could improve performance by only
|
|
|
|
|
* checking the first few timers (up to the first one whose fire
|
|
|
|
|
* date is in the future) each time -limitDateForMode: is called.
|
|
|
|
|
* The problem with this was that it's possible for one timer to
|
|
|
|
|
* be added in multiple modes (or to different run loops) and for
|
|
|
|
|
* a repeated timer this could mean that the firing of the timer
|
|
|
|
|
* in one mode/loop adjusts its date ... without changing the
|
|
|
|
|
* ordering of the timers in the other modes/loops which contain
|
|
|
|
|
* the timer. When the ordering of timers in an array was broken
|
|
|
|
|
* we could get delays in processing timeouts, so we reverted to
|
|
|
|
|
* simply having timers in an unordered array and checking them
|
|
|
|
|
* all each time -limitDateForMode: is called.
|
|
|
|
|
*/
|
|
|
|
|
GSIArrayAddItem(timers, (GSIArrayItem)((id)timer));
|
2009-09-07 09:53:27 +00:00
|
|
|
|
i = GSIArrayCount(timers);
|
|
|
|
|
if (i % 1000 == 0 && i > context->maxTimers)
|
|
|
|
|
{
|
|
|
|
|
context->maxTimers = i;
|
|
|
|
|
NSLog(@"WARNING ... there are %u timers scheduled in mode %@ of %@",
|
|
|
|
|
i, mode, self);
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-09-10 14:22:14 +00:00
|
|
|
|
|
|
|
|
|
/* Ensure that the fire date has been updated either by the timeout handler
|
|
|
|
|
* updating it or by incrementing it ourselves.<br />
|
|
|
|
|
* Return YES if it was updated, NO if it was invalidated.
|
|
|
|
|
*/
|
|
|
|
|
static BOOL
|
|
|
|
|
updateTimer(NSTimer *t, NSDate *d, NSTimeInterval now)
|
|
|
|
|
{
|
|
|
|
|
if (timerInvalidated(t) == YES)
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
if (timerDate(t) == d)
|
|
|
|
|
{
|
|
|
|
|
NSTimeInterval ti = [d timeIntervalSinceReferenceDate];
|
|
|
|
|
NSTimeInterval increment = [t timeInterval];
|
|
|
|
|
|
2009-09-10 16:41:06 +00:00
|
|
|
|
if (increment <= 0.0)
|
2009-09-10 14:22:14 +00:00
|
|
|
|
{
|
2009-09-10 16:41:06 +00:00
|
|
|
|
/* Should never get here ... unless a subclass is returning
|
|
|
|
|
* a bad interval ... we return NO so that the timer gets
|
|
|
|
|
* removed from the loop.
|
|
|
|
|
*/
|
|
|
|
|
NSLog(@"WARNING timer %@ had bad interval ... removed", t);
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ti += increment; // Hopefully a single increment will do.
|
|
|
|
|
|
|
|
|
|
if (ti < now)
|
|
|
|
|
{
|
|
|
|
|
NSTimeInterval add;
|
|
|
|
|
|
|
|
|
|
/* Just incrementing the date was insufficieint to bring it to
|
|
|
|
|
* the current time, so we must have missed one or more fire
|
|
|
|
|
* opportunities, or the fire date has been set on the timer.
|
|
|
|
|
* If a fire date long ago has been set and the increment value
|
|
|
|
|
* is really small, we might need to increment very many times
|
|
|
|
|
* to get the new fire date. To avoid looping for ages, we
|
|
|
|
|
* calculate the number of increments needed and do them in one
|
|
|
|
|
* go.
|
|
|
|
|
*/
|
|
|
|
|
add = floor((now - ti) / increment);
|
|
|
|
|
ti += (increment * add);
|
|
|
|
|
if (ti < now)
|
|
|
|
|
{
|
|
|
|
|
add++;
|
|
|
|
|
ti += increment;
|
|
|
|
|
}
|
2009-09-10 14:22:14 +00:00
|
|
|
|
}
|
|
|
|
|
d = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: ti];
|
|
|
|
|
[t setFireDate: d];
|
|
|
|
|
RELEASE(d);
|
|
|
|
|
}
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
/**
|
2003-10-28 11:10:23 +00:00
|
|
|
|
* Fires timers whose fire date has passed, and checks timers and limit dates
|
2006-03-04 06:47:56 +00:00
|
|
|
|
* for input sources, determining the earliest time that any future timeout
|
2009-05-27 10:53:45 +00:00
|
|
|
|
* becomes due. Returns that date/time.<br />
|
|
|
|
|
* Returns distant future if the loop contains no timers, just input sources
|
|
|
|
|
* without timeouts.<br />
|
|
|
|
|
* Returns nil if the loop contains neither timers nor input sources.
|
2002-01-16 14:00:59 +00:00
|
|
|
|
*/
|
1999-04-21 20:16:25 +00:00
|
|
|
|
- (NSDate*) limitDateForMode: (NSString*)mode
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2006-03-04 06:47:56 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDate *when = nil;
|
2002-01-28 15:16:33 +00:00
|
|
|
|
|
2006-03-04 06:47:56 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
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
|
|
|
|
NSString *savedMode = _currentMode;
|
|
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
|
|
|
|
|
|
|
|
|
_currentMode = mode;
|
|
|
|
|
NS_DURING
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2006-03-05 07:06:58 +00:00
|
|
|
|
GSIArray timers = context->timers;
|
|
|
|
|
NSTimeInterval now;
|
2009-09-10 14:22:14 +00:00
|
|
|
|
NSDate *earliest;
|
|
|
|
|
NSDate *d;
|
2006-03-05 07:06:58 +00:00
|
|
|
|
NSTimer *t;
|
2007-12-22 18:10:12 +00:00
|
|
|
|
NSTimeInterval ti;
|
2009-09-10 14:22:14 +00:00
|
|
|
|
NSTimeInterval ei;
|
2009-09-10 14:48:37 +00:00
|
|
|
|
unsigned c;
|
2007-12-07 06:32:04 +00:00
|
|
|
|
unsigned i;
|
2006-03-05 07:06:58 +00:00
|
|
|
|
|
2010-08-10 10:38:50 +00:00
|
|
|
|
ei = 0.0; // Only needed to avoid compiler warning
|
|
|
|
|
|
2006-03-05 07:06:58 +00:00
|
|
|
|
/*
|
|
|
|
|
* Save current time so we don't keep redoing system call to
|
2007-12-24 12:08:11 +00:00
|
|
|
|
* get it and so that we check timer fire dates against a known
|
|
|
|
|
* value at the point when the method was called.
|
|
|
|
|
* If we refetched the date after firing each timer, the time
|
|
|
|
|
* taken in firing the timer could be large enough so we would
|
|
|
|
|
* just keep firing the timer repeatedly and never return from
|
|
|
|
|
* this method.
|
2006-03-05 07:06:58 +00:00
|
|
|
|
*/
|
2010-12-27 07:03:50 +00:00
|
|
|
|
now = GSPrivateTimeNow();
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
2009-09-10 15:30:55 +00:00
|
|
|
|
/* Fire housekeeping timer as necessary
|
2006-03-04 06:47:56 +00:00
|
|
|
|
*/
|
2007-12-07 08:35:16 +00:00
|
|
|
|
if ((t = context->housekeeper) != nil)
|
|
|
|
|
{
|
|
|
|
|
if (timerInvalidated(t))
|
|
|
|
|
{
|
|
|
|
|
DESTROY(context->housekeeper);
|
|
|
|
|
}
|
2009-09-10 14:22:14 +00:00
|
|
|
|
else if ([(d=timerDate(t)) timeIntervalSinceReferenceDate] <= now)
|
2007-12-07 08:35:16 +00:00
|
|
|
|
{
|
|
|
|
|
[t fire];
|
2009-11-27 07:53:38 +00:00
|
|
|
|
GSPrivateNotifyASAP(_currentMode);
|
2007-12-07 08:35:16 +00:00
|
|
|
|
IF_NO_GC([arp emptyPool]);
|
2009-09-10 14:22:14 +00:00
|
|
|
|
updateTimer(t, d, now);
|
2007-12-07 08:35:16 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2006-03-04 06:47:56 +00:00
|
|
|
|
|
2009-09-10 15:30:55 +00:00
|
|
|
|
/* Fire the oldest/first valid timer whose fire date has passed
|
|
|
|
|
* and fire it.
|
|
|
|
|
* We fire timers in the order in which they were added to the
|
|
|
|
|
* run loop rather than in date order. This prevents code
|
|
|
|
|
* from blocking other timers by adding timers whose fire date
|
|
|
|
|
* is some time in the past... we guarantee fair handling.
|
2009-09-10 14:22:14 +00:00
|
|
|
|
*/
|
2009-09-10 14:48:37 +00:00
|
|
|
|
c = GSIArrayCount(timers);
|
|
|
|
|
for (i = 0; i < c; i++)
|
2009-09-10 14:22:14 +00:00
|
|
|
|
{
|
2009-09-10 14:48:37 +00:00
|
|
|
|
t = GSIArrayItemAtIndex(timers, i).obj;
|
2009-09-10 15:30:55 +00:00
|
|
|
|
if (timerInvalidated(t) == NO)
|
2009-09-10 14:22:14 +00:00
|
|
|
|
{
|
2009-09-10 15:30:55 +00:00
|
|
|
|
d = timerDate(t);
|
|
|
|
|
ti = [d timeIntervalSinceReferenceDate];
|
|
|
|
|
if (ti < now)
|
2009-09-10 14:48:37 +00:00
|
|
|
|
{
|
2009-09-10 15:30:55 +00:00
|
|
|
|
GSIArrayRemoveItemAtIndexNoRelease(timers, i);
|
|
|
|
|
[t fire];
|
2010-08-10 10:38:50 +00:00
|
|
|
|
GSPrivateNotifyASAP(_currentMode);
|
2009-09-10 15:30:55 +00:00
|
|
|
|
IF_NO_GC([arp emptyPool]);
|
|
|
|
|
if (updateTimer(t, d, now) == YES)
|
|
|
|
|
{
|
|
|
|
|
/* Updated ... replace in array.
|
|
|
|
|
*/
|
|
|
|
|
GSIArrayAddItemNoRetain(timers,
|
|
|
|
|
(GSIArrayItem)((id)t));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* The timer was invalidated, so we can
|
|
|
|
|
* release it as we aren't putting it back
|
|
|
|
|
* in the array.
|
|
|
|
|
*/
|
|
|
|
|
RELEASE(t);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-09-10 14:48:37 +00:00
|
|
|
|
}
|
2009-09-10 14:22:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-10 15:30:55 +00:00
|
|
|
|
/* Now, find the earliest remaining timer date while removing
|
|
|
|
|
* any invalidated timers. We iterate from the end of the
|
|
|
|
|
* array to minimise the amount of array alteration needed.
|
2009-09-10 14:22:14 +00:00
|
|
|
|
*/
|
2009-09-10 14:48:37 +00:00
|
|
|
|
earliest = nil;
|
|
|
|
|
i = GSIArrayCount(timers);
|
|
|
|
|
while (i-- > 0)
|
2009-09-10 14:22:14 +00:00
|
|
|
|
{
|
2009-09-10 14:48:37 +00:00
|
|
|
|
t = GSIArrayItemAtIndex(timers, i).obj;
|
|
|
|
|
if (timerInvalidated(t) == YES)
|
2009-09-10 14:22:14 +00:00
|
|
|
|
{
|
2009-09-10 14:48:37 +00:00
|
|
|
|
GSIArrayRemoveItemAtIndex(timers, i);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
d = timerDate(t);
|
|
|
|
|
ti = [d timeIntervalSinceReferenceDate];
|
|
|
|
|
if (earliest == nil || ti < ei)
|
2009-09-10 14:22:14 +00:00
|
|
|
|
{
|
2009-09-10 14:48:37 +00:00
|
|
|
|
earliest = d;
|
|
|
|
|
ei = ti;
|
2009-09-10 14:22:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-12-22 18:10:12 +00:00
|
|
|
|
|
|
|
|
|
/* The earliest date of a valid timeout is copied into 'when'
|
|
|
|
|
* and used as our limit date.
|
|
|
|
|
*/
|
|
|
|
|
if (earliest != nil)
|
|
|
|
|
{
|
|
|
|
|
when = [earliest copy];
|
|
|
|
|
}
|
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
|
|
|
|
|
2009-01-12 12:48:46 +00:00
|
|
|
|
if (when == nil)
|
2006-03-21 15:33:05 +00:00
|
|
|
|
{
|
|
|
|
|
GSIArray watchers = context->watchers;
|
|
|
|
|
unsigned i = GSIArrayCount(watchers);
|
|
|
|
|
|
|
|
|
|
while (i-- > 0)
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
|
GSRunLoopWatcher *w = GSIArrayItemAtIndex(watchers, i).obj;
|
|
|
|
|
|
|
|
|
|
if (w->_invalidated == YES)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, i);
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
|
if (GSIArrayCount(context->watchers) > 0)
|
2006-03-04 06:47:56 +00:00
|
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
|
when = theFuture;
|
2006-03-04 06:47:56 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
2009-01-12 12:48:46 +00:00
|
|
|
|
#if !GS_WITH_GC
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AUTORELEASE(when);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2007-12-06 19:51:11 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"limit date %f in %@",
|
|
|
|
|
[when timeIntervalSinceReferenceDate], mode);
|
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
|
|
|
|
/**
|
2003-10-28 11:10:23 +00:00
|
|
|
|
* Listen for events from 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 />
|
2009-01-15 15:06:04 +00:00
|
|
|
|
* If the supplied mode is nil, uses NSDefaultRunLoopMode.<br />
|
2009-05-27 10:53:45 +00:00
|
|
|
|
* If there are no input sources or timers in the mode, returns immediately.
|
2002-01-16 11:38:46 +00:00
|
|
|
|
*/
|
2005-02-22 11:22:44 +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;
|
2008-11-17 13:45:32 +00:00
|
|
|
|
NSTimeInterval ti = 0;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
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
|
|
|
|
{
|
2006-03-04 06:47:56 +00:00
|
|
|
|
/*
|
|
|
|
|
* If we have a housekeeping timer, and it is earlier than the
|
|
|
|
|
* limit date we have been given, we use the date of the housekeeper
|
|
|
|
|
* to determine when to stop.
|
|
|
|
|
*/
|
|
|
|
|
if (limit_date != nil && context != nil && context->housekeeper != nil
|
|
|
|
|
&& [timerDate(context->housekeeper) timeIntervalSinceReferenceDate]
|
|
|
|
|
< [limit_date timeIntervalSinceReferenceDate])
|
|
|
|
|
{
|
|
|
|
|
limit_date = timerDate(context->housekeeper);
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-27 10:53:45 +00:00
|
|
|
|
if (context == nil
|
|
|
|
|
|| (GSIArrayCount(context->watchers) == 0
|
|
|
|
|
&& GSIArrayCount(context->timers) == 0))
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
2009-05-27 10:53:45 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"no inputs or timers in mode %@", mode);
|
2009-11-27 07:53:38 +00:00
|
|
|
|
GSPrivateNotifyASAP(_currentMode);
|
|
|
|
|
GSPrivateNotifyIdle(_currentMode);
|
2009-01-15 15:06:04 +00:00
|
|
|
|
/* Pause until the limit date or until we might have
|
|
|
|
|
* a method to perform in this thread.
|
2002-02-20 06:42:05 +00:00
|
|
|
|
*/
|
2009-01-15 15:06:04 +00:00
|
|
|
|
[GSRunLoopCtxt awakenedBefore: nil];
|
2006-10-19 13:51:19 +00:00
|
|
|
|
GSPrivateCheckTasks();
|
2002-02-20 06:42:05 +00:00
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
[self _checkPerformers: context];
|
|
|
|
|
}
|
2009-11-27 07:53:38 +00:00
|
|
|
|
GSPrivateNotifyASAP(_currentMode);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_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. */
|
2003-10-28 11:10:23 +00:00
|
|
|
|
if (limit_date == nil
|
|
|
|
|
|| (ti = [limit_date timeIntervalSinceNow]) <= 0.0)
|
2002-01-16 14:00:59 +00:00
|
|
|
|
{
|
|
|
|
|
/* Don't wait at all. */
|
2002-01-30 15:28:50 +00:00
|
|
|
|
timeout_ms = 0;
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
2003-10-28 11:10:23 +00:00
|
|
|
|
else
|
2002-01-16 14:00:59 +00:00
|
|
|
|
{
|
|
|
|
|
/* Wait until the LIMIT_DATE. */
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (ti >= INT_MAX / 1000)
|
|
|
|
|
{
|
|
|
|
|
timeout_ms = INT_MAX; // Far future.
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-05-27 10:53:45 +00:00
|
|
|
|
timeout_ms = (ti * 1000.0);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
2000-03-28 13:02:01 +00:00
|
|
|
|
|
2007-12-06 19:51:11 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop",
|
2009-05-27 10:53:45 +00:00
|
|
|
|
@"accept I/P before %d millisec from now in %@",
|
|
|
|
|
timeout_ms, mode);
|
2007-12-06 19:51:11 +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
|
|
|
|
{
|
2009-11-27 07:53:38 +00:00
|
|
|
|
GSPrivateNotifyIdle(_currentMode);
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[self _checkPerformers: context];
|
2009-11-27 07:53:38 +00:00
|
|
|
|
GSPrivateNotifyASAP(_currentMode);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_currentMode = savedMode;
|
2007-12-24 12:08:11 +00:00
|
|
|
|
|
|
|
|
|
/* Once a poll has been completed on a context, we can remove that
|
2002-01-30 15:28:50 +00:00
|
|
|
|
* 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];
|
2007-12-06 19:51:11 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"accept I/P completed in %@", mode);
|
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
|
|
|
|
/**
|
2007-06-13 06:09:42 +00:00
|
|
|
|
* Calls -limitDateForMode: to determine if a timeout occurs before the
|
|
|
|
|
* specified date, then calls -acceptInputForMode:beforeDate: to run the
|
|
|
|
|
* loop once.<br />
|
2002-10-03 16:21:06 +00:00
|
|
|
|
* The specified date may be nil ... in which case the loop runs
|
2003-10-28 11:10:23 +00:00
|
|
|
|
* until the limit date of the first input or timeout.<br />
|
2007-06-13 06:09:42 +00:00
|
|
|
|
* If the specified date is in the past, this runs the loop once only,
|
|
|
|
|
* to handle any events already available.<br />
|
2009-06-06 07:36:48 +00:00
|
|
|
|
* If there are no input sources or timers in mode, this method
|
2009-05-27 10:53:45 +00:00
|
|
|
|
* returns NO without running the loop (irrespective of the supplied
|
|
|
|
|
* date argument), otherwise returns YES.
|
2002-01-16 11:38:46 +00:00
|
|
|
|
*/
|
2001-06-21 13:36:13 +00:00
|
|
|
|
- (BOOL) runMode: (NSString*)mode beforeDate: (NSDate*)date
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2004-11-19 09:41:34 +00:00
|
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
|
|
|
|
NSDate *d;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-10-03 16:21:06 +00:00
|
|
|
|
NSAssert(mode != nil, NSInvalidArgumentException);
|
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)
|
2009-01-15 15:19:19 +00:00
|
|
|
|
{
|
|
|
|
|
RELEASE(arp);
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2002-10-03 16:21:06 +00:00
|
|
|
|
if (date != nil)
|
|
|
|
|
{
|
|
|
|
|
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);
|
2004-11-19 09:41:34 +00:00
|
|
|
|
RELEASE(arp);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return YES;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Runs the loop in <code>NSDefaultRunLoopMode</code> by repeated calls to
|
|
|
|
|
* -runMode:beforeDate: while there are still input sources. Exits when no
|
|
|
|
|
* more input sources remain.
|
|
|
|
|
*/
|
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
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Runs the loop in <code>NSDefaultRunLoopMode</code> by repeated calls to
|
|
|
|
|
* -runMode:beforeDate: while there are still input sources. Exits when no
|
|
|
|
|
* more input sources remain, or date is reached, whichever occurs first.
|
|
|
|
|
*/
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* OpenStep-compatibility methods for [NSRunLoop]. These methods are also
|
|
|
|
|
* all in OS X.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
@implementation NSRunLoop (OPENSTEP)
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Adds port to be monitored in given mode.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) addPort: (NSPort*)port
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2003-08-20 12:13:34 +00:00
|
|
|
|
[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
|
2003-01-31 19:06:51 +00:00
|
|
|
|
* with which the performs were set up match those supplied.<br />
|
|
|
|
|
* Matching of the argument may be either by pointer equality or by
|
|
|
|
|
* use of the [NSObject-isEqual:] method.
|
2002-08-27 14:24:54 +00:00
|
|
|
|
*/
|
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;
|
2010-02-22 10:13:20 +00:00
|
|
|
|
if (p->target == target && sel_isEqual(p->selector, aSelector)
|
2003-01-31 19:06:51 +00:00
|
|
|
|
&& (p->argument == argument || [p->argument isEqual: argument]))
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Configure event processing for acting as a server process for distributed
|
|
|
|
|
* objects. (In the current implementation this is a no-op.)
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) configureAsServer
|
|
|
|
|
{
|
2005-11-28 15:41:35 +00:00
|
|
|
|
return; /* Nothing to do here */
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-30 07:45:59 +00:00
|
|
|
|
/**
|
2003-02-15 14:58:25 +00:00
|
|
|
|
* Sets up sending of aSelector to target with argument.<br />
|
|
|
|
|
* The selector is sent before the next runloop iteration (unless
|
2006-03-04 06:47:56 +00:00
|
|
|
|
* cancelled before then) in any of the specified modes.<br />
|
2007-02-19 19:11:50 +00:00
|
|
|
|
* The target and argument objects are retained.<br />
|
2002-10-30 07:45:59 +00:00
|
|
|
|
* The order value is used to determine the order in which messages
|
2003-02-15 14:58:25 +00:00
|
|
|
|
* are sent if multiple messages have been set up. Messages with a lower
|
2006-03-04 06:47:56 +00:00
|
|
|
|
* order value are sent first.<br />
|
|
|
|
|
* If the modes array is empty, this method has no effect.
|
2002-10-30 07:45:59 +00:00
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) performSelector: (SEL)aSelector
|
1999-09-14 10:03:02 +00:00
|
|
|
|
target: (id)target
|
|
|
|
|
argument: (id)argument
|
2009-02-23 20:42:32 +00:00
|
|
|
|
order: (NSUInteger)order
|
1997-09-01 21:59:51 +00:00
|
|
|
|
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];
|
|
|
|
|
|
2008-07-06 09:18:30 +00:00
|
|
|
|
if ([modes isProxy])
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
array[i] = [modes objectAtIndex: i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[modes getObjects: array];
|
|
|
|
|
}
|
1999-09-14 13:50:24 +00:00
|
|
|
|
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;
|
2003-02-15 14:58:25 +00:00
|
|
|
|
if (p->order > order)
|
1999-09-14 13:50:24 +00:00
|
|
|
|
{
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIArrayInsertItem(performers, (GSIArrayItem)((id)item), i);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i == end)
|
|
|
|
|
{
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIArrayInsertItem(performers, (GSIArrayItem)((id)item), i);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2009-09-07 09:53:27 +00:00
|
|
|
|
i = GSIArrayCount(performers);
|
|
|
|
|
if (i % 1000 == 0 && i > context->maxPerformers)
|
|
|
|
|
{
|
|
|
|
|
context->maxPerformers = i;
|
|
|
|
|
NSLog(@"WARNING ... there are %u performers scheduled"
|
|
|
|
|
@" in mode %@ of %@", i, mode, self);
|
|
|
|
|
}
|
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
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Removes port to be monitored from given mode.
|
|
|
|
|
* Ports are also removed if they are detected to be invalid.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) removePort: (NSPort*)port
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2003-08-20 12:13:34 +00:00
|
|
|
|
[self removeEvent: (void*)port type: ET_RPORT forMode: mode all: NO];
|
1996-04-10 18:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
2006-03-04 06:47:56 +00:00
|
|
|
|
|