2
Copyright (C) 2000-2005 SKYRIX Software AG
4
This file is part of SOPE.
6
SOPE is free software; you can redistribute it and/or modify it under
7
the terms of the GNU Lesser General Public License as published by the
8
Free Software Foundation; either version 2, or (at your option) any
11
SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12
WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14
License for more details.
16
You should have received a copy of the GNU Lesser General Public
17
License along with SOPE; see the file COPYING. If not, write to the
18
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22
#include "WORunLoop.h"
26
#if !LIB_FOUNDATION_LIBRARY && !APPLE_Foundation_LIBRARY && !NeXT_Foundation_LIBRARY
28
#ifndef CREATE_AUTORELEASE_POOL
29
# define CREATE_AUTORELEASE_POOL(pool) \
30
id pool = [[NSAutoreleasePool alloc] init]
33
#include <sys/types.h>
34
#include <sys/errno.h>
37
#include <sys/time.h> /* for struct timeval */
42
#include <sys/select.h>
44
#include "WORunLoop.h"
45
#import <Foundation/Foundation.h>
47
#if NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY
48
# include <FoundationExt/objc-runtime.h>
50
# include <extensions/objc-runtime.h>
54
#warning breaks AppKit, should *extend* NSRunLoop on MacOSX
55
@implementation NSRunLoop(Override)
56
+ (NSRunLoop *)currentRunLoop {
57
return [WORunLoop currentRunLoop];
64
NSPosixNoActivity = 0,
65
NSPosixReadableActivity = 1,
66
NSPosixWritableActivity = 2,
67
NSPosixExceptionalActivity = 4
68
} NSPosixFileActivities;
71
NSString* NSFileObjectBecameActiveNotificationName =
72
@"NSFileObjectBecameActiveNotificationName";
74
static char *activityDesc[8] = {
85
@interface WORunLoopFileObjectInfo : NSObject
88
NSPosixFileActivities watchedActivities;
92
- (id)initWithFileObject:(id)_fileObject
93
activities:(NSPosixFileActivities)_activities;
96
- (int)fileDescriptor;
97
- (NSPosixFileActivities)watchedActivities;
99
- (void)activity:(NSPosixFileActivities)_activity onDescriptor:(int)_fd;
103
@implementation WORunLoopFileObjectInfo
105
- (id)initWithFileObject:(id)_fileObject
106
activities:(NSPosixFileActivities)_activities
108
self->fileObject = RETAIN(_fileObject);
109
self->watchedActivities = _activities;
110
self->canCheckAlive = [_fileObject respondsToSelector:@selector(isAlive)];
115
NSLog(@"ERROR: do not use init with WORunLoopFileObjectInfo ..");
122
RELEASE(self->fileObject); self->fileObject = nil;
126
- (BOOL)isEqual:(WORunLoopFileObjectInfo*)otherInfo
128
return [self->fileObject isEqual:otherInfo->fileObject];
132
return (self->canCheckAlive) ? [self->fileObject isAlive] : YES;
134
- (int)fileDescriptor
136
return [self->fileObject fileDescriptor];
139
- (NSPosixFileActivities)watchedActivities
141
return self->watchedActivities;
144
- (void)activity:(NSPosixFileActivities)_activity onDescriptor:(int)_fd
146
//NSLog(@"FileObject %@ was active ..", self->fileObject);
148
[[NSNotificationCenter defaultCenter]
149
postNotificationName:
150
NSFileObjectBecameActiveNotificationName
151
object:self->fileObject];
154
- (NSString *)description
156
return [NSString stringWithFormat:
157
@"<%@[0x%08X]: object=%@ actitivity=%s>",
158
NSStringFromClass([self class]), self,
160
activityDesc[self->watchedActivities]
166
@interface WORunLoopTimerInfo : NSObject
172
+ (WORunLoopTimerInfo*)infoWithTimer:(NSTimer*)timer;
173
- (void)recomputeFireDate;
174
- (NSComparisonResult)compare:(WORunLoopTimerInfo*)anObject;
179
@implementation WORunLoopTimerInfo
181
+ (WORunLoopTimerInfo*)infoWithTimer:(NSTimer*)aTimer
183
WORunLoopTimerInfo* info = [self new];
185
info->timer = RETAIN(aTimer);
186
info->fireDate = RETAIN([aTimer fireDate]);
187
return AUTORELEASE(info);
197
- (void)recomputeFireDate
199
if ([timer isValid]) {
200
id tmp = [timer fireDate];
201
ASSIGN(fireDate, tmp);
205
- (NSComparisonResult)compare:(WORunLoopTimerInfo*)anObject
207
return [fireDate compare:anObject->fireDate];
210
- (NSTimer*)timer { return timer; }
211
- (NSDate*)fireDate { return fireDate; }
215
@interface WORunLoopActionHolder : NSObject
222
+ objectWithTarget:(id)target
223
argument:(id)argument
226
- (BOOL)isEqual:(id)anotherHolder;
230
@implementation WORunLoopActionHolder
232
+ objectWithTarget:(id)_target
233
argument:(id)_argument
234
selector:(SEL)_action
237
WORunLoopActionHolder* holder = AUTORELEASE([self alloc]);
239
holder->target = RETAIN(_target);
240
holder->argument = RETAIN(_argument);
241
holder->action = _action;
242
holder->order = _order;
249
return [(NSObject*)target hash];
252
- (BOOL)isEqual:(WORunLoopActionHolder*)anotherHolder
254
return [target isEqual:anotherHolder->target]
255
&& [argument isEqual:anotherHolder->argument]
256
&& SEL_EQ(action, anotherHolder->action);
261
[target performSelector:action withObject:argument];
264
- (NSComparisonResult)compare:(WORunLoopActionHolder*)anotherHolder
266
return order - anotherHolder->order;
272
@interface WORunLoopInputManager : NSObject
274
NSMutableArray* fileObjects;
275
NSMutableArray* timers;
276
NSMutableArray* otherOperations;
279
- (void)addFileObject:(id)_fileObject
280
activities:(NSPosixFileActivities)_activities;
281
- (void)removeFileObject:(id)_fileObject;
283
- (void)addTimer:(NSTimer*)aTimer;
285
- (NSMutableArray*)fileObjects;
286
- (NSMutableArray*)timers;
288
- (void)addOperation:(WORunLoopActionHolder*)holder;
289
- (void)removeOperation:(WORunLoopActionHolder*)holder;
290
- (void)performAdditionalOperations;
294
@implementation WORunLoopInputManager
298
fileObjects = [NSMutableArray new];
299
timers = [NSMutableArray new];
300
otherOperations = [NSMutableArray new];
306
RELEASE(fileObjects);
308
RELEASE(otherOperations);
312
- (void)addFileObject:(id)_fileObject
313
activities:(NSPosixFileActivities)_activities
315
WORunLoopFileObjectInfo *info = nil;
316
//NSAssert(_activities, @"no activity to watch ?!");
317
info = [[WORunLoopFileObjectInfo allocWithZone:[self zone]]
318
initWithFileObject:_fileObject
319
activities:_activities];
320
[self->fileObjects addObject:info];
321
//NSLog(@"file objects now: %@", self->fileObjects);
322
RELEASE(info); info = nil;
324
- (void)removeFileObject:(id)_fileObject
326
WORunLoopFileObjectInfo *info = nil;
327
info = [[WORunLoopFileObjectInfo allocWithZone:[self zone]]
328
initWithFileObject:_fileObject
330
[self->fileObjects removeObject:info];
331
//NSLog(@"file objects now: %@", self->fileObjects);
332
RELEASE(info); info = nil;
335
- (void)addTimer:(NSTimer*)aTimer
337
[timers addObject:[WORunLoopTimerInfo infoWithTimer:aTimer]];
340
- (void)addOperation:(WORunLoopActionHolder*)holder
342
[otherOperations addObject:holder];
343
[otherOperations sortUsingSelector:@selector(compare:)];
346
- (void)removeOperation:(WORunLoopActionHolder*)holder
348
[otherOperations removeObject:holder];
351
- (void)performAdditionalOperations
353
[otherOperations makeObjectsPerformSelector:@selector(execute)];
356
- (NSMutableArray*)fileObjects { return fileObjects; }
357
- (NSMutableArray*)timers { return timers; }
359
@end /* WORunLoopInputManager */
361
@implementation WORunLoop
364
static WORunLoop *currentRunLoop = nil;
365
static BOOL taskIsMultithreaded = NO;
367
+ (void)error:(id)_o {
372
+ (NSRunLoop *)currentRunLoop
374
if (taskIsMultithreaded) {
375
NSLog(@"WORunLoop does not work multithreaded, exit ..");
380
currentRunLoop = [[self alloc] init];
381
return currentRunLoop;
386
self->inputsForMode = [[NSMutableDictionary allocWithZone:[self zone]] init];
387
self->mode = RETAIN(NSDefaultRunLoopMode);
392
RELEASE(self->inputsForMode);
397
- (NSString *)currentMode {
401
static inline WORunLoopInputManager*
402
_getInputManager(WORunLoop *self, NSString *_mode)
404
WORunLoopInputManager* inputManager;
406
inputManager = [self->inputsForMode objectForKey:_mode];
407
if (inputManager == nil) {
408
inputManager = [WORunLoopInputManager new];
409
[self->inputsForMode setObject:inputManager forKey:_mode];
410
RELEASE(inputManager);
415
static int compare_fire_dates(id timer1, id timer2, void* userData)
417
return [[timer1 fireDate] compare:[timer2 fireDate]];
420
- (NSDate*)limitDateForMode:(NSString*)aMode
422
NSString *format = @"%s: Caught exception %@ with reason %@ ";
423
NSMutableArray *timers = [[inputsForMode objectForKey:aMode] timers];
424
volatile int i, count;
425
NSMutableArray *copyOfTimers;
429
/* Remove invalid timers */
430
for(count = [timers count], i = count - 1; i >= 0; i--)
431
if(![[[timers objectAtIndex:i] timer] isValid]) {
432
[timers removeObjectAtIndex:i];
436
/* Currently only timers have limit dates associated with them */
440
copyOfTimers = [timers mutableCopy];
442
/* Sort the timers based on their fire date */
443
[copyOfTimers sortUsingFunction:compare_fire_dates context:NULL];
445
/* Fire all the timers with their fire date expired */
446
for(i = 0; i < count; i++) {
447
WORunLoopTimerInfo* timerInfo = [copyOfTimers objectAtIndex:i];
448
NSDate* fireDate = [timerInfo fireDate];
449
NSDate* currentDate = [NSDate date];
451
if([fireDate earlierDate:currentDate] == fireDate
452
|| [fireDate isEqualToDate:currentDate]) {
453
NSTimer* timer = [timerInfo timer];
457
NSLog(format, __PRETTY_FUNCTION__,
458
[localException name], [localException reason]);
462
#warning no repeated timers !
469
RELEASE(copyOfTimers);
471
/* Recompute the fire dates for this cycle */
472
[timers makeObjectsPerformSelector:@selector(recomputeFireDate)];
474
/* Sort the timers based on their fire date */
475
[timers sortUsingFunction:compare_fire_dates context:NULL];
477
return [timers count] ? [[timers objectAtIndex:0] fireDate] : nil;
480
- (void)addTimer:(NSTimer*)aTimer
481
forMode:(NSString*)aMode
483
[_getInputManager(self, aMode) addTimer:aTimer];
486
- (BOOL)runMode:(NSString*)aMode
487
beforeDate:(NSDate*)limitDate
489
id inputManager, fileObjects;
493
/* Retain the limitDate so it doesn't get released by limitDateForMode:
494
if it fires a timer that has as fireDate the limitDate.
495
(bug report from Benhur Stein <Benhur-de-Oliveira.Stein@imag.fr>)
497
(void)RETAIN(limitDate);
499
inputManager = [inputsForMode objectForKey:aMode];
500
timers = [inputManager timers];
501
fileObjects = [inputManager fileObjects];
503
if (([timers count] != 0) || ([fileObjects count] != 0)) {
504
CREATE_AUTORELEASE_POOL(pool);
506
date = [self limitDateForMode:aMode];
507
date = date ? [date earlierDate:limitDate] : limitDate;
508
[self acceptInputForMode:aMode beforeDate:date];
518
/* Runs the loop until limitDate or until the earliest limit date for input
519
sources in the specified mode. */
520
- (void)acceptInputForMode:(NSString*)aMode
521
beforeDate:(NSDate*)limitDate
523
id inputManager, fileObjects;
524
struct timeval tp = { 0, 0 };
525
struct timeval* timeout = NULL;
526
NSTimeInterval delay = 0;
527
fd_set readSet, writeSet, exceptionsSet;
528
volatile int i, r, count;
532
if(limitDate == nil) // delay = 0
533
limitDate = [NSDate distantFuture];
535
delay = [limitDate timeIntervalSinceNow];
536
/* delay > 0 means a future date */
538
/* If limitDate is in the past return */
543
inputManager = [inputsForMode objectForKey:aMode];
544
fileObjects = [inputManager fileObjects];
546
/* Compute the timeout for select */
547
if([limitDate isEqual:[NSDate distantFuture]])
551
tp.tv_usec = (delay - (NSTimeInterval)tp.tv_sec) * 1000000.0;
559
FD_ZERO(&exceptionsSet);
562
count = [fileObjects count];
563
for (i = 0; i < count; i++) {
564
WORunLoopFileObjectInfo *info;
565
NSPosixFileActivities fileActivity;
568
info = [fileObjects objectAtIndex:i];
572
fileActivity = [info watchedActivities];
573
fd = [info fileDescriptor];
576
#if !defined(__MINGW32__) /* on Windows descriptors can be BIG */
577
if (fd >= FD_SETSIZE) {
578
NSLog(@"%s: fd %i of %@ exceeds select size %i",
580
fd, info, FD_SETSIZE);
583
#endif /* !defined(__MINGW32__) */
585
//NSLog(@"registering activity %s for fd %i ..",
586
// activityDesc[fileActivity], fd);
588
if (fileActivity & NSPosixReadableActivity)
589
FD_SET(fd, &readSet);
590
if (fileActivity & NSPosixWritableActivity)
591
FD_SET(fd, &writeSet);
592
if (fileActivity & NSPosixExceptionalActivity)
593
FD_SET(fd, &exceptionsSet);
597
// ???: errno = 0; What is this good for ?
598
r = select(FD_SETSIZE, &readSet, &writeSet, &exceptionsSet, timeout);
600
if (errno == EINTR) {
601
/* Interrupt occured; break the loop to give a chance to
602
UnixSignalHandler to handle the signals. */
607
NSLog(@"%s: select() error: '%s'",
608
__PRETTY_FUNCTION__, strerror (errno));
617
NSString* format = @"%s: Caught exception %@ with reason %@ ";
619
*(&fileObjectsCopy) = nil;
622
// made copy, so that modifications in the delegate don't
624
fileObjectsCopy = [fileObjects copyWithZone:[self zone]];
625
count = [fileObjectsCopy count];
627
for (i = 0; (i < count) && (r > 0); i++) {
628
WORunLoopFileObjectInfo *info;
629
NSPosixFileActivities activity = 0;
632
info = [fileObjectsCopy objectAtIndex:i];
633
fd = [info fileDescriptor];
636
//NSLog(@"checking activity for %i info %@ ..", fd, info);
638
if (FD_ISSET(fd, &readSet)) {
639
activity |= NSPosixReadableActivity;
642
if (FD_ISSET(fd, &writeSet)) {
643
activity |= NSPosixWritableActivity;
646
if (FD_ISSET(fd, &exceptionsSet)) {
647
activity |= NSPosixExceptionalActivity;
652
[info activity:activity onDescriptor:fd];
656
NSLog(@"WARNING: could not resolve all activities (%i) ..",
661
NSLog(format, __PRETTY_FUNCTION__,
662
[localException name], [localException reason]);
666
RELEASE(fileObjectsCopy); fileObjectsCopy = nil;
669
[inputManager performAdditionalOperations];
670
#if !NeXT_Foundation_LIBRARY
671
[NSNotificationQueue runLoopASAP];
675
- (void)runUntilDate:(NSDate*)limitDate
677
BOOL shouldContinue = YES;
680
limitDate = [NSDate distantFuture];
682
/* If limitDate is in the past return */
683
if([limitDate timeIntervalSinceNow] < 0)
687
while (shouldContinue) {
688
CREATE_AUTORELEASE_POOL(pool);
690
if ([limitDate laterDate:[NSDate date]] == limitDate) {
691
if([self runMode:NSDefaultRunLoopMode beforeDate:limitDate] == NO)
702
[self runUntilDate:[NSDate distantFuture]];
705
- (void)performSelector:(SEL)aSelector
707
argument:(id)anArgument
708
order:(unsigned)order
709
modes:(NSArray*)modes
711
id holder = [WORunLoopActionHolder objectWithTarget:target
715
int i, count = [modes count];
717
for (i = 0; i < count; i++)
718
[[inputsForMode objectForKey:[modes objectAtIndex:i]]
719
addOperation:holder];
722
- (void)cancelPerformSelector:(SEL)aSelector
724
argument:(id)anArgument
726
id holder = [WORunLoopActionHolder objectWithTarget:target
730
id enumerator = [inputsForMode keyEnumerator];
733
while ((aMode = [enumerator nextObject]))
734
[[inputsForMode objectForKey:aMode] removeOperation:holder];
737
/* Monitoring file objects */
739
- (void)addFileObject:(id)_fileObject
740
activities:(NSPosixFileActivities)_activities
741
forMode:(NSString *)_mode
743
[_getInputManager(self, _mode) addFileObject:_fileObject
744
activities:_activities];
747
- (void)removeFileObject:(id)_fileObject
748
forMode:(NSString *)_mode
750
[_getInputManager(self, _mode) removeFileObject:_fileObject];
755
#endif /* !LIB_FOUNDATION_LIBRARY */