Spinning wheel stuck - writeTo(file) inside Thread::launch

Hi there,

I am saving the state in a new thread. The windows spinning wheel appears and doesn’t go away even after the saving is done. Unless I move the mouse cursor off the UI once, then it disappears. Maybe someone can point out the issue to me and show me how to go about doing this?
I should mention that I never call showWaitCursor() anywhere but when I call hideWaitCursor() after saving is done the spinning wheel doesn’t get stuck.

void JuceProcessor::save(File newFile)
{
	Thread::launch([this, newFile]()
		{
			std::unique_ptr<XmlElement> xml(Parameters.state.createXml());

			XmlElement::TextFormat frmt;
			frmt.dtd = String("");
			frmt.customEncoding = "UTF-8";
			frmt.lineWrapLength = 80;			
			
			xml->writeTo(newFile, frmt);
		});

}

Do you know what causes the wait cursor to be shown initially? If you’re not manually changing the cursor, then perhaps Windows is noticing that the main thread has stalled for some reason and is updating the cursor automatically.

You mention that you’re not calling showWaitCursor(), but maybe this is being called internally by the framework. You could check whether the wait cursor is being shown for some other reason by putting a breakpoint on the WaitCursor case in MouseCursor::PlatformSpecificHandle::makeHandle() at the bottom of juce_Windowing_windows.cpp. If the breakpoint is hit, then you can inspect the call stack to find out where the call is coming from.

Is your project a plugin or a standalone app? If it’s a plugin, maybe the host is updating the cursor for some reason. It would be interesting to test out other hosts, and to check whether you see the same behaviour in all cases.

1 Like

I think I’ve found the cause. This is happening because I’m calling the save function directly from a pop up menu.

FilePopMenu.showMenuAsync(
				juce::PopupMenu::Options(), [this](int result)
				{
					if (result == 1)
                       {...processor.save(newFile);}
                });

Is doing something like this wrong?

Is there a way to launch a new save/load thread safely after picking an option from the pop up menu? Because saving or loading without launching a new thread freezes the UI. Maybe someone can tell me what’s the correct approach or workaround. Or maybe what I’m doing is completely fine and I should just slap a hideWaitCursor() at the end of the thread and be done with it? I just don’t wanna hide the possible underlying issue.

I also have that issue, the spinner wheel shows and to make it switch to normal I have to move it outside of the UI window. It happens after I click on a menu item in an opened PopupMenu and then the menu hides.
I was not able to catch any calls in MouseCursor::PlatformSpecificHandle::makeHandle() in juce_Windowing_windows.cpp.

I have to say, that it does not happen every time, it is inconsistent, every 5-10th try.

menu-spinner

Hey,

I made a minimal working example here. Could you please take a look at it?
The problem is - after selecting an item from ComboBox, handling a callback that launches a new thread, the cursor is left in Wait state. It is possible to trigger by clicking on the combo box, then clicking on the menu item. But it happens much often if you click and hold on the combo box and release the mouse button on the menu item.

Thanks.

I can’t comment on the exact problem, but it does look like the code in run () is questionable. Since sleep cannot be woken up (per the documentation), you can easily end up in a situation where you are sleeping for 10ms, but asking stopThread () to only wait for 1ms before resorting to killing the thread. Killing a thread is a last ditch effort that should be avoided. Does the same problem occur if you use wait () instead of sleep ()? Generally speaking, if you want to call stopThread (), you should write your code in run () to be able exit cleanly when threadShouldExit () returns true. There are times where this is difficult because you have called something outside of your control which takes a long period of time, but there are patterns to handle to mitigate the need to kill threads.

Again, I am not saying this is your problem, but the test app is questionable because of this.

The code in the run() is just illustrative, it could be any heavy work, writing to a file, calculating fft and prefiltering something.

It has the same effect if I use wait() instead of sleep().

It also happens when I leave the run() body empty.

As it was meant to be illustrative, it also added data to be considered. So it’s good to have that piece removed from the equation! :slight_smile: The next question is why is the cursor even changing states? Like the OP, maybe you could put a breakpoint MouseCursor::showWaitCursor(), and see why it is even happening?

I already did that before posting here. The breakpoint at that point is not hit when this happens, that’s why I have no clue.

It seems likely that Windows itself is displaying the wait cursor. Which it does when the UI thread is blocked for a “long time". I’m may be stating things you have already considered, but not mentioned in this discussion, but it’s helpful to state what we can discern. Since it seems to be an issue with the UI thread being blocked too long, I would probably want to profile the code to see where the time is being spent. Since it is a tiny application that can recreate the issue, you could start by simply inserting some manual measurement code around the suspect areas, such as the thread code in comboBoxChanged

Try lowering the thread priority to “low” instead of leaving it on normal. We do this routinely for ANY threads to avoid blocking the UI thread. Even on “low,” the thread executes in real-time; it’s just yielding to the UI thread when it needs to.

Thanks for the tip, interesting insight! However lowering the priority did not help in this case. I get more or less the same behavior with all thread priorities.

Instead of stopping and starting the thread in the combobox change event, create a thread pool and post a job from there

1 Like

Can you please check if I am doing it right? juce_WaitCursorTest/Source/MainComponent.cpp at a760143a2a8ad4d740e631f30e86b358d132ee34 · TheMates/juce_WaitCursorTest · GitHub

I can confirm, that adding a job to threadpool instead of starting a new thread fixes that issue.

Seems fine, but why all that code? Just post a lambda to the thread pool

1 Like