discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: AppKit, libdispatch and 100% CPU


From: dr_clow
Subject: Re: AppKit, libdispatch and 100% CPU
Date: Sun, 27 Aug 2023 14:53:58 -0500

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 <tom.sheffler@gmail.com> wrote:



Date: Tue, 22 Aug 2023 08:37:24 -0700
From: Tom Sheffler <tom.sheffler@gmail.com>
To: discuss-gnustep@gnu.org
Subject: AppKit, libdispatch and 100% CPU
Message-ID:
<CAMBtMcui299U1iF--r+5jrcgjSv13tGzQxxZ4Vf39dLce_k9zQ@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

[Prev in Thread] Current Thread [Next in Thread]