Author: rfm Date: Wed Jul 13 10:58:23 2016 New Revision: 39995 URL: http://svn.gna.org/viewcvs/gnustep?rev=39995&view=rev Log: More OSX compatibility changes
Modified: libs/base/trunk/ChangeLog libs/base/trunk/Source/NSRunLoop.m Modified: libs/base/trunk/ChangeLog URL: http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/ChangeLog?rev=39995&r1=39994&r2=39995&view=diff ============================================================================== --- libs/base/trunk/ChangeLog (original) +++ libs/base/trunk/ChangeLog Wed Jul 13 10:58:23 2016 @@ -1,3 +1,11 @@ +2016-07-13 Richard Frith-Macdonald <r...@gnu.org> + + * Source/NSRunLoop.m (-acceptInputForMode:beforeDate:): + Closer to OSX behavior ... when accepting input we should fire all + timers (possibly repeatedly) since a timer is not counted as an + input source. Also simplify housekeeping timer so that it will + keep loops running as it does on OSX. + 2016-07-12 Niels Grewe <niels.gr...@halbordnung.de> * Headers/Foundation/NSObjCRuntime.h Modified: libs/base/trunk/Source/NSRunLoop.m URL: http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/Source/NSRunLoop.m?rev=39995&r1=39994&r2=39995&view=diff ============================================================================== --- libs/base/trunk/Source/NSRunLoop.m (original) +++ libs/base/trunk/Source/NSRunLoop.m Wed Jul 13 10:58:23 2016 @@ -749,7 +749,6 @@ if (nil != current && [GSCurrentThread() isMainThread] == YES) { NSAutoreleasePool *arp = [NSAutoreleasePool new]; - GSRunLoopCtxt *context; NSNotificationCenter *ctr; NSNotification *not; NSInvocation *inv; @@ -768,27 +767,13 @@ [inv setArgument: ¬ atIndex: 2]; [inv retainArguments]; - context = NSMapGet(current->_contextMap, NSDefaultRunLoopMode); - if (context == nil) - { - context = [GSRunLoopCtxt alloc]; - context = [context initWithMode: NSDefaultRunLoopMode - extra: current->_extra]; - NSMapInsert(current->_contextMap, context->mode, context); - RELEASE(context); - } - if (context->housekeeper != nil) - { - [context->housekeeper invalidate]; - DESTROY(context->housekeeper); - } timer = [[NSTimer alloc] initWithFireDate: nil interval: 30.0 target: inv selector: NULL userInfo: nil repeats: YES]; - context->housekeeper = timer; + [current addTimer: timer forMode: NSDefaultRunLoopMode]; #ifdef RL_INTEGRATE_DISPATCH // We leak the queue drainer, because it's integral part of RL @@ -979,6 +964,131 @@ return YES; } +- (NSDate*) _limitDateForContext: (GSRunLoopCtxt *)context +{ + NSDate *when = nil; + NSAutoreleasePool *arp = [NSAutoreleasePool new]; + GSIArray timers = context->timers; + NSTimeInterval now; + NSDate *earliest; + NSDate *d; + NSTimer *t; + NSTimeInterval ti; + NSTimeInterval ei; + unsigned c; + unsigned i; + + ei = 0.0; // Only needed to avoid compiler warning + + /* + * Save current time so we don't keep redoing system call to + * get it and so that we check timer fire dates against a known + * value at the point when the method was called. + * If we refetched the date after firing each timer, the time + * taken in firing the timer could be large enough so we would + * just keep firing the timer repeatedly and never return from + * this method. + */ + now = GSPrivateTimeNow(); + + /* Fire the oldest/first valid timer whose fire date has passed + * and fire it. + * We fire timers in the order in which they were added to the + * run loop rather than in date order. This prevents code + * from blocking other timers by adding timers whose fire date + * is some time in the past... we guarantee fair handling. + */ + c = GSIArrayCount(timers); + for (i = 0; i < c; i++) + { + t = GSIArrayItemAtIndex(timers, i).obj; + if (timerInvalidated(t) == NO) + { + d = timerDate(t); + ti = [d timeIntervalSinceReferenceDate]; + if (ti < now) + { + GSIArrayRemoveItemAtIndexNoRelease(timers, i); + [t fire]; + GSPrivateNotifyASAP(_currentMode); + IF_NO_GC([arp emptyPool];) + if (updateTimer(t, d, now) == YES) + { + /* Updated ... replace in array. + */ + GSIArrayAddItemNoRetain(timers, + (GSIArrayItem)((id)t)); + } + else + { + /* The timer was invalidated, so we can + * release it as we aren't putting it back + * in the array. + */ + RELEASE(t); + } + break; + } + } + } + + /* Now, find the earliest remaining timer date while removing + * any invalidated timers. We iterate from the end of the + * array to minimise the amount of array alteration needed. + */ + earliest = nil; + i = GSIArrayCount(timers); + while (i-- > 0) + { + t = GSIArrayItemAtIndex(timers, i).obj; + if (timerInvalidated(t) == YES) + { + GSIArrayRemoveItemAtIndex(timers, i); + } + else + { + d = timerDate(t); + ti = [d timeIntervalSinceReferenceDate]; + if (earliest == nil || ti < ei) + { + earliest = d; + ei = ti; + } + } + } + + /* The earliest date of a valid timeout is copied into 'when' + * and used as our limit date. + */ + if (earliest != nil) + { + [arp drain]; + when = AUTORELEASE([earliest copy]); + } + else + { + GSIArray watchers = context->watchers; + unsigned i = GSIArrayCount(watchers); + + while (i-- > 0) + { + GSRunLoopWatcher *w = GSIArrayItemAtIndex(watchers, i).obj; + + if (w->_invalidated == YES) + { + GSIArrayRemoveItemAtIndex(watchers, i); + } + } + if (GSIArrayCount(context->watchers) > 0) + { + when = theFuture; + } + [arp drain]; + } + + return when; +} + /** * Fires timers whose fire date has passed, and checks timers and limit dates * for input sources, determining the earliest time that any future timeout @@ -996,124 +1106,11 @@ if (context != nil) { NSString *savedMode = _currentMode; - NSAutoreleasePool *arp = [NSAutoreleasePool new]; _currentMode = mode; NS_DURING { - GSIArray timers = context->timers; - NSTimeInterval now; - NSDate *earliest; - NSDate *d; - NSTimer *t; - NSTimeInterval ti; - NSTimeInterval ei; - unsigned c; - unsigned i; - - ei = 0.0; // Only needed to avoid compiler warning - - /* - * Save current time so we don't keep redoing system call to - * get it and so that we check timer fire dates against a known - * value at the point when the method was called. - * If we refetched the date after firing each timer, the time - * taken in firing the timer could be large enough so we would - * just keep firing the timer repeatedly and never return from - * this method. - */ - now = GSPrivateTimeNow(); - - /* Fire housekeeping timer as necessary - */ - if ((t = context->housekeeper) != nil) - { - if (timerInvalidated(t)) - { - DESTROY(context->housekeeper); - } - else if ([(d=timerDate(t)) timeIntervalSinceReferenceDate] <= now) - { - [t fire]; - GSPrivateNotifyASAP(_currentMode); - IF_NO_GC([arp emptyPool];) - updateTimer(t, d, now); - } - } - - /* Fire the oldest/first valid timer whose fire date has passed - * and fire it. - * We fire timers in the order in which they were added to the - * run loop rather than in date order. This prevents code - * from blocking other timers by adding timers whose fire date - * is some time in the past... we guarantee fair handling. - */ - c = GSIArrayCount(timers); - for (i = 0; i < c; i++) - { - t = GSIArrayItemAtIndex(timers, i).obj; - if (timerInvalidated(t) == NO) - { - d = timerDate(t); - ti = [d timeIntervalSinceReferenceDate]; - if (ti < now) - { - GSIArrayRemoveItemAtIndexNoRelease(timers, i); - [t fire]; - GSPrivateNotifyASAP(_currentMode); - IF_NO_GC([arp emptyPool];) - if (updateTimer(t, d, now) == YES) - { - /* Updated ... replace in array. - */ - GSIArrayAddItemNoRetain(timers, - (GSIArrayItem)((id)t)); - } - else - { - /* The timer was invalidated, so we can - * release it as we aren't putting it back - * in the array. - */ - RELEASE(t); - } - break; - } - } - } - - /* Now, find the earliest remaining timer date while removing - * any invalidated timers. We iterate from the end of the - * array to minimise the amount of array alteration needed. - */ - earliest = nil; - i = GSIArrayCount(timers); - while (i-- > 0) - { - t = GSIArrayItemAtIndex(timers, i).obj; - if (timerInvalidated(t) == YES) - { - GSIArrayRemoveItemAtIndex(timers, i); - } - else - { - d = timerDate(t); - ti = [d timeIntervalSinceReferenceDate]; - if (earliest == nil || ti < ei) - { - earliest = d; - ei = ti; - } - } - } - - /* The earliest date of a valid timeout is copied into 'when' - * and used as our limit date. - */ - if (earliest != nil) - { - when = [earliest copy]; - } + when = [self _limitDateForContext: context]; _currentMode = savedMode; } NS_HANDLER @@ -1122,32 +1119,6 @@ [localException raise]; } NS_ENDHANDLER - - [arp release]; - - if (when == nil) - { - GSIArray watchers = context->watchers; - unsigned i = GSIArrayCount(watchers); - - while (i-- > 0) - { - GSRunLoopWatcher *w = GSIArrayItemAtIndex(watchers, i).obj; - - if (w->_invalidated == YES) - { - GSIArrayRemoveItemAtIndex(watchers, i); - } - } - if (GSIArrayCount(context->watchers) > 0) - { - when = theFuture; - } - } - else - { - AUTORELEASE(when); - } NSDebugMLLog(@"NSRunLoop", @"limit date %f in %@", nil == when ? 0.0 : [when timeIntervalSinceReferenceDate], mode); @@ -1158,9 +1129,9 @@ /** * Listen for events from input sources.<br /> * If limit_date is nil or in the past, then don't wait; - * just poll inputs and return, - * otherwise block until input is available or until the - * earliest limit date has passed (whichever comes first).<br /> + * just fire timers, poll inputs and return, otherwise block + * (firing timers when they are due) until input is available + * or until the earliest limit date has passed (whichever comes first).<br /> * If the supplied mode is nil, uses NSDefaultRunLoopMode.<br /> * If there are no input sources or timers in the mode, returns immediately. */ @@ -1178,82 +1149,94 @@ { mode = NSDefaultRunLoopMode; } + context = NSMapGet(_contextMap, mode); + if (nil == context) + { + return; + } _currentMode = mode; - context = NSMapGet(_contextMap, mode); [self _checkPerformers: context]; NS_DURING { - /* - * If we have a housekeeping timer, and it is earlier than the - * limit date we have been given, we use the date of the housekeeper - * to determine when to stop. - */ - if (limit_date != nil && context != nil && context->housekeeper != nil - && [timerDate(context->housekeeper) timeIntervalSinceReferenceDate] - < [limit_date timeIntervalSinceReferenceDate]) - { - limit_date = timerDate(context->housekeeper); - } - - if (context == nil - || (GSIArrayCount(context->watchers) == 0 - && GSIArrayCount(context->timers) == 0)) - { - NSDebugMLLog(@"NSRunLoop", @"no inputs or timers in mode %@", mode); - GSPrivateNotifyASAP(_currentMode); - GSPrivateNotifyIdle(_currentMode); - /* Pause until the limit date or until we might have - * a method to perform in this thread. - */ - [GSRunLoopCtxt awakenedBefore: nil]; - GSPrivateCheckTasks(); - if (context != nil) - { - [self _checkPerformers: context]; - } - GSPrivateNotifyASAP(_currentMode); - _currentMode = savedMode; - [arp drain]; - NS_VOIDRETURN; - } - - /* Find out how much time we should wait, and set SELECT_TIMEOUT. */ - if (limit_date == nil - || (ti = [limit_date timeIntervalSinceNow]) <= 0.0) - { - /* Don't wait at all. */ - timeout_ms = 0; - } - else - { - /* Wait until the LIMIT_DATE. */ - if (ti >= INT_MAX / 1000) - { - timeout_ms = INT_MAX; // Far future. - } - else - { - timeout_ms = (ti * 1000.0); - } - } - - NSDebugMLLog(@"NSRunLoop", - @"accept I/P before %d millisec from now in %@", - timeout_ms, mode); + BOOL done = NO; + NSDate *when; if ([_contextStack indexOfObjectIdenticalTo: context] == NSNotFound) { [_contextStack addObject: context]; } - if ([context pollUntil: timeout_ms within: _contextStack] == NO) - { - GSPrivateNotifyIdle(_currentMode); - } - [self _checkPerformers: context]; - GSPrivateNotifyASAP(_currentMode); - _currentMode = savedMode; + + while (NO == done) + { + when = [self _limitDateForContext: context]; + if (nil == when) + { + NSDebugMLLog(@"NSRunLoop", + @"no inputs or timers in mode %@", mode); + GSPrivateNotifyASAP(_currentMode); + GSPrivateNotifyIdle(_currentMode); + /* Pause until the limit date or until we might have + * a method to perform in this thread. + */ + [GSRunLoopCtxt awakenedBefore: nil]; + GSPrivateCheckTasks(); + [self _checkPerformers: context]; + GSPrivateNotifyASAP(_currentMode); + [_contextStack removeObjectIdenticalTo: context]; + _currentMode = savedMode; + [arp drain]; + NS_VOIDRETURN; + } + else + { + if (nil == limit_date) + { + when = nil; + } + else + { + when = [when earlierDate: limit_date]; + } + } + + /* Find out how much time we should wait, and set SELECT_TIMEOUT. */ + if (nil == when || (ti = [when timeIntervalSinceNow]) <= 0.0) + { + /* Don't wait at all. */ + timeout_ms = 0; + } + else + { + /* Wait until the LIMIT_DATE. */ + if (ti >= INT_MAX / 1000) + { + timeout_ms = INT_MAX; // Far future. + } + else + { + timeout_ms = (ti * 1000.0); + } + } + + NSDebugMLLog(@"NSRunLoop", + @"accept I/P before %d millisec from now in %@", + timeout_ms, mode); + + done = [context pollUntil: timeout_ms within: _contextStack]; + if (NO == done) + { + GSPrivateNotifyIdle(_currentMode); + if (nil == limit_date || [limit_date timeIntervalSinceNow] <= 0.0) + { + done = YES; + } + } + [self _checkPerformers: context]; + GSPrivateNotifyASAP(_currentMode); + [context endPoll]; + } /* Once a poll has been completed on a context, we can remove that * context from the stack even if it actually polling at an outer @@ -1261,9 +1244,8 @@ * have handled any events that the outer levels would have wanted * to handle, and the polling for this context will be marked as ended. */ - [context endPoll]; + _currentMode = savedMode; [_contextStack removeObjectIdenticalTo: context]; - NSDebugMLLog(@"NSRunLoop", @"accept I/P completed in %@", mode); } NS_HANDLER { @@ -1273,6 +1255,7 @@ [localException raise]; } NS_ENDHANDLER + NSDebugMLLog(@"NSRunLoop", @"accept I/P completed in %@", mode); [arp drain]; } _______________________________________________ Gnustep-cvs mailing list Gnustep-cvs@gna.org https://mail.gna.org/listinfo/gnustep-cvs