[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: AppKit, libdispatch and 100% CPU
From: |
Tom Sheffler |
Subject: |
Re: AppKit, libdispatch and 100% CPU |
Date: |
Sun, 27 Aug 2023 08:29:11 -0700 |
> 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