Protecting __block id from Thread Sanitizer

I'm doing some multithreading in Metal, and Thread Sanitizer is tripping up on a "__block id" type (Objective-C) I use in some framework code to collect an identifier returned from a callout block, which is later supplied to a callback block, like:

__block id identifier; //Read-then-write from different threads
id renderer = [self rendererWithCompletionHandler:^{ callback(identifier); }];
dispatch_async(SOME_GLOBAL_QUEUE, ^{
     identifier = callout(renderer);
     [renderer finish];
});

The API allows any subsystem to do Metal work, then perform any cleanup in the callback without the framework having to understand it, or even what form the identifier takes (frame number, object state, etc). The sample above is heavily simplified, the framework does a lot of setup work to make it convenient to attach to the drawing loop.

The issue is Thread Sanitizer doesn't like the read/write from multiple threads, but how can I efficiently protect "identifier" without doing a lot of extra work, like creating an object? I know the completion handler is strictly ordered after the dispatch_async, but I can't think of how to effectively describe this using GCD or other synchronization primitives. Keep in mind there is an outer loop supplying callout/callback pairs, this occurs at every draw, and pairs may be registered/unregistered after any frame.

Replies

The problem with the code you've shown is that there's nothing there to guarantee that line 4 executes before the next time line 2 is executed. That makes it thread-unsafe to read the value of 'identifier' in line 2.


If you're saying that the code fragment can't be re-entered at line 1 until after line 4 executes (for example because new work for the new identifier isn't scheduled until line 5 executes), then I think the solution is to pass the new identifier into the 'finish' method, and from there "around and into" the new work that ends up at line 2. In that case, 'identifier' doesn't need to be __block.

I don't think that would work because the completion handler is passed into the Metal API before anything is prepared to return on line 2. If I stored it in renderer or similar, wouldn't that still require synchronization? I just ended up using a GCD semaphore.

The semaphore sounds great, if that solved your problem. It's a nice, pretty lightweight way of getting atomicity.


Regarding the first part of your reply, I'm not 100% sure what you mean, but the __block annotation means that you're effectively capturing a reference to the variable in the completion handler, so you don't know exactly when the value of the variable will be retrieved.


Still, that's moot, if the problem is solved. 🙂