Hi Tom, 

I don't have an experience with libdispatch, but I want to follow your work 
here and learn. Which version/port of libdispatch are you using? Are you using 
the one provided here https://github.com/apple/swift-corelibs-libdispatch ? Or 
a different port? 

> On Aug 27, 2023, at 10:29 AM, Tom Sheffler <[email protected]> wrote:
> 
> 
> 
>> Date: Tue, 22 Aug 2023 08:37:24 -0700
>> From: Tom Sheffler <[email protected]>
>> To: [email protected]
>> Subject: AppKit, libdispatch and 100% CPU
>> Message-ID:
>>      <cambtmcui299u1if--r+5jrcgjsv13tgzqxxz4vf39dlce_k...@mail.gmail.com>
>> Content-Type: text/plain; charset="utf-8"
>> 
>> ...
> 
>> In an app that creates a window with AppKit and calls
>> dispatch_async(dispatch_get_main_queue, ...) I am seeing the CPU spike to
>> 100% when nothing is happening.  I first noticed in a small program that
>> had a background queue that wanted to update the display.  But I have
>> managed to illustrate the issue in a very simple program here:
> 
> 
> 
> A followup to my previous post about a use of AppKit with dispatch_async and 
> observing 100% CPU use.  I have an easy workaround, but thought I would 
> report what I learned along the way.
> 
> The use case is the situation when an operation on the non-main queue
> wishes to update a GUI element.  The canonical way to do this with dispatch is
> 
>    dispatch_async(dispatch_get_main_queue(), ^{
>      OpThatModifiesAppKitGuiWidgets();
>      });
> 
> However, when using this with [NSApp run], after the first call to
> dispatch_async to the main queue, the CPU goes to 100%.  If the call
> doesn't happen at first, the CPU remains low, but then rises after the
> first call.
> 
>    dispatch_after(5.0 SECONDS, non_main_queue, ^{
>      dispatch_async(dispatch_get_main_queue, ^{
>        OpThatModifiesAppKitGuiWidgets();
>        });
>      });
> 
> The CPU wouldn't go to 100% until after 5 seconds.
> 
> A straightforward way to fix this is to use performSelectorOnMainThread: 
> instead of a dispatch to the main queue.
> 
>    [self performSelectorOnMainThread:@selector(theGuiOp:)
>                   withObject:self
>                   waitUntilDone:NO];
> 
> It's easy enough to write something like performBlockOnMainThread: so that 
> the code
> above becomes something like this
> 
>    [self performBlockOnMainThread:^{
>      OpThatModifiesAppKitGuiWidgets();
>      });
> 
> and then I can have blocks and all is fine.  The app runs well and the
> CPU usage is low.  Use of non-main dispatch queues for background
> operations is fine too.
> 
> What do I think is happenening?
> 
> Inside NSSRunLoop, there are two calls to dispatch internals
> 
>  _dispatch_get_main_queue_handle_4CF()
>  _dispatch_main_queue_callback_4CF()
> 
> that give a handle to wait on and the function to run when its ready.
> I'm no expert in RunLoop internals, but it seems that after the first
> dispatch_async(main_queue()) the handle is always ready for reading
> and some sort of busy-wait is happening.  Even when I have not
> scheduled future events on the main_queue, it is woken up repeatedly.
> 
> -T
> 
> 
> 

Reply via email to