diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 7f370216e..f049730f4 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -67,7 +67,7 @@ _isDrawingFlag); + [view.displayLinkDelegate openGLViewDisplayLinkDidFire:view now:now outputTime:outputTime]; /* Do not touch the display link from after this call; there's a bit of a race condition with setupDisplayLink. @@ -77,15 +86,22 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt access the display link itself as part of -drawAtTime:frequency:. */ + OSAtomicDecrement32(&view->_isDrawingFlag); + return kCVReturnSuccess; } - (void)checkDisplayLink { + // Don't do anything if this view has already been invalidated. + if(_isInvalid) { + return; + } + // Test now whether the screen this view is on has changed since last time it was checked. // There's likely a callback available for this, on NSWindow if nowhere else, or an NSNotification, // but since this method is going to be called repeatedly anyway, and the test is cheap, polling // feels fine. - if(self.window.screen != _currentScreen) { + if(![self.window.screen isEqual:_currentScreen]) { _currentScreen = self.window.screen; // Issue a reshape, in case a switch to/from a Retina display has @@ -113,13 +129,24 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt } - (void)invalidate { + _isInvalid = YES; + [self stopDisplayLink]; +} + +- (void)stopDisplayLink { const double duration = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_displayLink); CVDisplayLinkStop(_displayLink); - // This is a workaround; I could find no way to ensure that a callback from the display - // link is not currently ongoing. In short: call stop, wait for an entire refresh period, - // then assume (/hope) the coast is clear. + // This is a workaround; CVDisplayLinkStop does not wait for any existing call to the + // display-link callback to stop. Furthermore there's a race condition between a callback + // and any ability by me to set state. + // + // So: wait for a whole display link tick to avoid the second race condition. Then spin + // on an atomic flag. usleep((useconds_t)ceil(duration * 1000000.0)); + + // Spin until _isDrawingFlag is 0 (and leave it as 0). + while(!OSAtomicCompareAndSwap32(0, 0, &_isDrawingFlag)); } - (void)dealloc {