mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-10 16:20:42 +00:00
Various tweaks.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@12123 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
0e2bd154dc
commit
22cb892946
4 changed files with 468 additions and 411 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2002-01-16 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSRunLoop.m: Wrap code in exception handlers to reset current
|
||||
runloop mode if an exception occurs ... may remove this again if the
|
||||
performance is too bad.
|
||||
Use initialiser for NSTimer to avoid having to put timers into the
|
||||
autorelease pool.
|
||||
* Headers/Foundation/NSTimer.h: Expose GNUstep initialiser since
|
||||
OpenStep and MacOS-X don't have one.
|
||||
|
||||
Wed Jan 16 13:46:24 2002 Nicola Pero <nicola@brainstorm.co.uk>
|
||||
|
||||
Fixed dynamical loading of frameworks.
|
||||
|
|
|
@ -40,9 +40,9 @@
|
|||
BOOL _invalidated; /* Must be 2nd - for NSRunLoop optimisation */
|
||||
BOOL _repeats;
|
||||
NSTimeInterval _interval;
|
||||
id _target;
|
||||
SEL _selector;
|
||||
id _info;
|
||||
id _target;
|
||||
SEL _selector;
|
||||
id _info;
|
||||
}
|
||||
|
||||
/* Creating timer objects. */
|
||||
|
@ -51,18 +51,18 @@
|
|||
invocation: (NSInvocation*)invocation
|
||||
repeats: (BOOL)f;
|
||||
+ (NSTimer*) scheduledTimerWithTimeInterval: (NSTimeInterval)ti
|
||||
target: object
|
||||
target: (id)object
|
||||
selector: (SEL)selector
|
||||
userInfo: info
|
||||
userInfo: (id)info
|
||||
repeats: (BOOL)f;
|
||||
|
||||
+ (NSTimer*) timerWithTimeInterval: (NSTimeInterval)ti
|
||||
invocation: (NSInvocation*)invocation
|
||||
repeats: (BOOL)f;
|
||||
+ (NSTimer*) timerWithTimeInterval: (NSTimeInterval)ti
|
||||
target: object
|
||||
target: (id)object
|
||||
selector: (SEL)selector
|
||||
userInfo: info
|
||||
userInfo: (id)info
|
||||
repeats: (BOOL)f;
|
||||
|
||||
- (void) fire;
|
||||
|
@ -76,6 +76,13 @@
|
|||
- (NSDate*) fireDate;
|
||||
- (id) userInfo;
|
||||
|
||||
#ifndef NO_GNUSTEP
|
||||
- (id) initWithTimeInterval: (NSTimeInterval)ti
|
||||
targetOrInvocation: (id)object
|
||||
selector: (SEL)selector
|
||||
userInfo: (id)info
|
||||
repeats: (BOOL)f;
|
||||
#endif
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -244,8 +244,8 @@ static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
@end
|
||||
|
||||
/*
|
||||
* The GSTimedPerformer class is used to hold information about
|
||||
* messages which are due to be sent to objects at a particular time.
|
||||
* The GSTimedPerformer class is used to hold information about
|
||||
* messages which are due to be sent to objects at a particular time.
|
||||
*/
|
||||
@interface GSTimedPerformer: NSObject <GCFinalization>
|
||||
{
|
||||
|
@ -268,6 +268,7 @@ static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
- (void) dealloc
|
||||
{
|
||||
[self gcFinalize];
|
||||
TEST_RELEASE(timer);
|
||||
RELEASE(target);
|
||||
RELEASE(argument);
|
||||
[super dealloc];
|
||||
|
@ -275,7 +276,7 @@ static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
|
||||
- (void) fire
|
||||
{
|
||||
timer = nil;
|
||||
DESTROY(timer);
|
||||
[target performSelector: selector withObject: argument];
|
||||
[[[NSRunLoop currentRunLoop] _timedPerformers]
|
||||
removeObjectIdenticalTo: self];
|
||||
|
@ -284,7 +285,9 @@ static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
- (void) gcFinalize
|
||||
{
|
||||
if (timer != nil)
|
||||
[timer invalidate];
|
||||
{
|
||||
[timer invalidate];
|
||||
}
|
||||
}
|
||||
|
||||
- (id) initWithSelector: (SEL)aSelector
|
||||
|
@ -293,16 +296,17 @@ static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1)
|
|||
delay: (NSTimeInterval)delay
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
if (self != nil)
|
||||
{
|
||||
selector = aSelector;
|
||||
target = RETAIN(aTarget);
|
||||
argument = RETAIN(anArgument);
|
||||
timer = [NSTimer timerWithTimeInterval: delay
|
||||
target: self
|
||||
selector: @selector(fire)
|
||||
userInfo: nil
|
||||
repeats: NO];
|
||||
timer = [[NSTimer allocWithZone: NSDefaultMallocZone()]
|
||||
initWithTimeInterval: delay
|
||||
targetOrInvocation: self
|
||||
selector: @selector(fire)
|
||||
userInfo: nil
|
||||
repeats: NO];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -830,20 +834,23 @@ const NSMapTableValueCallBacks ArrayMapValueCallBacks =
|
|||
/* This is the designated initializer. */
|
||||
- (id) init
|
||||
{
|
||||
[super init];
|
||||
_mode_2_timers = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
||||
ArrayMapValueCallBacks, 0);
|
||||
_mode_2_watchers = NSCreateMapTable (NSObjectMapKeyCallBacks,
|
||||
ArrayMapValueCallBacks, 0);
|
||||
_mode_2_performers = NSCreateMapTable (NSObjectMapKeyCallBacks,
|
||||
ArrayMapValueCallBacks, 0);
|
||||
_timedPerformers = [[NSMutableArray alloc] initWithCapacity: 8];
|
||||
_efdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
||||
WatcherMapValueCallBacks, 0);
|
||||
_rfdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
||||
WatcherMapValueCallBacks, 0);
|
||||
_wfdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
||||
WatcherMapValueCallBacks, 0);
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
_mode_2_timers = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
||||
ArrayMapValueCallBacks, 0);
|
||||
_mode_2_watchers = NSCreateMapTable (NSObjectMapKeyCallBacks,
|
||||
ArrayMapValueCallBacks, 0);
|
||||
_mode_2_performers = NSCreateMapTable (NSObjectMapKeyCallBacks,
|
||||
ArrayMapValueCallBacks, 0);
|
||||
_timedPerformers = [[NSMutableArray alloc] initWithCapacity: 8];
|
||||
_efdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
||||
WatcherMapValueCallBacks, 0);
|
||||
_rfdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
||||
WatcherMapValueCallBacks, 0);
|
||||
_wfdMap = NSCreateMapTable (NSIntMapKeyCallBacks,
|
||||
WatcherMapValueCallBacks, 0);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -894,9 +901,10 @@ const NSMapTableValueCallBacks ArrayMapValueCallBacks =
|
|||
}
|
||||
|
||||
|
||||
/* Fire appropriate timers and determine the earliest time that anything
|
||||
watched for becomes useless. */
|
||||
|
||||
/**
|
||||
* Fire appropriate timers and determine the earliest time that anything
|
||||
* watched for becomes useless.
|
||||
*/
|
||||
- (NSDate*) limitDateForMode: (NSString*)mode
|
||||
{
|
||||
id saved_mode;
|
||||
|
@ -910,116 +918,125 @@ const NSMapTableValueCallBacks ArrayMapValueCallBacks =
|
|||
saved_mode = _current_mode;
|
||||
_current_mode = mode;
|
||||
|
||||
timers = NSMapGet(_mode_2_timers, mode);
|
||||
if (timers)
|
||||
NS_DURING
|
||||
{
|
||||
while (GSIArrayCount(timers) != 0)
|
||||
timers = NSMapGet(_mode_2_timers, mode);
|
||||
if (timers)
|
||||
{
|
||||
min_timer = GSIArrayItemAtIndex(timers, 0).obj;
|
||||
if (timerInvalidated(min_timer) == YES)
|
||||
while (GSIArrayCount(timers) != 0)
|
||||
{
|
||||
GSIArrayRemoveItemAtIndex(timers, 0);
|
||||
min_timer = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([timerDate(min_timer) timeIntervalSinceNow] > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
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. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Is this right? At the moment we invalidate and discard watchers
|
||||
whose limit-dates have passed. */
|
||||
watchers = NSMapGet(_mode_2_watchers, mode);
|
||||
if (watchers)
|
||||
{
|
||||
while (GSIArrayCount(watchers) != 0)
|
||||
{
|
||||
min_watcher = GSIArrayItemAtIndex(watchers, 0).obj;
|
||||
|
||||
if (min_watcher->_invalidated == YES)
|
||||
{
|
||||
GSIArrayRemoveItemAtIndex(watchers, 0);
|
||||
min_watcher = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([min_watcher->_date timeIntervalSinceNow] > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
id obj;
|
||||
NSDate *nxt = nil;
|
||||
|
||||
/*
|
||||
* If the receiver or its delegate wants to know about
|
||||
* timeouts - inform it and give it a chance to set a
|
||||
* revised limit date.
|
||||
*/
|
||||
GSIArrayRemoveItemAtIndexNoRelease(watchers, 0);
|
||||
obj = min_watcher->receiver;
|
||||
if ([obj respondsToSelector:
|
||||
@selector(timedOutEvent:type:forMode:)])
|
||||
min_timer = GSIArrayItemAtIndex(timers, 0).obj;
|
||||
if (timerInvalidated(min_timer) == YES)
|
||||
{
|
||||
nxt = [obj timedOutEvent: min_watcher->data
|
||||
type: min_watcher->type
|
||||
forMode: _current_mode];
|
||||
GSIArrayRemoveItemAtIndex(timers, 0);
|
||||
min_timer = nil;
|
||||
continue;
|
||||
}
|
||||
else if ([obj respondsToSelector: @selector(delegate)])
|
||||
|
||||
if ([timerDate(min_timer) timeIntervalSinceNow] > 0)
|
||||
{
|
||||
obj = [obj delegate];
|
||||
if (obj != nil && [obj respondsToSelector:
|
||||
@selector(timedOutEvent:type:forMode:)])
|
||||
break;
|
||||
}
|
||||
|
||||
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. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Is this right? At the moment we invalidate and discard watchers
|
||||
whose limit-dates have passed. */
|
||||
watchers = NSMapGet(_mode_2_watchers, mode);
|
||||
if (watchers)
|
||||
{
|
||||
while (GSIArrayCount(watchers) != 0)
|
||||
{
|
||||
min_watcher = GSIArrayItemAtIndex(watchers, 0).obj;
|
||||
|
||||
if (min_watcher->_invalidated == YES)
|
||||
{
|
||||
GSIArrayRemoveItemAtIndex(watchers, 0);
|
||||
min_watcher = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([min_watcher->_date timeIntervalSinceNow] > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
id obj;
|
||||
NSDate *nxt = nil;
|
||||
|
||||
/*
|
||||
* If the receiver or its delegate wants to know about
|
||||
* timeouts - inform it and give it a chance to set a
|
||||
* revised limit date.
|
||||
*/
|
||||
GSIArrayRemoveItemAtIndexNoRelease(watchers, 0);
|
||||
obj = min_watcher->receiver;
|
||||
if ([obj respondsToSelector:
|
||||
@selector(timedOutEvent:type:forMode:)])
|
||||
{
|
||||
nxt = [obj timedOutEvent: min_watcher->data
|
||||
type: min_watcher->type
|
||||
forMode: _current_mode];
|
||||
}
|
||||
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
|
||||
forMode: _current_mode];
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
_current_mode = saved_mode;
|
||||
}
|
||||
NS_HANDLER
|
||||
{
|
||||
_current_mode = saved_mode;
|
||||
[localException raise];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
|
||||
_current_mode = saved_mode;
|
||||
RELEASE(arp);
|
||||
|
||||
/*
|
||||
|
@ -1087,309 +1104,329 @@ const NSMapTableValueCallBacks ArrayMapValueCallBacks =
|
|||
}
|
||||
_current_mode = mode;
|
||||
|
||||
/* Find out how much time we should wait, and set SELECT_TIMEOUT. */
|
||||
if (!limit_date)
|
||||
NS_DURING
|
||||
{
|
||||
/* Don't wait at all. */
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
select_timeout = &timeout;
|
||||
}
|
||||
else if ((ti = [limit_date timeIntervalSinceNow])
|
||||
< LONG_MAX && ti > 0.0)
|
||||
{
|
||||
/* Wait until the LIMIT_DATE. */
|
||||
if (debug_run_loop)
|
||||
printf ("\tNSRunLoop accept input before %f (seconds from now %f)\n",
|
||||
[limit_date timeIntervalSinceReferenceDate], ti);
|
||||
/* If LIMIT_DATE has already past, return immediately. */
|
||||
timeout.tv_sec = ti;
|
||||
timeout.tv_usec = (ti - timeout.tv_sec) * 1000000.0;
|
||||
select_timeout = &timeout;
|
||||
}
|
||||
else if (ti <= 0.0)
|
||||
{
|
||||
/* The LIMIT_DATE has already past; return immediately without
|
||||
polling any inputs. */
|
||||
GSCheckTasks();
|
||||
[self _checkPerformers];
|
||||
GSNotifyASAP();
|
||||
if (debug_run_loop)
|
||||
printf ("\tNSRunLoop limit date past, returning\n");
|
||||
_current_mode = saved_mode;
|
||||
RELEASE(arp);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait forever. */
|
||||
if (debug_run_loop)
|
||||
printf ("\tNSRunLoop accept input waiting forever\n");
|
||||
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);
|
||||
|
||||
/*
|
||||
* Do the pre-listening set-up for the file descriptors of this mode.
|
||||
*/
|
||||
{
|
||||
GSIArray watchers;
|
||||
|
||||
watchers = NSMapGet(_mode_2_watchers, mode);
|
||||
if (watchers)
|
||||
{
|
||||
unsigned i = GSIArrayCount(watchers);
|
||||
|
||||
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 > end_inputs)
|
||||
end_inputs = fd;
|
||||
FD_SET (fd, &exception_fds);
|
||||
NSMapInsert(_efdMap, (void*)fd, info);
|
||||
num_inputs++;
|
||||
break;
|
||||
|
||||
case ET_RDESC:
|
||||
fd = (int)info->data;
|
||||
if (fd > end_inputs)
|
||||
end_inputs = fd;
|
||||
FD_SET (fd, &read_fds);
|
||||
NSMapInsert(_rfdMap, (void*)fd, info);
|
||||
num_inputs++;
|
||||
break;
|
||||
|
||||
case ET_WDESC:
|
||||
fd = (int)info->data;
|
||||
if (fd > end_inputs)
|
||||
end_inputs = 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];
|
||||
if (debug_run_loop)
|
||||
printf("\tNSRunLoop listening to %d sockets\n",
|
||||
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 > end_inputs)
|
||||
end_inputs = fd;
|
||||
NSMapInsert(_rfdMap,
|
||||
(void*)port_fd_array[port_fd_count], info);
|
||||
num_inputs++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end_inputs++;
|
||||
|
||||
/*
|
||||
* 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 it's notifications.
|
||||
*/
|
||||
if (GSCheckTasks() || GSNotifyMore())
|
||||
{
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
select_timeout = &timeout;
|
||||
select_return = select (end_inputs, &read_fds, &write_fds, &exception_fds,
|
||||
select_timeout);
|
||||
}
|
||||
else
|
||||
select_return = select (end_inputs, &read_fds, &write_fds, &exception_fds,
|
||||
select_timeout);
|
||||
|
||||
if (debug_run_loop)
|
||||
printf ("\tNSRunLoop select returned %d\n", select_return);
|
||||
|
||||
if (select_return < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
/* Find out how much time we should wait, and set SELECT_TIMEOUT. */
|
||||
if (!limit_date)
|
||||
{
|
||||
GSCheckTasks();
|
||||
select_return = 0;
|
||||
/* Don't wait at all. */
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
select_timeout = &timeout;
|
||||
}
|
||||
else if ((ti = [limit_date timeIntervalSinceNow])
|
||||
< LONG_MAX && ti > 0.0)
|
||||
{
|
||||
/* Wait until the LIMIT_DATE. */
|
||||
if (debug_run_loop)
|
||||
printf ("\tNSRunLoop accept input before %f (sec from now %f)\n",
|
||||
[limit_date timeIntervalSinceReferenceDate], ti);
|
||||
/* If LIMIT_DATE has already past, return immediately. */
|
||||
timeout.tv_sec = ti;
|
||||
timeout.tv_usec = (ti - timeout.tv_sec) * 1000000.0;
|
||||
select_timeout = &timeout;
|
||||
}
|
||||
else if (ti <= 0.0)
|
||||
{
|
||||
/* The LIMIT_DATE has already past; return immediately without
|
||||
polling any inputs. */
|
||||
GSCheckTasks();
|
||||
[self _checkPerformers];
|
||||
GSNotifyASAP();
|
||||
if (debug_run_loop)
|
||||
printf ("\tNSRunLoop limit date past, returning\n");
|
||||
_current_mode = saved_mode;
|
||||
RELEASE(arp);
|
||||
return;
|
||||
}
|
||||
#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 ();
|
||||
/* Wait forever. */
|
||||
if (debug_run_loop)
|
||||
printf ("\tNSRunLoop accept input waiting forever\n");
|
||||
select_timeout = NULL;
|
||||
}
|
||||
}
|
||||
if (select_return == 0)
|
||||
{
|
||||
|
||||
/*
|
||||
* 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);
|
||||
GSNotifyIdle();
|
||||
[self _checkPerformers];
|
||||
_current_mode = saved_mode;
|
||||
RELEASE(arp);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 roop is entered, the starting position (_fdStart)
|
||||
* is incremented - this is to ensure a fair distribtion over all
|
||||
* inputs where multiple inputs are in use. Note - _fdStart can be
|
||||
* modified while we are in the loop (by recursive calls).
|
||||
*/
|
||||
if (_fdStart >= end_inputs)
|
||||
{
|
||||
_fdStart = 0;
|
||||
fdIndex = 0;
|
||||
fdEnd = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fdStart++;
|
||||
fdIndex = _fdStart;
|
||||
fdEnd = _fdStart;
|
||||
}
|
||||
do
|
||||
{
|
||||
BOOL found = NO;
|
||||
|
||||
if (FD_ISSET (fdIndex, &exception_fds))
|
||||
{
|
||||
GSRunLoopWatcher *watcher;
|
||||
/*
|
||||
* Do the pre-listening set-up for the file descriptors of this mode.
|
||||
*/
|
||||
{
|
||||
GSIArray watchers;
|
||||
|
||||
watcher = (GSRunLoopWatcher*)NSMapGet(_efdMap, (void*)fdIndex);
|
||||
if (watcher != nil && watcher->_invalidated == NO)
|
||||
watchers = NSMapGet(_mode_2_watchers, mode);
|
||||
if (watchers)
|
||||
{
|
||||
unsigned i = GSIArrayCount(watchers);
|
||||
|
||||
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 > end_inputs)
|
||||
end_inputs = fd;
|
||||
FD_SET (fd, &exception_fds);
|
||||
NSMapInsert(_efdMap, (void*)fd, info);
|
||||
num_inputs++;
|
||||
break;
|
||||
|
||||
case ET_RDESC:
|
||||
fd = (int)info->data;
|
||||
if (fd > end_inputs)
|
||||
end_inputs = fd;
|
||||
FD_SET (fd, &read_fds);
|
||||
NSMapInsert(_rfdMap, (void*)fd, info);
|
||||
num_inputs++;
|
||||
break;
|
||||
|
||||
case ET_WDESC:
|
||||
fd = (int)info->data;
|
||||
if (fd > end_inputs)
|
||||
end_inputs = 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];
|
||||
}
|
||||
if (debug_run_loop)
|
||||
{
|
||||
printf("\tNSRunLoop listening to %d sockets\n",
|
||||
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 > end_inputs)
|
||||
{
|
||||
end_inputs = fd;
|
||||
}
|
||||
NSMapInsert(_rfdMap,
|
||||
(void*)port_fd_array[port_fd_count], info);
|
||||
num_inputs++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end_inputs++;
|
||||
|
||||
/*
|
||||
* 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 it's notifications.
|
||||
*/
|
||||
if (GSCheckTasks() || GSNotifyMore())
|
||||
{
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
select_timeout = &timeout;
|
||||
select_return = select (end_inputs, &read_fds, &write_fds,
|
||||
&exception_fds, select_timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
select_return = select (end_inputs, &read_fds, &write_fds,
|
||||
&exception_fds, select_timeout);
|
||||
}
|
||||
|
||||
if (debug_run_loop)
|
||||
{
|
||||
printf ("\tNSRunLoop select returned %d\n", select_return);
|
||||
}
|
||||
|
||||
if (select_return < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
{
|
||||
/*
|
||||
* The watcher is still valid - so call it's receivers
|
||||
* event handling method.
|
||||
*/
|
||||
(*watcher->handleEvent)(watcher->receiver,
|
||||
eventSel, watcher->data, watcher->type,
|
||||
(void*)(gsaddr)fdIndex, _current_mode);
|
||||
GSCheckTasks();
|
||||
select_return = 0;
|
||||
}
|
||||
GSNotifyASAP();
|
||||
found = YES;
|
||||
}
|
||||
if (FD_ISSET (fdIndex, &write_fds))
|
||||
{
|
||||
GSRunLoopWatcher *watcher;
|
||||
|
||||
watcher = NSMapGet(_wfdMap, (void*)fdIndex);
|
||||
if (watcher != nil && watcher->_invalidated == NO)
|
||||
#ifdef __MINGW__
|
||||
else if (errno == 0)
|
||||
{
|
||||
/*
|
||||
* The watcher is still valid - so call it's receivers
|
||||
* event handling method.
|
||||
*/
|
||||
(*watcher->handleEvent)(watcher->receiver,
|
||||
eventSel, watcher->data, watcher->type,
|
||||
(void*)(gsaddr)fdIndex, _current_mode);
|
||||
/* MinGW often returns an errno == 0. Not sure why */
|
||||
select_return = 0;
|
||||
}
|
||||
GSNotifyASAP();
|
||||
found = YES;
|
||||
}
|
||||
if (FD_ISSET (fdIndex, &read_fds))
|
||||
{
|
||||
GSRunLoopWatcher *watcher;
|
||||
|
||||
watcher = (GSRunLoopWatcher*)NSMapGet(_rfdMap, (void*)fdIndex);
|
||||
if (watcher != nil && watcher->_invalidated == NO)
|
||||
#endif
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The watcher is still valid - so call it's receivers
|
||||
* event handling method.
|
||||
*/
|
||||
(*watcher->handleEvent)(watcher->receiver,
|
||||
/* 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)
|
||||
{
|
||||
NSResetMapTable(_efdMap);
|
||||
NSResetMapTable(_rfdMap);
|
||||
NSResetMapTable(_wfdMap);
|
||||
GSNotifyIdle();
|
||||
[self _checkPerformers];
|
||||
_current_mode = saved_mode;
|
||||
RELEASE(arp);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 roop is entered, the starting position (_fdStart)
|
||||
* is incremented - this is to ensure a fair distribtion over all
|
||||
* inputs where multiple inputs are in use. Note - _fdStart can be
|
||||
* modified while we are in the loop (by recursive calls).
|
||||
*/
|
||||
if (_fdStart >= end_inputs)
|
||||
{
|
||||
_fdStart = 0;
|
||||
fdIndex = 0;
|
||||
fdEnd = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fdStart++;
|
||||
fdIndex = _fdStart;
|
||||
fdEnd = _fdStart;
|
||||
}
|
||||
do
|
||||
{
|
||||
BOOL found = NO;
|
||||
|
||||
if (FD_ISSET (fdIndex, &exception_fds))
|
||||
{
|
||||
GSRunLoopWatcher *watcher;
|
||||
|
||||
watcher = (GSRunLoopWatcher*)NSMapGet(_efdMap, (void*)fdIndex);
|
||||
if (watcher != nil && watcher->_invalidated == NO)
|
||||
{
|
||||
/*
|
||||
* The watcher is still valid - so call it's receivers
|
||||
* event handling method.
|
||||
*/
|
||||
(*watcher->handleEvent)(watcher->receiver,
|
||||
eventSel, watcher->data, watcher->type,
|
||||
(void*)(gsaddr)fdIndex, _current_mode);
|
||||
}
|
||||
GSNotifyASAP();
|
||||
found = YES;
|
||||
}
|
||||
if (FD_ISSET (fdIndex, &write_fds))
|
||||
{
|
||||
GSRunLoopWatcher *watcher;
|
||||
|
||||
watcher = NSMapGet(_wfdMap, (void*)fdIndex);
|
||||
if (watcher != nil && watcher->_invalidated == NO)
|
||||
{
|
||||
/*
|
||||
* The watcher is still valid - so call it's receivers
|
||||
* event handling method.
|
||||
*/
|
||||
(*watcher->handleEvent)(watcher->receiver,
|
||||
eventSel, watcher->data, watcher->type,
|
||||
(void*)(gsaddr)fdIndex, _current_mode);
|
||||
}
|
||||
GSNotifyASAP();
|
||||
found = YES;
|
||||
}
|
||||
if (FD_ISSET (fdIndex, &read_fds))
|
||||
{
|
||||
GSRunLoopWatcher *watcher;
|
||||
|
||||
watcher = (GSRunLoopWatcher*)NSMapGet(_rfdMap, (void*)fdIndex);
|
||||
if (watcher != nil && watcher->_invalidated == NO)
|
||||
{
|
||||
/*
|
||||
* The watcher is still valid - so call it's receivers
|
||||
* event handling method.
|
||||
*/
|
||||
(*watcher->handleEvent)(watcher->receiver,
|
||||
eventSel, watcher->data, watcher->type,
|
||||
(void*)(gsaddr)fdIndex, _current_mode);
|
||||
}
|
||||
GSNotifyASAP();
|
||||
found = YES;
|
||||
}
|
||||
if (found == YES && --select_return == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (++fdIndex >= end_inputs)
|
||||
{
|
||||
fdIndex = 0;
|
||||
}
|
||||
GSNotifyASAP();
|
||||
found = YES;
|
||||
}
|
||||
if (found == YES && --select_return == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (++fdIndex >= end_inputs)
|
||||
{
|
||||
fdIndex = 0;
|
||||
}
|
||||
while (fdIndex != fdEnd);
|
||||
|
||||
/* Clean up before returning. */
|
||||
NSResetMapTable(_efdMap);
|
||||
NSResetMapTable(_rfdMap);
|
||||
NSResetMapTable(_wfdMap);
|
||||
|
||||
[self _checkPerformers];
|
||||
GSNotifyASAP();
|
||||
_current_mode = saved_mode;
|
||||
}
|
||||
while (fdIndex != fdEnd);
|
||||
|
||||
|
||||
/* Clean up before returning. */
|
||||
NSResetMapTable(_efdMap);
|
||||
NSResetMapTable(_rfdMap);
|
||||
NSResetMapTable(_wfdMap);
|
||||
|
||||
[self _checkPerformers];
|
||||
GSNotifyASAP();
|
||||
_current_mode = saved_mode;
|
||||
NS_HANDLER
|
||||
{
|
||||
_current_mode = saved_mode;
|
||||
[localException raise];
|
||||
}
|
||||
NS_ENDHANDLER
|
||||
RELEASE(arp);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,10 @@ static Class NSDate_class;
|
|||
}
|
||||
}
|
||||
|
||||
/* This is the designated initializer. */
|
||||
/*
|
||||
* <init />
|
||||
* Initialise a newly allocated NSTimer object.
|
||||
*/
|
||||
- (id) initWithTimeInterval: (NSTimeInterval)seconds
|
||||
targetOrInvocation: (id)t
|
||||
selector: (SEL)sel
|
||||
|
|
Loading…
Reference in a new issue