0

So far, I couldn't find any mention about this feature. AFAIK, by definition, any GCD queue can run in main thread. If a GCDQ with some heavy task selects main thread to execute on, it'll affect something bad to UI. (I am talking about DispatchQueue.async case)

Typical workaround for this situation is skipping task if current thread is detected as main thread. But this wastes system resources and introduces unnecessary complexity. I believe there must be a declarative way to do this.

Is there any better way? I have various sized tasks that shouldn't run on main thread. I tried to spawn a separated thread, but it didn't worked well, because macOS lacks unnamed semaphore. Though DispatchSemaphore is implemented using unnamed semaphores, I am not sure whether it's safe to use DispatchSemaphore with pthread stuffs as it's GCD level object.

eonil
  • 83,476
  • 81
  • 317
  • 516

1 Answers1

0

So far, the best way would be setting quality-of-service level to background.

According to this document;

... the system runs items on a thread whose priority is set for background status. ...

And this document says that "main thread"'s default QoS is set to "user-interactive" for GUI apps.

... In an app, the main thread runs at a QoS level of user-interactive. ...

It's unclear whether the QoS level can be changed later. Anyway, with default settings, the system won't send GCD tasks with lower QoS levels to main thread. If there's no such thread available, system would spawn a new one.

This is my thoughts, and I still couldn't find explicit mention for this from official documentations.

eonil
  • 83,476
  • 81
  • 317
  • 516
  • 2
    Note that the `.background` QoS may be blocked for hours or days depending on the system power situation. You should not put things on `.background` unless you mean "it's ok if this task never actually runs." Generally you mean `.utility` (and even more often, you just mean `.default`). As a rule, you should not think about threads in GCD, and definitely do not try to work around the queue system to target specific threads. Put your blocks on the appropriate queue (using `DispatchQueue.global()`), and the system will generally do the right thing. – Rob Napier Jan 26 '19 at 15:49
  • If you want a deeper understanding, see the WWDC videos on the topic: https://developer.apple.com/videos/play/wwdc2017/706/ https://developer.apple.com/videos/play/wwdc2016/720/ – Rob Napier Jan 26 '19 at 15:49
  • @RobNapier Thanks for the note. I'll try `.utility` QoS level too. I know that thread and GCD are at different levels. The problem is non-UI tasks that potentially can run longer. Regardless of good or bad, GCD on Apple platforms are implemented on top of pthread. Therefore, they cannot escape from pthread rules. With current definition of GCD, global queue or any private serial queue can send tasks into main thread, and if any of them takes >100ms, it'll degrades UX quality seriously. Therefore, I need a way to *guarantee* to never send specific tasks into main thread. – eonil Jan 26 '19 at 22:54
  • I wouldn't worry if Apple explicitly state that global/serial queues will never be executed in main-thread, So far unfortunately, I couldn't find such mention. I'll take a look at the videos. I hope I can find some mentions here. – eonil Jan 26 '19 at 22:56
  • @RobNapier I couldn't find any mention about prohibition of running non-main-queue tasks in main thread. So far, QoS seems to be the only solution. – eonil Jan 26 '19 at 23:16
  • Blocks dispatched to a background queue can absolutely run on the main thread if they're enqueued using dispatch_sync on the main queue. That's a good thing. It prevents context switching, and the main queue will donate its priority to the block to make sure it's scheduled as quickly as possible. As a rule, however, you should never do that. But, no, the system doesn't guarantee that you won't write code that tells it to do that. In normal cases, however, this is not a problem. – Rob Napier Jan 26 '19 at 23:16
  • @RobNapier Of course, I'm talking about `DispatchQueue.async`. Synchronous call is not in my scenario now. I'll update my question. – eonil Jan 26 '19 at 23:19
  • Performing a dispatch_async to a global queue will not run on the main thread. That doesn't mean it won't impact the UI, but no, it'll never wind up on the main thread. That's mostly an implementation detail, however. If you're thinking hard about threads, you're going to get confused in GCD. Think about queues. If you dispatch to a queue that does not target the main queue, it's not going to jump to the main queue. But there are definitely ways to create non-main queues that *do* target the main queue. So again, there's no guarantee that if you ask it to run on the main thread that it won't. – Rob Napier Jan 26 '19 at 23:22
  • But the global queues do not target the main queue, and you can't change their targets. So using the global queues asynchronously will never cause the block to itself run on the main thread. But this isn't because there's an overarching guarantee. It's just that there's nothing that would *cause* it to run on the main thread. The system doesn't say "guarantee" because there are lots of ways to cause lots of things to be different if you ask for it. – Rob Napier Jan 26 '19 at 23:23
  • @RobNapier By definition, nothing prevents global/serial queue to send async blocks to main *thread* unless there's no QoS is specified. They'll never send blocks to main *queue*, but as like you mentioned, *queue and thread are different*. Though I am not sure, but AFAIK, this actually happens in real devices. Why do you believe global/serial queue never send blocks to main thread? Can I ask some reference? – eonil Jan 26 '19 at 23:26
  • @RobNapier The core issue is multiple GCD queue can run in single (including main) thread as they do not care which thread they are running. – eonil Jan 26 '19 at 23:28
  • Yes, GCD queues can be configured to target the main queue, which runs on the main thread. No GCD queue that does not target the main queue, however, will be put on the main thread absent a synchronous request from the main thread. If this weren't true, most iOS apps wouldn't work. Have you run into actual code that is causing you a problem here? Or are you just nervous that Apples docs have grown quite lousy over the years and it does not specify things like an ISO document? – Rob Napier Jan 26 '19 at 23:29
  • @RobNapier Thanks for confirmation. Apps will work fine even with some blocks sent to main thread but with unexpected slowdown sometimes as long as they follow good GCD practices. Or they could be QoS controlled. Therefore existing apps can't be a proof of such "main-thread prevention". – eonil Jan 26 '19 at 23:40