Here's the (terse) code of my MTKView render loop.
- (void)drawRect:(NSRect)dirtyRect {
dispatch_semaphore_wait(self.inflightSemaphore, DISPATCH_TIME_FOREVER);
[super drawRect:dirtyRect];
...
[commandBuffer addCompletedHandler:^(id buffer) {
...
dispatch_semaphore_signal(self.inflightSemaphore); // being flagged here!
}
}
With thread sanitation turned on, XCode (9.4.1) is flagging the dispatch_semaphore_signal as a 'Data Race detected'.
In my 'applicationDidFinishLaunching' code, I allocate the MTKView (MonitorMTKView and DisplayMTKView) programatically and them as subviews to an NSWindow. This is flagged as a 'Heap block allocated by thread1' and 'Write of size 8 by thread 1' by the race detector. These MTKViews are setup to require manual draw calls (enableSetNeedsDisplay = NO and paused = YES).
After this setup, I create my CVDisplayLink callbacks; I have these issuing the render call, and that's where the semaphore is flagged as a 'Read of Size 8 by thread 5' in a MonitorMTKView instance. I should mention that render output of DisplayMTKView is being passed down to a MonitorMTKView for further processing/display.
How is this even possible? It's a semaphore!
Here's some further debug info:
WARNING: ThreadSanitizer: data race (pid=2876)
Read of size 8 at 0x7b0c000cd4c0 by thread T35:
#0 __32-[MonitorMTKView drawRect:]_block_invoke MonitorMTKView.m:225 (CDTest:x86_64+0x100241e5c)
#1 _doMTLDispatch <null>:11239952 (Metal:x86_64+0x550e9)
#2 _dispatch_client_callout <null>:11239952 (libdispatch.dylib:x86_64+0x1d8e)
Previous write of size 8 at 0x7b0c000cd4c0 by thread T30 (mutexes: write M578848238724123704):
#0 __copy_helper_block_ MonitorMTKView.m:224 (CDTest:x86_64+0x100241f5d)
#1 _Block_copy <null>:11239952 (libsystem_blocks.dylib:x86_64+0x8ef)
#2 -[MTKView draw] <null>:11239952 (MetalKit:x86_64+0x134d9)
#3 -[DisplayMTKView drawRect:] DisplayMTKView.m:647 (CDTest:x86_64+0x1000eb774)
#4 -[MTKView draw] <null>:11239952 (MetalKit:x86_64+0x134d9)
#5 cvCallback DisplayCVLinkWrapper.m:228 (CDTest:x86_64+0x1001f2e1c)
#6 CVDisplayLink::performIO(CVTimeStamp*) <null>:11239952 (CoreVideo:x86_64+0x35ce)
Location is heap block 2018-10-14 08:49:57.169041-0700 CDTest[2876:386511] Bailed on: NO_ROTATIONAL_BUFFER
of size 40 at 0x7b0c000cd4a0 allocated by thread T30:
2018-10-14 08:49:57.158746-0700 CDTest[2876:386509] Bailed on: NO_ROTATIONAL_BUFFER
2018-10-14 08:49:57.169280-0700 CDTest[2876:386515] Bailed on: NO_ROTATIONAL_BUFFER
#0 malloc <null>:11239984 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x4998a)
#1 _Block_copy <null>:11239984 (libsystem_blocks.dylib:x86_64+0x8b2)
#2 -[MTKView draw] <null>:11239984 (MetalKit:x86_64+0x134d9)
#3 -[DisplayMTKView drawRect:] DisplayMTKView.m:647 (CDTest:x86_64+0x1000eb774)
#4 -[MTKView draw] <null>:11239984 (MetalKit:x86_64+0x134d9)
#5 cvCallback DisplayCVLinkWrapper.m:228 (CDTest:x86_64+0x1001f2e1c)
#6 CVDisplayLink::performIO(CVTimeStamp*) <null>:11239984 (CoreVideo:x86_64+0x35ce)
Mutex M578848238724123704 is already destroyed.
Thread T35 (tid=386576, running) is a GCD worker thread
Thread T30 (tid=386513, running) created by main thread at:
#0 pthread_create <null>:11240032 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x283ed)
#1 CVDisplayLink::start() <null>:11240032 (CoreVideo:x86_64+0x268f)
#2 -[DisplayItemController setupWindows] DisplayItemController.m:312 (CDTest:x86_64+0x100012b39)
#3 -[DisplayItemController initWithController:withContext:] DisplayItemController.m:90 (CDTest:x86_64+0x10000d71d)
#4 -[AppDelegate applicationDidFinishLaunching:] AppDelegate.m:229 (CDTest:x86_64+0x100208d87)
#5 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ <null>:11240032 (CoreFoundation:x86_64h+0x9aedb)
#6 start <null>:11240032 (libdyld.dylib:x86_64+0x1014)
SUMMARY: ThreadSanitizer: data race MonitorMTKView.m:225 in __32-[MonitorMTKView drawRect:]_block_invoke