1/* 2 Copyright (C) 2000-2005 SKYRIX Software AG 3 4 This file is part of SOPE. 5 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 9 later version. 10 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. 15 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 19 02111-1307, USA. 20*/ 21 22#include "WORunLoop.h" 23#include "common.h" 24 25// TODO: not used anymore? 26 27#if 0 28#if !LIB_FOUNDATION_LIBRARY && !APPLE_Foundation_LIBRARY && !NeXT_Foundation_LIBRARY 29 30#ifndef CREATE_AUTORELEASE_POOL 31# define CREATE_AUTORELEASE_POOL(pool) \ 32 id pool = [[NSAutoreleasePool alloc] init] 33#endif 34 35#include <sys/types.h> 36#include <sys/errno.h> 37#include <errno.h> 38 39#include <sys/time.h> /* for struct timeval */ 40#include <string.h> 41#include <memory.h> 42#include <libc.h> 43#include <unistd.h> 44#include <sys/select.h> 45 46#include "WORunLoop.h" 47#import <Foundation/Foundation.h> 48 49#if NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY 50# include <FoundationExt/objc-runtime.h> 51#else 52# include <extensions/objc-runtime.h> 53#endif 54 55#if 0 56#warning breaks AppKit, should *extend* NSRunLoop on MacOSX 57@implementation NSRunLoop(Override) 58+ (NSRunLoop *)currentRunLoop { 59 return [WORunLoop currentRunLoop]; 60} 61@end 62#endif 63 64#if 0 65typedef enum { 66 NSPosixNoActivity = 0, 67 NSPosixReadableActivity = 1, 68 NSPosixWritableActivity = 2, 69 NSPosixExceptionalActivity = 4 70} NSPosixFileActivities; 71#endif 72 73NSString* NSFileObjectBecameActiveNotificationName = 74 @"NSFileObjectBecameActiveNotificationName"; 75 76static char *activityDesc[8] = { 77 "---", // 0 78 "--R", // 1 79 "-W-", // 2 80 "-WR", // 3 81 "E--", // 4 82 "E-R", // 5 83 "EW-", // 6 84 "EWR" // 7 85}; 86 87@interface WORunLoopFileObjectInfo : NSObject 88{ 89 id fileObject; 90 NSPosixFileActivities watchedActivities; 91 BOOL canCheckAlive; 92} 93 94- (id)initWithFileObject:(id)_fileObject 95 activities:(NSPosixFileActivities)_activities; 96 97- (BOOL)isAlive; 98- (int)fileDescriptor; 99- (NSPosixFileActivities)watchedActivities; 100 101- (void)activity:(NSPosixFileActivities)_activity onDescriptor:(int)_fd; 102 103@end 104 105@implementation WORunLoopFileObjectInfo 106 107- (id)initWithFileObject:(id)_fileObject 108 activities:(NSPosixFileActivities)_activities 109{ 110 self->fileObject = RETAIN(_fileObject); 111 self->watchedActivities = _activities; 112 self->canCheckAlive = [_fileObject respondsToSelector:@selector(isAlive)]; 113 return self; 114} 115- (id)init 116{ 117 [self errorWithFormat:@"do not use init with WORunLoopFileObjectInfo .."]; 118 AUTORELEASE(self); 119 return nil; 120} 121 122- (void)dealloc 123{ 124 RELEASE(self->fileObject); self->fileObject = nil; 125 [super dealloc]; 126} 127 128- (BOOL)isEqual:(WORunLoopFileObjectInfo*)otherInfo 129{ 130 return [self->fileObject isEqual:otherInfo->fileObject]; 131} 132 133- (BOOL)isAlive { 134 return (self->canCheckAlive) ? [self->fileObject isAlive] : YES; 135} 136- (int)fileDescriptor 137{ 138 return [self->fileObject fileDescriptor]; 139} 140 141- (NSPosixFileActivities)watchedActivities 142{ 143 return self->watchedActivities; 144} 145 146- (void)activity:(NSPosixFileActivities)_activity onDescriptor:(int)_fd 147{ 148 //NSLog(@"FileObject %@ was active ..", self->fileObject); 149 150 [[NSNotificationCenter defaultCenter] 151 postNotificationName: 152 NSFileObjectBecameActiveNotificationName 153 object:self->fileObject]; 154} 155 156- (NSString *)description 157{ 158 return [NSString stringWithFormat: 159 @"<%@[0x%p]: object=%@ actitivity=%s>", 160 NSStringFromClass([self class]), self, 161 self->fileObject, 162 activityDesc[self->watchedActivities] 163 ]; 164} 165 166@end 167 168@interface WORunLoopTimerInfo : NSObject 169{ 170 NSTimer* timer; 171 NSDate* fireDate; 172} 173 174+ (WORunLoopTimerInfo*)infoWithTimer:(NSTimer*)timer; 175- (void)recomputeFireDate; 176- (NSComparisonResult)compare:(WORunLoopTimerInfo*)anObject; 177- (NSTimer*)timer; 178- (NSDate*)fireDate; 179@end 180 181@implementation WORunLoopTimerInfo 182 183+ (WORunLoopTimerInfo*)infoWithTimer:(NSTimer*)aTimer 184{ 185 WORunLoopTimerInfo* info = [self new]; 186 187 info->timer = RETAIN(aTimer); 188 info->fireDate = RETAIN([aTimer fireDate]); 189 return AUTORELEASE(info); 190} 191 192- (void)dealloc 193{ 194 RELEASE(timer); 195 RELEASE(fireDate); 196 [super dealloc]; 197} 198 199- (void)recomputeFireDate 200{ 201 if ([timer isValid]) { 202 id tmp = [timer fireDate]; 203 ASSIGN(fireDate, tmp); 204 } 205} 206 207- (NSComparisonResult)compare:(WORunLoopTimerInfo*)anObject 208{ 209 return [fireDate compare:anObject->fireDate]; 210} 211 212- (NSTimer*)timer { return timer; } 213- (NSDate*)fireDate { return fireDate; } 214 215@end 216 217@interface WORunLoopActionHolder : NSObject 218{ 219 id target; 220 id argument; 221 SEL action; 222 int order; 223} 224+ objectWithTarget:(id)target 225 argument:(id)argument 226 selector:(SEL)action 227 order:(int)order; 228- (BOOL)isEqual:(id)anotherHolder; 229- (void)execute; 230@end 231 232@implementation WORunLoopActionHolder 233 234+ objectWithTarget:(id)_target 235 argument:(id)_argument 236 selector:(SEL)_action 237 order:(int)_order 238{ 239 WORunLoopActionHolder* holder = AUTORELEASE([self alloc]); 240 241 holder->target = RETAIN(_target); 242 holder->argument = RETAIN(_argument); 243 holder->action = _action; 244 holder->order = _order; 245 246 return holder; 247} 248 249- (unsigned)hash 250{ 251 return [(NSObject*)target hash]; 252} 253 254- (BOOL)isEqual:(WORunLoopActionHolder*)anotherHolder 255{ 256 return [target isEqual:anotherHolder->target] 257 && [argument isEqual:anotherHolder->argument] 258 && sel_isEqual(action, anotherHolder->action); 259} 260 261- (void)execute 262{ 263 [target performSelector:action withObject:argument]; 264} 265 266- (NSComparisonResult)compare:(WORunLoopActionHolder*)anotherHolder 267{ 268 return order - anotherHolder->order; 269} 270 271@end 272 273 274@interface WORunLoopInputManager : NSObject 275{ 276 NSMutableArray* fileObjects; 277 NSMutableArray* timers; 278 NSMutableArray* otherOperations; 279} 280 281- (void)addFileObject:(id)_fileObject 282 activities:(NSPosixFileActivities)_activities; 283- (void)removeFileObject:(id)_fileObject; 284 285- (void)addTimer:(NSTimer*)aTimer; 286 287- (NSMutableArray*)fileObjects; 288- (NSMutableArray*)timers; 289 290- (void)addOperation:(WORunLoopActionHolder*)holder; 291- (void)removeOperation:(WORunLoopActionHolder*)holder; 292- (void)performAdditionalOperations; 293@end 294 295 296@implementation WORunLoopInputManager 297 298- init 299{ 300 fileObjects = [NSMutableArray new]; 301 timers = [NSMutableArray new]; 302 otherOperations = [NSMutableArray new]; 303 return [super init]; 304} 305 306- (void)dealloc 307{ 308 RELEASE(fileObjects); 309 RELEASE(timers); 310 RELEASE(otherOperations); 311 [super dealloc]; 312} 313 314- (void)addFileObject:(id)_fileObject 315 activities:(NSPosixFileActivities)_activities 316{ 317 WORunLoopFileObjectInfo *info = nil; 318 //NSAssert(_activities, @"no activity to watch ?!"); 319 info = [[WORunLoopFileObjectInfo allocWithZone:[self zone]] 320 initWithFileObject:_fileObject 321 activities:_activities]; 322 [self->fileObjects addObject:info]; 323 //NSLog(@"file objects now: %@", self->fileObjects); 324 RELEASE(info); info = nil; 325} 326- (void)removeFileObject:(id)_fileObject 327{ 328 WORunLoopFileObjectInfo *info = nil; 329 info = [[WORunLoopFileObjectInfo allocWithZone:[self zone]] 330 initWithFileObject:_fileObject 331 activities:0]; 332 [self->fileObjects removeObject:info]; 333 //NSLog(@"file objects now: %@", self->fileObjects); 334 RELEASE(info); info = nil; 335} 336 337- (void)addTimer:(NSTimer*)aTimer 338{ 339 [timers addObject:[WORunLoopTimerInfo infoWithTimer:aTimer]]; 340} 341 342- (void)addOperation:(WORunLoopActionHolder*)holder 343{ 344 [otherOperations addObject:holder]; 345 [otherOperations sortUsingSelector:@selector(compare:)]; 346} 347 348- (void)removeOperation:(WORunLoopActionHolder*)holder 349{ 350 [otherOperations removeObject:holder]; 351} 352 353- (void)performAdditionalOperations 354{ 355 [otherOperations makeObjectsPerformSelector:@selector(execute)]; 356} 357 358- (NSMutableArray*)fileObjects { return fileObjects; } 359- (NSMutableArray*)timers { return timers; } 360 361@end /* WORunLoopInputManager */ 362 363@implementation WORunLoop 364 365/* Class variable */ 366static WORunLoop *currentRunLoop = nil; 367static BOOL taskIsMultithreaded = NO; 368 369+ (void)error:(id)_o { 370 NSLog(@"ERROR:"); 371 NSLog(@" %@", _o); 372} 373 374+ (NSRunLoop *)currentRunLoop 375{ 376 if (taskIsMultithreaded) { 377 NSLog(@"WORunLoop does not work multithreaded, exit .."); 378 return nil; 379 } 380 else { 381 if (!currentRunLoop) 382 currentRunLoop = [[self alloc] init]; 383 return currentRunLoop; 384 } 385} 386 387- (id)init { 388 self->inputsForMode = [[NSMutableDictionary allocWithZone:[self zone]] init]; 389 self->mode = RETAIN(NSDefaultRunLoopMode); 390 return self; 391} 392 393- (void)dealloc { 394 RELEASE(self->inputsForMode); 395 RELEASE(self->mode); 396 [super dealloc]; 397} 398 399- (NSString *)currentMode { 400 return self->mode; 401} 402 403static inline WORunLoopInputManager* 404_getInputManager(WORunLoop *self, NSString *_mode) 405{ 406 WORunLoopInputManager* inputManager; 407 408 inputManager = [self->inputsForMode objectForKey:_mode]; 409 if (inputManager == nil) { 410 inputManager = [WORunLoopInputManager new]; 411 [self->inputsForMode setObject:inputManager forKey:_mode]; 412 RELEASE(inputManager); 413 } 414 return inputManager; 415} 416 417static int compare_fire_dates(id timer1, id timer2, void* userData) 418{ 419 return [[timer1 fireDate] compare:[timer2 fireDate]]; 420} 421 422- (NSDate*)limitDateForMode:(NSString*)aMode 423{ 424 NSString *format = @"%s: Caught exception %@ with reason %@ "; 425 NSMutableArray *timers = [[inputsForMode objectForKey:aMode] timers]; 426 volatile int i, count; 427 NSMutableArray *copyOfTimers; 428 429 ASSIGN(mode, aMode); 430 431 /* Remove invalid timers */ 432 for(count = [timers count], i = count - 1; i >= 0; i--) 433 if(![[[timers objectAtIndex:i] timer] isValid]) { 434 [timers removeObjectAtIndex:i]; 435 count--; 436 } 437 438 /* Currently only timers have limit dates associated with them */ 439 if(!count) 440 return nil; 441 442 copyOfTimers = [timers mutableCopy]; 443 444 /* Sort the timers based on their fire date */ 445 [copyOfTimers sortUsingFunction:compare_fire_dates context:NULL]; 446 447 /* Fire all the timers with their fire date expired */ 448 for(i = 0; i < count; i++) { 449 WORunLoopTimerInfo* timerInfo = [copyOfTimers objectAtIndex:i]; 450 NSDate* fireDate = [timerInfo fireDate]; 451 NSDate* currentDate = [NSDate date]; 452 453 if([fireDate earlierDate:currentDate] == fireDate 454 || [fireDate isEqualToDate:currentDate]) { 455 NSTimer* timer = [timerInfo timer]; 456 NS_DURING 457 [timer fire]; 458 NS_HANDLER 459 NSLog(format, __PRETTY_FUNCTION__, 460 [localException name], [localException reason]); 461 NS_ENDHANDLER; 462 463#if 0 464#warning no repeated timers ! 465 if(![timer repeats]) 466#endif 467 [timer invalidate]; 468 } 469 } 470 471 RELEASE(copyOfTimers); 472 473 /* Recompute the fire dates for this cycle */ 474 [timers makeObjectsPerformSelector:@selector(recomputeFireDate)]; 475 476 /* Sort the timers based on their fire date */ 477 [timers sortUsingFunction:compare_fire_dates context:NULL]; 478 479 return [timers count] ? [[timers objectAtIndex:0] fireDate] : nil; 480} 481 482- (void)addTimer:(NSTimer*)aTimer 483 forMode:(NSString*)aMode 484{ 485 [_getInputManager(self, aMode) addTimer:aTimer]; 486} 487 488- (BOOL)runMode:(NSString*)aMode 489 beforeDate:(NSDate*)limitDate 490{ 491 id inputManager, fileObjects; 492 NSArray* timers; 493 NSDate* date; 494 495 /* Retain the limitDate so it doesn't get released by limitDateForMode: 496 if it fires a timer that has as fireDate the limitDate. 497 (bug report from Benhur Stein <Benhur-de-Oliveira.Stein@imag.fr>) 498 */ 499 (void)RETAIN(limitDate); 500 501 inputManager = [inputsForMode objectForKey:aMode]; 502 timers = [inputManager timers]; 503 fileObjects = [inputManager fileObjects]; 504 505 if (([timers count] != 0) || ([fileObjects count] != 0)) { 506 CREATE_AUTORELEASE_POOL(pool); 507 508 date = [self limitDateForMode:aMode]; 509 date = date ? [date earlierDate:limitDate] : limitDate; 510 [self acceptInputForMode:aMode beforeDate:date]; 511 RELEASE(pool); 512 RELEASE(limitDate); 513 return YES; 514 } 515 516 RELEASE(limitDate); 517 return NO; 518} 519 520/* Runs the loop until limitDate or until the earliest limit date for input 521 sources in the specified mode. */ 522- (void)acceptInputForMode:(NSString*)aMode 523 beforeDate:(NSDate*)limitDate 524{ 525 id inputManager, fileObjects; 526 struct timeval tp = { 0, 0 }; 527 struct timeval* timeout = NULL; 528 NSTimeInterval delay = 0; 529 fd_set readSet, writeSet, exceptionsSet; 530 volatile int i, r, count; 531 532 ASSIGN(mode, aMode); 533 534 if(limitDate == nil) // delay = 0 535 limitDate = [NSDate distantFuture]; 536 else { 537 delay = [limitDate timeIntervalSinceNow]; 538 /* delay > 0 means a future date */ 539 540 /* If limitDate is in the past return */ 541 if(delay < 0) 542 return; 543 } 544 545 inputManager = [inputsForMode objectForKey:aMode]; 546 fileObjects = [inputManager fileObjects]; 547 548 /* Compute the timeout for select */ 549 if([limitDate isEqual:[NSDate distantFuture]]) 550 timeout = NULL; 551 else { 552 tp.tv_sec = delay; 553 tp.tv_usec = (delay - (NSTimeInterval)tp.tv_sec) * 1000000.0; 554 timeout = &tp; 555 } 556 557 ASSIGN(mode, aMode); 558 559 FD_ZERO(&readSet); 560 FD_ZERO(&writeSet); 561 FD_ZERO(&exceptionsSet); 562 563 do { 564 count = [fileObjects count]; 565 for (i = 0; i < count; i++) { 566 WORunLoopFileObjectInfo *info; 567 NSPosixFileActivities fileActivity; 568 int fd; 569 570 info = [fileObjects objectAtIndex:i]; 571 if (![info isAlive]) 572 continue; 573 574 fileActivity = [info watchedActivities]; 575 fd = [info fileDescriptor]; 576 577 if (fd >= 0) { 578#if !defined(__MINGW32__) /* on Windows descriptors can be BIG */ 579 if (fd >= FD_SETSIZE) { 580 NSLog(@"%s: fd %i of %@ exceeds select size %i", 581 __PRETTY_FUNCTION__, 582 fd, info, FD_SETSIZE); 583 continue; 584 } 585#endif /* !defined(__MINGW32__) */ 586 587 //NSLog(@"registering activity %s for fd %i ..", 588 // activityDesc[fileActivity], fd); 589 590 if (fileActivity & NSPosixReadableActivity) 591 FD_SET(fd, &readSet); 592 if (fileActivity & NSPosixWritableActivity) 593 FD_SET(fd, &writeSet); 594 if (fileActivity & NSPosixExceptionalActivity) 595 FD_SET(fd, &exceptionsSet); 596 } 597 } 598 599 // ???: errno = 0; What is this good for ? 600 r = select(FD_SETSIZE, &readSet, &writeSet, &exceptionsSet, timeout); 601 if (r == -1) { 602 if (errno == EINTR) { 603 /* Interrupt occured; break the loop to give a chance to 604 UnixSignalHandler to handle the signals. */ 605 errno = 0; 606 break; 607 } 608 else { 609 NSLog(@"%s: select() error: '%s'", 610 __PRETTY_FUNCTION__, strerror (errno)); 611 break; 612 } 613 errno = 0; 614 } 615 } while (r == -1); 616 617 if(r > 0) { 618 id fileObjectsCopy; 619 NSString* format = @"%s: Caught exception %@ with reason %@ "; 620 621 *(&fileObjectsCopy) = nil; 622 623 NS_DURING { 624 // made copy, so that modifications in the delegate don't 625 // alter the loop 626 fileObjectsCopy = [fileObjects copyWithZone:[self zone]]; 627 count = [fileObjectsCopy count]; 628 629 for (i = 0; (i < count) && (r > 0); i++) { 630 WORunLoopFileObjectInfo *info; 631 NSPosixFileActivities activity = 0; 632 int fd; 633 634 info = [fileObjectsCopy objectAtIndex:i]; 635 fd = [info fileDescriptor]; 636 637 if (fd >= 0) { 638 //NSLog(@"checking activity for %i info %@ ..", fd, info); 639 640 if (FD_ISSET(fd, &readSet)) { 641 activity |= NSPosixReadableActivity; 642 r--; 643 } 644 if (FD_ISSET(fd, &writeSet)) { 645 activity |= NSPosixWritableActivity; 646 r--; 647 } 648 if (FD_ISSET(fd, &exceptionsSet)) { 649 activity |= NSPosixExceptionalActivity; 650 r--; 651 } 652 653 if (activity != 0) 654 [info activity:activity onDescriptor:fd]; 655 } 656 } 657 if (r > 0) { 658 [self warnWithFormat:@"could not resolve all activities (%i) ..", 659 r); 660 } 661 } 662 NS_HANDLER { 663 [self errorWithFormat:format, __PRETTY_FUNCTION__, 664 [localException name], [localException reason]]; 665 } 666 NS_ENDHANDLER; 667 668 RELEASE(fileObjectsCopy); fileObjectsCopy = nil; 669 } 670 671 [inputManager performAdditionalOperations]; 672#if !NeXT_Foundation_LIBRARY 673 [NSNotificationQueue runLoopASAP]; 674#endif 675} 676 677- (void)runUntilDate:(NSDate*)limitDate 678{ 679 BOOL shouldContinue = YES; 680 681 if(!limitDate) 682 limitDate = [NSDate distantFuture]; 683 else { 684 /* If limitDate is in the past return */ 685 if([limitDate timeIntervalSinceNow] < 0) 686 return; 687 } 688 689 while (shouldContinue) { 690 CREATE_AUTORELEASE_POOL(pool); 691 692 if ([limitDate laterDate:[NSDate date]] == limitDate) { 693 if([self runMode:NSDefaultRunLoopMode beforeDate:limitDate] == NO) 694 shouldContinue = NO; 695 } 696 else 697 shouldContinue = NO; 698 RELEASE(pool); 699 } 700} 701 702- (void)run 703{ 704 [self runUntilDate:[NSDate distantFuture]]; 705} 706 707- (void)performSelector:(SEL)aSelector 708 target:(id)target 709 argument:(id)anArgument 710 order:(unsigned)order 711 modes:(NSArray*)modes 712{ 713 id holder = [WORunLoopActionHolder objectWithTarget:target 714 argument:anArgument 715 selector:aSelector 716 order:order]; 717 int i, count = [modes count]; 718 719 for (i = 0; i < count; i++) 720 [[inputsForMode objectForKey:[modes objectAtIndex:i]] 721 addOperation:holder]; 722} 723 724- (void)cancelPerformSelector:(SEL)aSelector 725 target:(id)target 726 argument:(id)anArgument 727{ 728 id holder = [WORunLoopActionHolder objectWithTarget:target 729 argument:anArgument 730 selector:aSelector 731 order:0]; 732 id enumerator = [inputsForMode keyEnumerator]; 733 id aMode; 734 735 while ((aMode = [enumerator nextObject])) 736 [[inputsForMode objectForKey:aMode] removeOperation:holder]; 737} 738 739/* Monitoring file objects */ 740 741- (void)addFileObject:(id)_fileObject 742 activities:(NSPosixFileActivities)_activities 743 forMode:(NSString *)_mode 744{ 745 [_getInputManager(self, _mode) addFileObject:_fileObject 746 activities:_activities]; 747} 748 749- (void)removeFileObject:(id)_fileObject 750 forMode:(NSString *)_mode 751{ 752 [_getInputManager(self, _mode) removeFileObject:_fileObject]; 753} 754 755@end /* WORunLoop */ 756 757#endif /* !LIB_FOUNDATION_LIBRARY */ 758#endif // 0 759