]> err.no Git - sope/blob - libFoundation/Foundation/NSRunLoop.m
Add libxml2-dev to libsope-xml4.7-dev deps
[sope] / libFoundation / Foundation / NSRunLoop.m
1 /* 
2    NSRunLoop.m
3
4    Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
5    All rights reserved.
6
7    Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
8            Helge Hess <helge.hess@mdlink.de>
9
10    This file is part of libFoundation.
11
12    Permission to use, copy, modify, and distribute this software and its
13    documentation for any purpose and without fee is hereby granted, provided
14    that the above copyright notice appear in all copies and that both that
15    copyright notice and this permission notice appear in supporting
16    documentation.
17
18    We disclaim all warranties with regard to this software, including all
19    implied warranties of merchantability and fitness, in no event shall
20    we be liable for any special, indirect or consequential damages or any
21    damages whatsoever resulting from loss of use, data or profits, whether in
22    an action of contract, negligence or other tortious action, arising out of
23    or in connection with the use or performance of this software.
24 */
25
26 #include <Foundation/common.h>
27
28 #include <sys/types.h>
29 #if HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
31 #endif
32 #include <errno.h>
33
34 #if HAVE_SYS_TIME_H
35 # include <sys/time.h>  /* for struct timeval */
36 #endif
37
38 #if HAVE_STRING_H
39 # include <string.h>
40 #endif
41
42 #if HAVE_MEMORY_H
43 # include <memory.h>
44 #endif
45
46 #if !HAVE_MEMCPY
47 # define memcpy(d, s, n)       bcopy((s), (d), (n))
48 # define memmove(d, s, n)      bcopy((s), (d), (n))
49 #endif
50
51 #if HAVE_LIBC_H
52 # include <libc.h>
53 #else
54 # include <unistd.h>
55 #endif
56
57 #if HAVE_WINDOWS_H
58 # include <windows.h>
59 #endif
60
61 #if HAVE_SYS_SELECT_H
62 # include <sys/select.h>
63 #endif
64
65 #include <Foundation/NSRunLoop.h>
66 #include <Foundation/NSArray.h>
67 #include <Foundation/NSDictionary.h>
68 #include <Foundation/NSString.h>
69 #include <Foundation/NSValue.h>
70 #include <Foundation/NSAutoreleasePool.h>
71 #include <Foundation/NSDate.h>
72 #include <Foundation/NSTimer.h>
73 #include <Foundation/NSUtilities.h>
74 #include <Foundation/NSThread.h>
75 #include <Foundation/NSLock.h>
76 #include <Foundation/NSException.h>
77 #include <Foundation/NSPosixFileDescriptor.h>
78 #include <Foundation/NSNotification.h>
79 #include <Foundation/NSNotificationQueue.h>
80 #include <Foundation/UnixSignalHandler.h>
81
82 #include <extensions/objc-runtime.h>
83
84 NSString* NSDefaultRunLoopMode = @"NSDefaultRunLoopMode";
85 NSString* NSConnectionReplyMode = @"NSConnectionReplyMode";
86 NSString* NSFileObjectBecameActiveNotificationName =
87   @"NSFileObjectBecameActiveNotificationName";
88
89 static char *activityDesc[8] = {
90     "---", // 0
91     "--R", // 1
92     "-W-", // 2
93     "-WR", // 3
94     "E--", // 4
95     "E-R", // 5
96     "EW-", // 6
97     "EWR"  // 7
98 };
99
100 @interface NSRunLoopFileObjectInfo : NSObject
101 {
102     id                    fileObject;
103     NSPosixFileActivities watchedActivities;
104     BOOL                  canCheckAlive;
105 }
106
107 - (id)initWithFileObject:(id)_fileObject
108   activities:(NSPosixFileActivities)_activities;
109
110 - (BOOL)isAlive;
111 - (int)fileDescriptor;
112 - (NSPosixFileActivities)watchedActivities;
113
114 - (void)activity:(NSPosixFileActivities)_activity onDescriptor:(int)_fd;
115
116 @end
117
118 @implementation NSRunLoopFileObjectInfo
119
120 - (id)initWithFileObject:(id)_fileObject
121   activities:(NSPosixFileActivities)_activities
122 {
123   self->fileObject        = RETAIN(_fileObject);
124   self->watchedActivities = _activities;
125   self->canCheckAlive     = [_fileObject respondsToSelector:@selector(isAlive)];
126   return self;
127 }
128 - (id)init
129 {
130       NSLog(@"ERROR: do not use init with NSRunLoopFileObjectInfo ..");
131       self = AUTORELEASE(self);
132       return nil;
133 }
134
135 - (void)dealloc
136 {
137     RELEASE(self->fileObject);
138     [super dealloc];
139 }
140
141 - (BOOL)isEqual:(id)otherInfo
142 {
143     if (otherInfo == nil)
144         return NO;
145     if (*(Class *)self != *(Class *)otherInfo) /* check class */
146         return NO;
147     
148     return [self->fileObject isEqual:
149                     ((NSRunLoopFileObjectInfo *)otherInfo)->fileObject];
150 }
151
152 - (BOOL)isAlive {
153     return (self->canCheckAlive) ? [self->fileObject isAlive] : YES;
154 }
155 - (int)fileDescriptor
156 {
157     return [self->fileObject fileDescriptor];
158 }
159
160 - (NSPosixFileActivities)watchedActivities
161 {
162     return self->watchedActivities;
163 }
164
165 - (void)activity:(NSPosixFileActivities)_activity onDescriptor:(int)_fd
166 {
167 #if 0
168     NSLog(@"%s:%i: FileObject %@ became active ..", __PRETTY_FUNCTION__,__LINE__,
169           self->fileObject);
170 #endif
171     
172     if ([self->fileObject isKindOfClass:[NSPosixFileDescriptor class]]) {
173         if (_activity & NSPosixReadableActivity) {
174             [[self->fileObject delegate]
175                                activity:NSPosixReadableActivity
176                                posixFileDescriptor:self->fileObject];
177         }
178         if (_activity & NSPosixWritableActivity) {
179             [[self->fileObject delegate]
180                                activity:NSPosixWritableActivity
181                                posixFileDescriptor:self->fileObject];
182         }
183         if (_activity & NSPosixExceptionalActivity) {
184             [[self->fileObject delegate]
185                                activity:NSPosixExceptionalActivity
186                                posixFileDescriptor:self->fileObject];
187         }
188     }
189     else {
190         [[NSNotificationCenter defaultCenter]
191                                postNotificationName:
192                                  NSFileObjectBecameActiveNotificationName
193                                object:self->fileObject];
194     }
195 }
196
197 - (NSString *)description
198 {
199     return [NSString stringWithFormat:
200                        @"<%@[0x%p]: object=%@ actitivity=%s>",
201                        NSStringFromClass([self class]), self,
202                        self->fileObject,
203                        activityDesc[self->watchedActivities]
204                      ];
205 }
206
207 @end
208
209 @interface NSRunLoopTimerInfo : NSObject
210 {
211     NSTimer* timer;
212     NSDate* fireDate;
213 }
214
215 + (NSRunLoopTimerInfo*)infoWithTimer:(NSTimer*)timer;
216 - (void)recomputeFireDate;
217 - (NSComparisonResult)compare:(NSRunLoopTimerInfo*)anObject;
218 - (NSTimer*)timer;
219 - (NSDate*)fireDate;
220 @end
221
222 @implementation NSRunLoopTimerInfo
223
224 + (NSRunLoopTimerInfo *)infoWithTimer:(NSTimer*)aTimer
225 {
226     NSRunLoopTimerInfo *info = [self new];
227     
228     info->timer    = RETAIN(aTimer);
229     info->fireDate = RETAIN([aTimer fireDate]);
230     return AUTORELEASE(info);
231 }
232
233 - (void)dealloc
234 {
235     RELEASE(self->timer);
236     RELEASE(self->fireDate);
237     [super dealloc];
238 }
239
240 - (void)recomputeFireDate
241 {
242   if ([self->timer isValid]) {
243     id tmp = [self->timer fireDate];
244     ASSIGN(self->fireDate, tmp);
245   }
246 }
247
248 - (NSComparisonResult)compare:(NSRunLoopTimerInfo*)anObject
249 {
250     return [self->fireDate compare:anObject->fireDate];
251 }
252
253 - (NSTimer *)timer   { return self->timer;    }
254 - (NSDate *)fireDate { return self->fireDate; }
255
256 @end
257
258 @interface NSRunLoopActionHolder : NSObject
259 {
260     id target;
261     id argument;
262     SEL action;
263     int order;
264 }
265 + objectWithTarget:(id)target
266   argument:(id)argument
267   selector:(SEL)action
268   order:(int)order;
269 - (BOOL)isEqual:(id)anotherHolder;
270 - (void)execute;
271 @end
272
273 @implementation NSRunLoopActionHolder
274
275 + (id)objectWithTarget:(id)_target
276   argument:(id)_argument
277   selector:(SEL)_action
278   order:(int)_order
279 {
280     NSRunLoopActionHolder* holder = AUTORELEASE([self alloc]);
281
282     holder->target   = RETAIN(_target);
283     holder->argument = RETAIN(_argument);
284     holder->action   = _action;
285     holder->order    = _order;
286
287     return holder;
288 }
289
290 - (unsigned)hash
291 {
292   return [(NSObject *)self->target hash];
293 }
294
295 - (BOOL)isEqual:(id)otherObject
296 {
297     NSRunLoopActionHolder *anotherHolder;
298     
299     if ((anotherHolder = otherObject) == nil)
300         return NO;
301     if (![anotherHolder isKindOfClass:[NSRunLoopActionHolder class]])
302         return NO;
303     
304     return [self->target isEqual:anotherHolder->target]
305             && [argument isEqual:anotherHolder->argument]
306             && SEL_EQ(self->action, anotherHolder->action);
307 }
308
309 - (void)execute
310 {
311     [self->target performSelector:self->action withObject:self->argument];
312 }
313
314 - (NSComparisonResult)compare:(NSRunLoopActionHolder*)anotherHolder
315 {
316     return (order - anotherHolder->order);
317 }
318
319 @end /* NSRunLoopActionHolder */
320
321
322 @interface NSRunLoopInputManager : NSObject
323 {
324     NSMutableArray *fileObjects;
325     NSMutableArray *timers;
326     NSMutableArray *otherOperations;
327 }
328
329 - (void)addFileObject:(id)_fileObject
330   activities:(NSPosixFileActivities)_activities;
331 - (void)removeFileObject:(id)_fileObject;
332
333 - (void)addPosixFileDescriptor:(NSPosixFileDescriptor*)fileDescriptor;
334 - (void)removePosixFileDescriptor:(NSPosixFileDescriptor*)fileDescriptor;
335
336 - (void)addTimer:(NSTimer*)aTimer;
337
338 - (NSMutableArray*)fileObjects;
339 - (NSMutableArray*)timers;
340
341 - (void)addOperation:(NSRunLoopActionHolder*)holder;
342 - (void)removeOperation:(NSRunLoopActionHolder*)holder;
343 - (void)performAdditionalOperations;
344 @end
345
346
347 @implementation NSRunLoopInputManager
348
349 - (id)init
350 {
351     NSZone *z = [self zone];
352     self->fileObjects     = [[NSMutableArray allocWithZone:z] init];
353     self->timers          = [[NSMutableArray allocWithZone:z] init];
354     self->otherOperations = [[NSMutableArray allocWithZone:z] init];
355     return [super init];
356 }
357
358 - (void)dealloc
359 {
360     RELEASE(self->fileObjects);
361     RELEASE(self->timers);
362     RELEASE(self->otherOperations);
363     [super dealloc];
364 }
365
366 - (void)addFileObject:(id)_fileObject
367   activities:(NSPosixFileActivities)_activities
368 {
369     NSRunLoopFileObjectInfo *info = nil;
370     //NSAssert(_activities, @"no activity to watch ?!");
371     info = [[NSRunLoopFileObjectInfo allocWithZone:[self zone]]
372                                      initWithFileObject:_fileObject
373                                      activities:_activities];
374     [self->fileObjects addObject:info];
375     //NSLog(@"file objects now: %@", self->fileObjects);
376     RELEASE(info); info = nil;
377 }
378 - (void)removeFileObject:(id)_fileObject
379 {
380     NSRunLoopFileObjectInfo *info = nil;
381     info = [[NSRunLoopFileObjectInfo allocWithZone:[self zone]]
382                                      initWithFileObject:_fileObject
383                                      activities:0];
384     [self->fileObjects removeObject:info];
385     //NSLog(@"file objects now: %@", self->fileObjects);
386     RELEASE(info); info = nil;
387 }
388
389 - (void)addPosixFileDescriptor:(NSPosixFileDescriptor*)fileDescriptor
390 {
391     [self addFileObject:fileDescriptor
392           activities:[fileDescriptor fileActivity]];
393 }
394
395 - (void)removePosixFileDescriptor:(NSPosixFileDescriptor*)fileDescriptor
396 {
397     [self removeFileObject:fileDescriptor];
398 }
399
400 - (void)addTimer:(NSTimer*)aTimer
401 {
402     [self->timers addObject:[NSRunLoopTimerInfo infoWithTimer:aTimer]];
403 }
404
405 - (void)addOperation:(NSRunLoopActionHolder*)holder
406 {
407     [self->otherOperations addObject:holder];
408     [self->otherOperations sortUsingSelector:@selector(compare:)];
409 }
410
411 - (void)removeOperation:(NSRunLoopActionHolder*)holder
412 {
413     [self->otherOperations removeObject:holder];
414 }
415
416 - (void)performAdditionalOperations
417 {
418     [self->otherOperations makeObjectsPerform:@selector(execute)];
419 }
420
421 - (NSMutableArray *)fileObjects
422 {
423     return self->fileObjects;
424 }
425 - (NSMutableArray *)timers
426 {
427     return self->timers;
428 }
429
430 @end /* NSRunLoopInputManager */
431
432
433 @implementation NSRunLoop
434
435 extern NSRecursiveLock* libFoundationLock;
436
437 /* Class variable */
438 static NSMutableDictionary* runLoopsDictionary = nil;
439 static NSRunLoop* currentRunLoop = nil;
440 static BOOL taskIsMultithreaded = NO;
441
442 /*
443 + (void)initialize
444 {
445     [[NSNotificationCenter defaultCenter]
446         addObserver:self
447         selector:@selector(taskNowMultiThreaded:)
448         name:NSWillBecomeMultiThreadedNotification
449         object:nil];
450 }
451 */
452
453 + (NSRunLoop *)currentRunLoop
454 {
455     if (taskIsMultithreaded) {
456         NSRunLoop* currentLoop;
457         NSThread* currentThread;
458
459         [libFoundationLock lock];
460
461         currentThread = [NSThread currentThread];
462         currentLoop = [runLoopsDictionary objectForKey:currentThread];
463         if (!currentLoop) {
464             currentLoop = AUTORELEASE([self new]);
465             [runLoopsDictionary setObject:currentLoop forKey:currentThread];
466         }
467
468         [libFoundationLock unlock];
469
470         return currentLoop;
471     }
472     else {
473         if (!currentRunLoop)
474             currentRunLoop = [self new];
475         return currentRunLoop;
476     }
477 }
478
479 + (void)taskNowMultiThreaded:(NSNotification *)notification
480 {
481     taskIsMultithreaded = YES;
482     runLoopsDictionary = [NSMutableDictionary new];
483
484     if (currentRunLoop) {
485         NSThread* currentThread = [NSThread currentThread];
486
487         [runLoopsDictionary setObject:currentRunLoop forKey:currentThread];
488         RELEASE(currentRunLoop);
489         currentRunLoop = nil;
490     }
491 }
492
493 - init
494 {
495     self = [super init];
496     self->inputsForMode =
497         [[NSMutableDictionary allocWithZone:[self zone]] init];
498     self->mode = RETAIN(NSDefaultRunLoopMode);
499     return self;
500 }
501
502 - (void)dealloc
503 {
504     RELEASE(self->inputsForMode);
505     RELEASE(self->mode);
506     [super dealloc];
507 }
508
509 - (NSString*)currentMode
510 {
511     return mode;
512 }
513
514 static inline NSRunLoopInputManager*
515 _getInputManager(NSRunLoop *self, NSString *_mode)
516 {
517     NSRunLoopInputManager* inputManager;
518
519     inputManager = [self->inputsForMode objectForKey:_mode];
520     if (inputManager == nil) {
521         inputManager = [[NSRunLoopInputManager alloc] init];
522         [self->inputsForMode setObject:inputManager forKey:_mode];
523         RELEASE(inputManager);
524     }
525     return inputManager;
526 }
527
528 static int compare_fire_dates(id timer1, id timer2, void* userData)
529 {
530     return [[timer1 fireDate] compare:[timer2 fireDate]];
531 }
532
533 - (NSDate*)limitDateForMode:(NSString*)aMode
534 {
535     NSString       *format =
536         @"%s: During NSTimer:-fire, caught exception %@ with reason %@ ";
537     NSMutableArray *timers = [[inputsForMode objectForKey:aMode] timers];
538     volatile int   i, count;
539     NSMutableArray *copyOfTimers;
540
541     ASSIGN(mode, aMode);
542
543     /* Remove invalid timers */
544     for(count = [timers count], i = count - 1; i >= 0; i--) {
545         if(![[[timers objectAtIndex:i] timer] isValid]) {
546             [timers removeObjectAtIndex:i];
547             count--;
548         }
549     }
550     
551     /* Currently only timers have limit dates associated with them */
552     if(!count)
553         return nil;
554
555     copyOfTimers = [timers mutableCopy];
556
557     /* Sort the timers based on their fire date */
558     [copyOfTimers sortUsingFunction:compare_fire_dates context:NULL];
559
560     /* Fire all the timers with their fire date expired */
561     for(i = 0; i < count; i++) {
562         NSRunLoopTimerInfo* timerInfo = [copyOfTimers objectAtIndex:i];
563         NSDate* fireDate = [timerInfo fireDate];
564         NSDate* currentDate = [NSDate date];
565
566         if([fireDate earlierDate:currentDate] == fireDate
567            || [fireDate isEqualToDate:currentDate]) {
568             NSTimer* timer = [timerInfo timer];
569             NS_DURING {
570               [timer fire];
571             }
572             NS_HANDLER {
573               NSLog(format, "NSRunLoop(-limitDateForMode:)",
574                     [localException name], [localException reason]);
575             }
576             NS_ENDHANDLER;
577             
578             if(![timer repeats])
579                 [timer invalidate];
580         }
581     }
582
583     RELEASE(copyOfTimers);
584
585     /* Recompute the fire dates for this cycle */
586     [timers makeObjectsPerform:@selector(recomputeFireDate)];
587
588     /* Sort the timers based on their fire date */
589     [timers sortUsingFunction:compare_fire_dates context:NULL];
590
591     return [timers count] > 0
592         ? [[timers objectAtIndex:0] fireDate] : (NSDate *)nil;
593 }
594
595 - (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)aMode
596 {
597     [_getInputManager(self, aMode) addTimer:aTimer];
598 }
599
600 - (BOOL)runMode:(NSString *)aMode beforeDate:(NSDate *)limitDate
601 {
602     id      inputManager, fileObjects;
603     NSArray *timers;
604     NSDate  *date;
605     
606     /* Retain the limitDate so it doesn't get released by limitDateForMode:
607         if it fires a timer that has as fireDate the limitDate.
608         (bug report from Benhur Stein <Benhur-de-Oliveira.Stein@imag.fr>)
609       */
610     (void)RETAIN(limitDate);
611
612     inputManager = [self->inputsForMode objectForKey:aMode];
613     timers       = [inputManager timers];
614     fileObjects  = [inputManager fileObjects];
615
616     if (([timers count] != 0) || ([fileObjects count] != 0)) {
617         CREATE_AUTORELEASE_POOL(pool);
618
619         date = [self limitDateForMode:aMode];
620         date = date ? [date earlierDate:limitDate] : limitDate;
621         [self acceptInputForMode:aMode beforeDate:date];
622         RELEASE(pool);
623         RELEASE(limitDate);
624         return YES;
625     }
626
627     RELEASE(limitDate);
628     return NO;
629 }
630
631 /*  Runs the loop until limitDate or until the earliest limit date for input
632     sources in the specified mode. */
633 - (void)acceptInputForMode:(NSString *)aMode beforeDate:(NSDate *)limitDate
634 {
635     id              inputManager, fileObjects;
636     struct timeval  tp = { 0, 0 };
637     struct timeval* timeout = NULL;
638     NSTimeInterval  delay = 0;
639     fd_set          readSet, writeSet, exceptionsSet;
640     volatile int    i, r, count;
641
642     ASSIGN(self->mode, aMode);
643
644     if(limitDate == nil) { // delay = 0
645         limitDate = [NSDate distantFuture];
646     }
647     else {
648         delay = [limitDate timeIntervalSinceNow];
649             /* delay > 0 means a future date */
650
651         /* If limitDate is in the past return */
652         if(delay < 0)
653             return;
654     }
655
656     inputManager = [inputsForMode objectForKey:aMode];
657     fileObjects  = [inputManager fileObjects];
658     
659     /* Compute the timeout for select */
660     if([limitDate isEqual:[NSDate distantFuture]])
661         timeout = NULL;
662     else {
663         tp.tv_sec = delay;
664         tp.tv_usec = (delay - (NSTimeInterval)tp.tv_sec) * 1000000.0;
665         timeout = &tp;
666     }
667     
668     [NSNotificationQueue runLoopASAP];
669     
670     ASSIGN(self->mode, aMode);
671     
672     FD_ZERO(&readSet);
673     FD_ZERO(&writeSet);
674     FD_ZERO(&exceptionsSet);
675
676     do {
677         count = [fileObjects count];
678         for (i = 0; i < count; i++) {
679             NSRunLoopFileObjectInfo *info = [fileObjects objectAtIndex:i];
680             NSPosixFileActivities   fileActivity;
681             int                     fd;
682
683             if (![info isAlive])
684                 continue;
685
686             fileActivity = [info watchedActivities];
687             fd           = [info fileDescriptor];
688             
689             if (fd >= 0) {
690 #if !defined(__MINGW32__) /* on Windows descriptors can be BIG */
691                 if (fd >= FD_SETSIZE) {
692                     NSLog(@"%s: fd %i of %@ exceeds select size %i",
693                           "NSRunLoop(-acceptInputForMode:beforeDate:)",
694                           fd, info, FD_SETSIZE);
695                     continue;
696                 }
697 #endif
698
699                 //NSLog(@"registering activity %s for fd %i ..",
700                 //      activityDesc[fileActivity], fd);
701
702                 if (fileActivity & NSPosixReadableActivity)
703                     FD_SET(fd, &readSet);
704                 if (fileActivity & NSPosixWritableActivity)
705                     FD_SET(fd, &writeSet);
706                 if (fileActivity & NSPosixExceptionalActivity)
707                     FD_SET(fd, &exceptionsSet);
708             }
709         }
710         
711         /* ???: errno = 0; What is this good for ? */
712         r = select(FD_SETSIZE, &readSet, &writeSet, &exceptionsSet, timeout);
713         if (r == -1) {
714             if (errno == EINTR) {
715                 /* Interrupt occured; break the loop to give a chance to
716                    UnixSignalHandler to handle the signals. */
717                 //printf("%s: GOT EINTR ...\n", __PRETTY_FUNCTION__);
718                 errno = 0;
719                 break;
720             }
721             else {
722                 NSLog(@"%s: select() error: '%s'",
723                       "NSRunLoop(-acceptInputForMode:beforeDate:)",
724                       strerror (errno));
725                 break;
726             }
727             errno = 0;
728         }
729     } while (r == -1);
730
731     if (r > 0) {
732         id fileObjectsCopy;
733
734         *(&fileObjectsCopy) = nil;
735
736         {
737             // made copy, so that modifications in the delegate don't
738             // alter the loop
739             fileObjectsCopy = [fileObjects copyWithZone:[self zone]];
740             count           = [fileObjectsCopy count];
741             
742             for (i = 0; (i < count) && (r > 0); i++) {
743                 NSRunLoopFileObjectInfo *info;
744                 NSPosixFileActivities   activity = 0;
745                 int fd;
746
747                 info = [fileObjectsCopy objectAtIndex:i];
748                 fd   = [info fileDescriptor];
749
750                 if (fd >= 0) {
751                     //NSLog(@"checking activity for %i info %@ ..", fd, info);
752                 
753                     if (FD_ISSET(fd, &readSet)) {
754                         activity |= NSPosixReadableActivity;
755                         r--;
756                     }
757                     if (FD_ISSET(fd, &writeSet)) {
758                         activity |= NSPosixWritableActivity;
759                         r--;
760                     }
761                     if (FD_ISSET(fd, &exceptionsSet)) {
762                         activity |= NSPosixExceptionalActivity;
763                         r--;
764                     }
765
766                     if (activity != 0)
767                         [info activity:activity onDescriptor:fd];
768                 }
769             }
770 #if 0
771             if (r > 0) {
772                 NSLog(@"WARNING: did not resolve all activities (%i) ..", r);
773             }
774 #endif
775         }
776
777         RELEASE(fileObjectsCopy); fileObjectsCopy = nil;
778     }
779     
780     [inputManager performAdditionalOperations];
781     [NSNotificationQueue runLoopASAP];
782     [inputManager performAdditionalOperations];
783 }
784
785 - (void)runUntilDate:(NSDate *)limitDate
786 {
787     BOOL shouldContinue = YES;
788     
789     if (limitDate == nil)
790         limitDate = [NSDate distantFuture];
791     else {
792         /* If limitDate is in the past return */
793         if ([limitDate timeIntervalSinceNow] < 0)
794             return;
795     }
796
797     while (shouldContinue) {
798         CREATE_AUTORELEASE_POOL(pool);
799         
800         if ([limitDate laterDate:[NSDate date]] == limitDate) {
801             if ([self runMode:NSDefaultRunLoopMode beforeDate:limitDate] == NO)
802                 shouldContinue = NO;
803         }
804         else
805             shouldContinue = NO;
806         RELEASE(pool);
807     }
808 }
809
810 - (void)run
811 {
812     [self runUntilDate:[NSDate distantFuture]];
813 }
814
815 - (void)performSelector:(SEL)aSelector
816   target:(id)target
817   argument:(id)anArgument
818   order:(unsigned)order
819   modes:(NSArray*)modes
820 {
821     id holder = [NSRunLoopActionHolder objectWithTarget:target
822                                         argument:anArgument
823                                         selector:aSelector
824                                         order:order];
825     int i, count = [modes count];
826
827     for (i = 0; i < count; i++)
828         [[inputsForMode objectForKey:[modes objectAtIndex:i]]
829             addOperation:holder];
830 }
831
832 - (void)cancelPerformSelector:(SEL)aSelector
833   target:(id)target
834   argument:(id)anArgument
835 {
836     id holder = [NSRunLoopActionHolder objectWithTarget:target
837                                         argument:anArgument
838                                         selector:aSelector
839                                         order:0];
840     id enumerator = [inputsForMode keyEnumerator];
841     id aMode;
842
843     while ((aMode = [enumerator nextObject]))
844         [[inputsForMode objectForKey:aMode] removeOperation:holder];
845 }
846
847 /* Monitoring file descriptors */
848
849 - (void)addPosixFileDescriptor:(NSPosixFileDescriptor *)fileDescriptor
850   forMode:(NSString *)aMode
851 {
852     [_getInputManager(self, aMode) addPosixFileDescriptor:fileDescriptor];
853 }
854
855 - (void)removePosixFileDescriptor:(NSPosixFileDescriptor *)fileDescriptor
856   forMode:(NSString *)aMode
857 {
858     [_getInputManager(self, aMode) removePosixFileDescriptor:fileDescriptor];
859 }
860
861 /* Monitoring file objects */
862
863 - (void)addFileObject:(id)_fileObject
864   activities:(unsigned int)_activities /* NSPosixFileActivities */
865   forMode:(NSString *)_mode
866 {
867     [_getInputManager(self, _mode) addFileObject:_fileObject
868                                    activities:_activities];
869 }
870
871 - (void)removeFileObject:(id)_fileObject
872   forMode:(NSString *)_mode
873 {
874     [_getInputManager(self, _mode) removeFileObject:_fileObject];
875 }
876
877 /* Ports */
878
879 - (void)addPort:(NSPort *)_port forMode:(NSString *)_mode
880 {
881     [self notImplemented:_cmd];
882 }
883
884 - (void)removePort:(NSPort *)_port forMode:(NSString *)_mode
885 {
886     [self notImplemented:_cmd];
887 }
888
889 /* Server operation */
890
891 - (void)configureAsServer
892 {
893     /* What is special about a server ?, maybe register as service in Win32 */
894 }
895
896 @end /* NSRunLoop */
897 /*
898   Local Variables:
899   c-basic-offset: 4
900   tab-width: 8
901   End:
902 */