/* Implementation of object for waiting on several input sources Copyright (C) 1996, 1997 Free Software Foundation, Inc. Original by: Andrew Kachites McCallum Created: March 1996 OPENSTEP version by: Richard Frith-Macdonald Created: August 1997 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* October 1996 - extensions to permit file descriptors to be watched for being readable or writable added by Richard Frith-Macdonald (richard@brainstorm.co.uk) */ /* Andrews original comments - may still be valid even now - Does it strike anyone else that NSNotificationCenter, NSNotificationQueue, NSNotification, NSRunLoop, the "notifications" a run loop sends the objects on which it is listening, NSEvent, and the event queue maintained by NSApplication are all much more intertwined/similar than OpenStep gives them credit for? I wonder if these classes could be re-organized a little to make a more uniform, "grand-unified" mechanism for: events, event-listening, event-queuing, and event-distributing. It could be quite pretty. (GNUstep would definitely provide classes that were compatible with all these OpenStep classes, but those classes could be wrappers around fundamentally cleaner GNU classes. RMS has advised using an underlying organization/implementation different from NeXT's whenever that makes sense---it helps legally distinguish our work.) Thoughts and insights, anyone? */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __WIN32__ #include #include #endif /* !__WIN32__ */ #include #include /* for memset() */ /* On some systems FD_ZERO is a macro that uses bzero(). Just define it to use memset(). */ #define bzero(PTR, LEN) memset (PTR, 0, LEN) static int debug_run_loop = 0; /* * The 'RunLoopWatcher' class was written to permit the (relatively) * easy addition of new events to be watched for in the runloop. * * To add a new type of event, the 'RunLoopEventType' enumeration must be * extended, and the methods must be modified to handle the new type. * * The internal variables if the RunLoopWatcher are used as follows - * If 'invalidated' is set, the wather should be disabled and should * be removed from the runloop when next encountered. * * The 'data' variable is used to identify the resource/event that the * watcher is interested in. * * The 'receiver' is the object which should be told when the event * occurs. This object is retained so that we know it will continue * to exist and can handle a callback. * * The 'type' variable indentifies the type of event watched for. * NSRunLoops [-acceptInputForMode:beforeDate:] method MUST contain * code to watch for events of each type. * * The 'limit' variable contains a date after which the event is useless * and the watcher can be removed from the runloop. If this is nil * then the watcher will only be removed if explicitly requested. * * To set this variable, the method adding the RunLoopWatcher to the * runloop must ask the 'receiver' (or its delegate) to supply a date * using the '[-limitDateForMode:]' message. * * NB. This class is private to NSRunLoop and must not be subclassed. */ @interface RunLoopWatcher: NSObject { @public BOOL invalidated; void *data; id receiver; RunLoopEventType type; NSDate *limit; unsigned count; } - (void) eventFor: (void*)info mode: (NSString*)mode; - initWithType: (RunLoopEventType)type receiver: (id)anObj data: (void*)data; - (void) invalidate; - (BOOL) isValid; @end @implementation RunLoopWatcher - (void) dealloc { [self invalidate]; [limit release]; [receiver release]; [super dealloc]; } - (void) eventFor: (void*)info mode: (NSString*)mode { if ([self isValid] == NO) { return; } if ([receiver respondsToSelector: @selector(receivedEvent:type:extra:forMode:)]) { [receiver receivedEvent: data type: type extra: info forMode: mode]; } else { switch (type) { case ET_RDESC: case ET_RPORT: [receiver readyForReadingOnFileDescriptor: (int)info]; break; case ET_WDESC: [receiver readyForWritingOnFileDescriptor: (int)info]; break; } } } - initWithType: (RunLoopEventType)aType receiver: (id)anObj data: (void*)item { invalidated = NO; switch (aType) { case ET_RDESC: type = aType; break; case ET_WDESC: type = aType; break; case ET_RPORT: type = aType; break; default: [NSException raise: NSInvalidArgumentException format: @"NSRunLoop - unknown event type"]; } receiver = [anObj retain]; data = item; return self; } - (void) invalidate { invalidated = YES; } - (BOOL) isValid { if (invalidated == YES) { return NO; } if ([receiver respondsToSelector: @selector(isValid)] && [receiver isValid] == NO) { [self invalidate]; return NO; } return YES; } @end @interface NSRunLoop (TimedPerformers) - (NSMutableArray*) _timedPerformers; @end @implementation NSRunLoop (TimedPerformers) - (NSMutableArray*) _timedPerformers { return _timedPerformers; } @end /* * The RunLoopPerformer class is used to hold information about * messages which are due to be sent to objects once a particular * runloop iteration has passed. */ @interface RunLoopPerformer: NSObject { SEL selector; id target; id argument; unsigned order; NSArray *modes; NSTimer *timer; } - (void) fire; - initWithSelector: (SEL)aSelector target: (id)target argument: (id)argument order: (unsigned int)order modes: (NSArray*)modes; - (BOOL) matchesSelector: (SEL)aSelector target: (id)aTarget argument: (id)anArgument; - (NSArray*) modes; - (unsigned int) order; - (void) setTimer: (NSTimer*)timer; @end @implementation RunLoopPerformer - (void) dealloc { [timer invalidate]; [target release]; [argument release]; [modes release]; [super dealloc]; } - (void) fire { if (timer != nil) { timer = nil; [[self retain] autorelease]; [[[NSRunLoop currentInstance] _timedPerformers] removeObjectIdenticalTo: self]; } [target performSelector: selector withObject: argument]; } - initWithSelector: (SEL)aSelector target: (id)aTarget argument: (id)anArgument order: (unsigned int)theOrder modes: (NSArray*)theModes { self = [super init]; if (self) { selector = aSelector; target = [aTarget retain]; argument = [anArgument retain]; order = theOrder; modes = [theModes copy]; } return self; } - (BOOL) matchesSelector: (SEL)aSelector target: (id)aTarget argument: (id)anArgument { if (selector == aSelector) { if (target == aTarget) { if ([argument isEqual:anArgument]) { return YES; } } } return NO; } - (NSArray*) modes { return modes; } - (unsigned int) order { return order; } - (void) setTimer: (NSTimer*)t { timer = t; } @end @implementation NSObject (TimedPerformers) + (void) cancelPreviousPerformRequestsWithTarget: (id)target selector: (SEL)aSelector object: (id)arg { NSMutableArray *array; int i; [target retain]; [arg retain]; array = [[NSRunLoop currentInstance] _timedPerformers]; for (i = [array count]; i > 0; i--) { if ([[array objectAtIndex: i-1] matchesSelector: aSelector target: target argument: arg]) { [array removeObjectAtIndex: i-1]; } } [arg release]; [target release]; } - (void) performSelector: (SEL)aSelector withObject: (id)argument afterDelay: (NSTimeInterval)seconds { NSMutableArray *array; RunLoopPerformer *item; array = [[NSRunLoop currentInstance] _timedPerformers]; item = [[RunLoopPerformer alloc] initWithSelector: aSelector target: self argument: argument order: 0 modes: nil]; [array addObject: item]; [item setTimer: [NSTimer scheduledTimerWithTimeInterval: seconds target: item selector: @selector(fire) userInfo: nil repeats: NO]]; [item release]; } - (void) performSelector: (SEL)aSelector withObject: (id)argument afterDelay: (NSTimeInterval)seconds inModes: (NSArray*)modes { NSRunLoop *loop; NSMutableArray *array; RunLoopPerformer *item; NSTimer *timer; int i; if (modes == nil || [modes count] == 0) { return; } loop = [NSRunLoop currentInstance]; array = [loop _timedPerformers]; item = [[RunLoopPerformer alloc] initWithSelector: aSelector target: self argument: argument order: 0 modes: nil]; [array addObject: item]; timer = [NSTimer timerWithTimeInterval: seconds target: item selector: @selector(fire) userInfo: nil repeats: NO]; [item setTimer: timer]; [item release]; for (i = 0; i < [modes count]; i++) { [loop addTimer: timer forMode: [modes objectAtIndex: i]]; } } @end @interface NSRunLoop (Private) - (void) _addWatcher: (RunLoopWatcher*)item forMode: (NSString*)mode; - (void) _checkPerformers; - (RunLoopWatcher*) _getWatcher: (void*)data type: (RunLoopEventType)type forMode: (NSString*)mode; - (NSMutableArray*) _performers; - (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: (RunLoopWatcher*) item forMode: (NSString*)mode { NSMutableArray *watchers; id obj; NSDate *limit; int count; watchers = NSMapGet (_mode_2_watchers, mode); if (watchers == nil) { watchers = [NSMutableArray new]; NSMapInsert (_mode_2_watchers, mode, watchers); [watchers release]; count = 0; } else { count = [watchers count]; } /* * If the receiver or its delegate (if any) respond to * 'limitDateForMode:' then we ask them for the limit date for * this watcher. */ obj = item->receiver; if ([obj respondsToSelector: @selector(limitDateForMode:)]) { NSDate *d = [obj limitDateForMode: mode]; ASSIGN(item->limit, d); } else if ([obj respondsToSelector: @selector(delegate)]) { obj = [obj delegate]; if ([obj respondsToSelector: @selector(limitDateForMode:)]) { NSDate *d = [obj limitDateForMode: mode]; ASSIGN(item->limit, d); } } limit = item->limit; /* * Make sure that the items in the watchers list are ordered. */ if (limit == nil || count == 0) { [watchers addObject:item]; } else { int i; for (i = 0; i < count; i++) { RunLoopWatcher *watcher = [watchers objectAtIndex: i]; NSDate *when = watcher->limit; if (when == nil || [limit earlierDate:when] == when) { [watchers insertObject:item atIndex:i]; break; } } if (i == count) { [watchers addObject:item]; } } } - (void) _checkPerformers { RunLoopPerformer *item; NSArray *array = [NSArray arrayWithArray: _performers]; int count = [array count]; unsigned pos; int i; for (i = 0; i < count; i++) { item = (RunLoopPerformer*)[array objectAtIndex: i]; pos = [_performers indexOfObjectIdenticalTo: item]; if (pos != NSNotFound) { if ([[item modes] containsObject: _current_mode]) { [_performers removeObjectAtIndex: pos]; [item fire]; } } } } @implementation NSRunLoop(GNUstepExtensions) + currentInstance { return [self currentRunLoop]; } + (NSString*) currentMode { return [[NSRunLoop currentRunLoop] currentMode]; } + (void) run { [[NSRunLoop currentRunLoop] run]; } + (void) runUntilDate: date { [[NSRunLoop currentRunLoop] runUntilDate: date]; } + (void) runUntilDate: date forMode: (NSString*)mode { [[NSRunLoop currentRunLoop] runUntilDate: date forMode: mode]; } + (BOOL) runOnceBeforeDate: date { return [[NSRunLoop currentRunLoop] runOnceBeforeDate: date]; } + (BOOL) runOnceBeforeDate: date forMode: (NSString*)mode { return [[NSRunLoop currentRunLoop] runOnceBeforeDate: date forMode: mode]; } - (void) addEvent: (void*)data type: (RunLoopEventType)type watcher: (id)watcher forMode: (NSString*)mode { RunLoopWatcher *info; if (mode == nil) { mode = _current_mode; } info = [self _getWatcher: data type: type forMode: mode]; if (info && info->receiver == (id)watcher) { /* Increment usage count for this watcher. */ info->count++; } else { /* Remove any existing handler for another watcher. */ [self _removeWatcher: data type: type forMode: mode]; /* Create new object to hold information. */ info = [[RunLoopWatcher alloc] initWithType: type receiver: watcher data: data]; /* Add the object to the array for the mode. */ [self _addWatcher:info forMode:mode]; [info release]; /* Now held in array. */ } } - (void) addReadDescriptor: (int)fd object: (id )listener forMode: (NSString*)mode { return [self addEvent: (void*)fd type: ET_RDESC watcher: (id)listener forMode: mode]; } /* Add our new handler information to the array. */ - (void) addWriteDescriptor: (int)fd object: (id )speaker forMode: (NSString*)mode { return [self addEvent: (void*)fd type: ET_WDESC watcher: (id)speaker forMode: mode]; } - (void) removeEvent: (void*)data type: (RunLoopEventType)type forMode: (NSString*)mode all: (BOOL)removeAll { if (mode == nil) { mode = _current_mode; } if (removeAll) { [self _removeWatcher: data type: type forMode: mode]; } else { RunLoopWatcher *info; info = [self _getWatcher: data type: type forMode: mode]; if (info) { if (info->count == 0) { [self _removeWatcher: data type: type forMode: mode]; } else { info->count--; } } } } - (void) removeReadDescriptor: (int)fd forMode: (NSString*)mode { return [self removeEvent:(void*)fd type: ET_RDESC forMode:mode all:NO]; } - (void) removeWriteDescriptor: (int)fd forMode: (NSString*)mode { return [self removeEvent:(void*)fd type: ET_WDESC forMode:mode all:NO]; } - (BOOL) runOnceBeforeDate: date { return [self runOnceBeforeDate: date forMode: _current_mode]; } /* Running the run loop once through for timers and input listening. */ - (BOOL) runOnceBeforeDate: date forMode: (NSString*)mode { return [self runMode:mode beforeDate:date]; } - (void) runUntilDate: date forMode: (NSString*)mode { volatile double ti; BOOL mayDoMore = YES; ti = [date timeIntervalSinceNow]; /* Positive values are in the future. */ while (ti > 0 && mayDoMore == YES) { id arp = [NSAutoreleasePool new]; if (debug_run_loop) printf ("\tNSRunLoop run until date %f seconds from now\n", ti); mayDoMore = [self runMode: mode beforeDate: date]; [arp release]; ti = [date timeIntervalSinceNow]; } } @end @implementation NSRunLoop + currentRunLoop { static NSString *key = @"NSRunLoopThreadKey"; NSRunLoop* r; NSThread* t; t = [NSThread currentThread]; r = [[t threadDictionary] objectForKey: key]; if (r == nil) { r = [NSRunLoop new]; [[t threadDictionary] setObject: r forKey: key]; [r release]; } return r; } + (void) initialize { if (self == [NSRunLoop class]) [self currentRunLoop]; } /* This is the designated initializer. */ - init { [super init]; _current_mode = NSDefaultRunLoopMode; _mode_2_timers = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 0); _mode_2_watchers = NSCreateMapTable (NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 0); _performers = [[NSMutableArray alloc] initWithCapacity:8]; _timedPerformers = [[NSMutableArray alloc] initWithCapacity:8]; return self; } - (void) dealloc { NSFreeMapTable(_mode_2_timers); NSFreeMapTable(_mode_2_watchers); [_performers release]; [_timedPerformers release]; [super dealloc]; } - (NSString*) currentMode { return _current_mode; } /* Adding timers. They are removed when they are invalid. */ - (void) addTimer: timer forMode: (NSString*)mode { Heap *timers; timers = NSMapGet (_mode_2_timers, mode); if (!timers) { timers = [Heap new]; NSMapInsert (_mode_2_timers, mode, timers); [timers release]; } /* xxx Should we make sure it isn't already there? */ [timers addObject: timer]; } /* Fire appropriate timers and determine the earliest time that anything watched for becomes useless. */ - limitDateForMode: (NSString*)mode { id saved_mode; Heap *timers; NSTimer *min_timer = nil; RunLoopWatcher *min_watcher = nil; NSMutableArray *watchers; NSDate *when; saved_mode = _current_mode; _current_mode = mode; timers = NSMapGet(_mode_2_timers, mode); if (timers) { while ((min_timer = [timers minObject]) != nil) { if (![min_timer isValid]) { [timers removeFirstObject]; min_timer = nil; continue; } if ([[min_timer fireDate] timeIntervalSinceNow] > 0) { break; } [min_timer retain]; [timers removeFirstObject]; /* Firing will also increment its fireDate, if it is repeating. */ [min_timer fire]; if ([min_timer isValid]) { [timers addObject: min_timer]; } [min_timer release]; min_timer = nil; [NSNotificationQueue runLoopASAP]; /* 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 ([watchers count] > 0) { min_watcher = (RunLoopWatcher*)[watchers objectAtIndex:0]; if (![min_watcher isValid]) { [watchers removeObjectAtIndex: 0]; min_watcher = nil; continue; } when = min_watcher->limit; if (when == nil || [when 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. */ 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 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. */ [min_watcher retain]; ASSIGN(min_watcher->limit, nxt); [watchers removeObjectAtIndex: 0]; [self _addWatcher: min_watcher forMode: mode]; [min_watcher release]; } else { /* * If the watcher is now useless - invalidate it and * remove it from the queue so that we don't need to * check it again. */ [min_watcher invalidate]; [watchers removeObjectAtIndex:0]; } min_watcher = nil; } } } /* * If there are timers - set limit date to the earliest of them. */ if (min_timer) { when = [min_timer fireDate]; } else { when = nil; } /* * If there are watchers, set the limit date to that of the earliest * watcher (or leave it as the date of the earliest timer if that is * before the watchers limit). * NB. A watcher without a limit date watches forever - so it's limit * is effectively some time in the distant future. */ if (min_watcher) { NSDate* lim; if (min_watcher->limit == nil) /* No limit for watcher */ { lim = [NSDate distantFuture]; /* - watches forever. */ } else { lim = min_watcher->limit; } if (when == nil) { when = lim; } else { when = [when earlierDate:lim]; } } /* * 'when' will only be nil if there are neither timers nor watchers * outstanding. */ if (when && debug_run_loop) { printf ("\tNSRunLoop limit date %f\n", [when timeIntervalSinceReferenceDate]); } _current_mode = saved_mode; return when; } - (RunLoopWatcher*) _getWatcher: (void*)data type: (RunLoopEventType)type forMode: (NSString*)mode { NSArray *watchers; RunLoopWatcher *info; int count; if (mode == nil) { mode = _current_mode; } watchers = NSMapGet (_mode_2_watchers, mode); if (watchers == nil) { return nil; } for (count = 0; count < [watchers count]; count++) { info = [watchers objectAtIndex: count]; if (info->type == type) { if (info->data == data) { return info; } } } return nil; } - (void) _removeWatcher: (void*)data type: (RunLoopEventType)type forMode: (NSString*)mode { NSMutableArray *watchers; if (mode == nil) { mode = _current_mode; } watchers = NSMapGet (_mode_2_watchers, mode); if (watchers) { int i; for (i = [watchers count]; i > 0; i--) { RunLoopWatcher* info; info = (RunLoopWatcher*)[watchers objectAtIndex:(i-1)]; if (info->type == type && info->data == data) { [info invalidate]; [watchers removeObject: info]; } } } } /* Listen to input sources. If LIMIT_DATE is nil, then don't wait; i.e. call select() with 0 timeout */ - (void) acceptInputForMode: (NSString*)mode beforeDate: limit_date { NSTimeInterval ti; struct timeval timeout; void *select_timeout; fd_set fds; /* The file descriptors we will listen to. */ fd_set read_fds; /* Copy for listening to read-ready fds. */ fd_set exception_fds; /* Copy for listening to exception fds. */ fd_set write_fds; /* Copy for listening for write-ready fds. */ int select_return; int fd_index; NSMapTable *rfd_2_object; NSMapTable *wfd_2_object; id saved_mode; int num_inputs = 0; NSAssert(mode, NSInvalidArgumentException); saved_mode = _current_mode; _current_mode = mode; /* Find out how much time we should wait, and set SELECT_TIMEOUT. */ if (!limit_date) { /* 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. */ if (ti < 0) { [self _checkPerformers]; if (debug_run_loop) printf ("\tNSRunLoop limit date past, returning\n"); _current_mode = saved_mode; return; } 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. */ _current_mode = saved_mode; 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 create an empty map for keeping track of which object is associated with which file descriptor. */ FD_ZERO (&fds); FD_ZERO (&write_fds); rfd_2_object = NSCreateMapTable (NSIntMapKeyCallBacks, NSObjectMapValueCallBacks, 0); wfd_2_object = NSCreateMapTable (NSIntMapKeyCallBacks, NSObjectMapValueCallBacks, 0); /* Do the pre-listening set-up for the file descriptors of this mode. */ { NSMutableArray *watchers; watchers = NSMapGet (_mode_2_watchers, mode); if (watchers) { int i; for (i = [watchers count]; i > 0; i--) { RunLoopWatcher* info = [watchers objectAtIndex:(i-1)]; int fd; if ([info isValid] == NO) { [watchers removeObjectAtIndex: (i-1)]; continue; } switch (info->type) { case ET_WDESC: fd = (int)info->data; FD_SET (fd, &write_fds); NSMapInsert (wfd_2_object, (void*)fd, info); num_inputs++; break; case ET_RDESC: fd = (int)info->data; FD_SET (fd, &fds); NSMapInsert (rfd_2_object, (void*)fd, info); num_inputs++; break; case ET_RPORT: { id port = info->receiver; int port_fd_count = 128; // xxx #define this constant int port_fd_array[port_fd_count]; if ([port respondsTo: @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_SET (port_fd_array[port_fd_count], &fds); NSMapInsert (rfd_2_object, (void*)port_fd_array[port_fd_count], info); num_inputs++; } } break; } } } } /* Wait for incoming data, listening to the file descriptors in _FDS. */ read_fds = fds; exception_fds = fds; /* Detect if the NSRunLoop is idle, and if necessary - dispatch the notifications from NSNotificationQueue's idle queue? */ if (num_inputs == 0 && [NSNotificationQueue runLoopMore]) { timeout.tv_sec = 0; timeout.tv_usec = 0; select_timeout = &timeout; select_return = select (FD_SETSIZE, &read_fds, &write_fds, &exception_fds, select_timeout); } else select_return = select (FD_SETSIZE, &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) { select_return = 0; } else { /* Some exceptional condition happened. */ /* xxx We can do something with exception_fds, instead of aborting here. */ perror ("[NSRunLoop acceptInputForMode:beforeDate:] select()"); abort (); } } if (select_return == 0) { NSFreeMapTable (rfd_2_object); NSFreeMapTable (wfd_2_object); [NSNotificationQueue runLoopIdle]; [self _checkPerformers]; _current_mode = saved_mode; return; } /* Look at all the file descriptors select() says are ready for reading; notify the corresponding object for each of the ready fd's. */ for (fd_index = 0; fd_index < FD_SETSIZE; fd_index++) { if (FD_ISSET (fd_index, &write_fds)) { id watcher = (id) NSMapGet (wfd_2_object, (void*)fd_index); NSAssert(watcher, NSInternalInconsistencyException); [watcher eventFor:(void*)fd_index mode:_current_mode]; [NSNotificationQueue runLoopASAP]; } if (FD_ISSET (fd_index, &read_fds)) { id watcher = (id) NSMapGet (rfd_2_object, (void*)fd_index); NSAssert(watcher, NSInternalInconsistencyException); [watcher eventFor:(void*)fd_index mode:_current_mode]; [NSNotificationQueue runLoopASAP]; } } /* Clean up before returning. */ NSFreeMapTable (rfd_2_object); NSFreeMapTable (wfd_2_object); [self _checkPerformers]; [NSNotificationQueue runLoopASAP]; _current_mode = saved_mode; } - (BOOL) runMode: (NSString*)mode beforeDate: date { id d; /* If date has already passed, simply return. */ if ([date timeIntervalSinceNow] < 0) { if (debug_run_loop) { printf ("\tNSRunLoop run mode with date already past\n"); } return NO; } /* Find out how long we can wait before first limit date. */ d = [self limitDateForMode: mode]; if (d == nil) { if (debug_run_loop) { printf ("\tNSRunLoop run mode with nothing to do\n"); } return NO; } /* Use the earlier of the two dates we have. */ d = [[d earlierDate:date] retain]; /* Wait, listening to our input sources. */ [self acceptInputForMode: mode beforeDate: d]; [d release]; return YES; } - (void) run { [self runUntilDate: [NSDate distantFuture]]; } - (void) runUntilDate: date { [self runUntilDate: date forMode: _current_mode]; } /* NSRunLoop mode strings. */ id NSDefaultRunLoopMode = @"NSDefaultRunLoopMode"; @end @implementation NSRunLoop (OPENSTEP) - (void) addPort: (NSPort*)port forMode: (NSString*)mode { return [self addEvent: (void*)port type: ET_RPORT watcher: (id)port forMode: (NSString*)mode]; } - (void) cancelPerformSelector: (SEL)aSelector target: target argument: argument { RunLoopPerformer *item; int count = [_performers count]; int i; [target retain]; [argument retain]; for (i = count; i > 0; i--) { item = (RunLoopPerformer*)[_performers objectAtIndex:(i-1)]; if ([item matchesSelector:aSelector target:target argument:argument]) { [_performers removeObjectAtIndex:(i-1)]; } } [argument release]; [target release]; } - (void) configureAsServer { /* Nothing to do here */ } - (void) performSelector: (SEL)aSelector target: target argument: argument order: (unsigned int)order modes: (NSArray*)modes { RunLoopPerformer *item; int count = [_performers count]; item = [[RunLoopPerformer alloc] initWithSelector: aSelector target: target argument: argument order: order modes: modes]; /* Add new item to list - reverse ordering */ if (count == 0) { [_performers addObject:item]; } else { int i; for (i = 0; i < count; i++) { if ([[_performers objectAtIndex:i] order] <= order) { [_performers insertObject:item atIndex:i]; break; } } if (i == count) { [_performers addObject:item]; } } [item release]; } - (void) removePort: (NSPort*)port forMode: (NSString*)mode { return [self removeEvent:(void*)port type: ET_RPORT forMode:mode all:NO]; } @end