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 <[email protected]>
+
+ * 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 <[email protected]>
* 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
[email protected]
https://mail.gna.org/listinfo/gnustep-cvs