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