Any way to fix "UI API called on a background thread" for gets?

We have code that calls


return self.mainProgressBar.doubleValue;


from a background thread. For the sets, it's easy to do this the correct way:


dispatch_async(dispatch_get_main_queue(), ^{
     self.mainProgressBar.doubleValue = val;
});


But how does one fix this for returning a value from a property?


After thinking about it, it was obvious. Too early in the day. Coffee hadn't kicked in. I did this:


__block double               result = 0;

// Note sync, not async, so it waits:
dispatch_sync(dispatch_get_main_queue(), ^{
     result = self.mainProgressBar.doubleValue;
});

return result;

Accepted Reply

Use a block just like the setter, but assign the result to a __block variable.

Replies

Use a block just like the setter, but assign the result to a __block variable.

We must've been typing at the same time. Thanks for concurring with my concurrency solution. 🙂

If you do this (have a secondary thread call the main thread synchronously) you have to be very careful about deadlocks. That’s because you typically want the main thread to be able to call your secondary thread synchronously — because the main thread often finds itself in need of immediate results — and you do both of these then you will eventually deadlock.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hey Quinn - haven't spoken to you in ages. Good to see you're still doing your thing.


I'm pretty sure our main thread never calls our worker threads. But let's say it did: How would the worker thread GET NSProgressIndicator.doubleValue in an asyncronous way?


Or are you saying that both setting and getting the doubleValue should both be done synchronously?

Or are you saying that both setting and getting the doubleValue should both be done synchronously?

I’m saying that:

  • In generally I recommend async for everything.

  • If you do stuff synchronously, only do it synchronously in one direction.

With regards the last point, my experience is it’s better to reserve your synchronous direction for the ‘main thread to worker thread’ case because:

  • The main thread is most likely to be the one that finds itself in a situation where it has to respond synchronously. The worker thread is already doing with… well… threading, and thus it’s easier to adapt it to deal with the async call.

  • The main thread can often experience significant latency, and thus a synchronous ‘worker thread to main thread’ call can end up blocking your worker thread for a while.

However, that’s only a rough guideline. If you can guarantee that the main thread never calls the worker thread synchronously (either directly or indirectly) and that your main thread is always responsive, going the other way is feasible.

Taking a step back, I’m curious about the goal you described in your first post. Why does your background thread need to get progress information from your UI? Normally progress flows the other direction, that is, the UI updates based on progress being made by the background thread.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Quinn, the progress bar is used in a framework written long ago not by me. The apps that use it can only set the new progress value by getting its current value and adding 1 (and that all happens from worker threads). That's why the get is needed. I never liked this scheme, so I think I'll add a simple increment method, so all the work can be done async.