Regarding runDispatchLoop, the documentation says :
Also, I am a bit surprised it works to create and show the AlertWindow in your thread’s run() method. Generally, the Juce GUI objects are not designed to be used in other threads than the GUI/main thread.
Yes, I saw the note about using runDispatchLoop() on application startup. In my limited knowledge, I did not know of another way to initiate the thread. How would the thread be launched?
I moved the AlertWindow to the thread’s run() method in an attempt to simplify everything. I realize that is probably sketchy. It can easily be moved back to runTaskWithProgressBar().
I am sure this has a relatively simple solution. I have tried a number of things, but I do not have sufficient knowledge of threads to find the right code.
Make your AlertWindow and progress into class data members, to ensure that they will live at least as long as the long-running task.
When job is ready to start, call enterModalState on the alert window, and set the progress to zero, all from the message thread.
Run the task on a background thread, querying the progress periodically. You can use MessageManager::callAsync to update the progress variable on the main thread.
Once the task is complete, you can use MessageManager::callAsync again to signal that the job is done, and launch a new AlertWindow.
Your main problem seems to be that you apparently want to have a stand alone function (the runTaskWithProgressBar) that would handle everything locally inside the function. But that’s not really feasible to do when following the new recommended async/non-modal model in Juce. You need to have some object that holds both the AlertWindow and the thread for the duration of the thread task. (You could maybe hack together something with heap allocations and self-destructing objects in order to run it all within a stand alone function, but that feels a bit iffy to do…)
Well, similar to another code snippet you posted before, the main issue is that your TaskRunner is declared locally in a method that returns immediately (on purpose). In that moment the TaskRunner will be destroyed and the Task is terminated.
The TaskRunner stops the task in the destructor, that’s why you don’t see progress.
I don’t know why the window doesn’t immediately disappears though.
Try making the TaskRunner a member of the parent class.
vcruntime140d.dll!memcpy_repmovs() Line 40
at D:\agent\_work\61\s\src\vctools\crt\vcruntime\src\string\amd64\memcpy.asm(40)
[Inline Frame] TheDAW.exe!juce::ArrayBase<tracktion_engine::Clip *,juce::DummyCriticalSection>::addArrayInternal(tracktion_engine::Clip * const *) Line 406
at C:\SDKs\JUCE\modules\juce_core\containers\juce_ArrayBase.h(406)
[Inline Frame] TheDAW.exe!juce::ArrayBase<tracktion_engine::Clip *,juce::DummyCriticalSection>::addArray(tracktion_engine::Clip * const *) Line 283
at C:\SDKs\JUCE\modules\juce_core\containers\juce_ArrayBase.h(283)
TheDAW.exe!juce::Array<tracktion_engine::Clip *,juce::DummyCriticalSection,0>::Array<tracktion_engine::Clip *,juce::DummyCriticalSection,0>(const juce::Array<tracktion_engine::Clip *,juce::DummyCriticalSection,0> & other) Line 71
at C:\SDKs\JUCE\modules\juce_core\containers\juce_Array.h(71)
TheDAW.exe!tracktion_engine::Renderer::Parameters::Parameters(const tracktion_engine::Renderer::Parameters & __that) Line 40
at C:\SDKs\TracktionEngine\modules\tracktion_engine\model\export\tracktion_Renderer.h(40)
TheDAW.exe!tracktion_engine::Renderer::RenderTask::RendererContext::RendererContext(tracktion_engine::Renderer::RenderTask & owner_, tracktion_engine::Renderer::Parameters & p, tracktion_engine::AudioNode * n, juce::AudioFormatWriter::ThreadedWriter::IncomingDataReceiver * sourceToUpdate_) Line 191
at C:\SDKs\TracktionEngine\modules\tracktion_engine\model\export\tracktion_Renderer.cpp(191)
[External Code]
TheDAW.exe!tracktion_engine::MessageThreadCallback::handleAsyncUpdate() Line 201
at C:\SDKs\TracktionEngine\modules\tracktion_engine\utilities\tracktion_AsyncFunctionUtils.h(201)
[Inline Frame] TheDAW.exe!juce::InternalMessageQueue::dispatchMessage(juce::MessageManager::MessageBase *) Line 198
at C:\SDKs\JUCE\modules\juce_events\native\juce_win32_Messaging.cpp(198)
TheDAW.exe!juce::InternalMessageQueue::dispatchMessages() Line 240
at C:\SDKs\JUCE\modules\juce_events\native\juce_win32_Messaging.cpp(240)
TheDAW.exe!juce::InternalMessageQueue::dispatchNextMessage(bool returnIfNoPendingMessages) Line 124
at C:\SDKs\JUCE\modules\juce_events\native\juce_win32_Messaging.cpp(124)
[Inline Frame] TheDAW.exe!juce::dispatchNextMessageOnSystemQueue(bool) Line 266
at C:\SDKs\JUCE\modules\juce_events\native\juce_win32_Messaging.cpp(266)
[Inline Frame] TheDAW.exe!juce::MessageManager::runDispatchLoop() Line 107
at C:\SDKs\JUCE\modules\juce_events\messages\juce_MessageManager.cpp(107)
[Inline Frame] TheDAW.exe!juce::JUCEApplicationBase::main() Line 262
at C:\SDKs\JUCE\modules\juce_events\messages\juce_ApplicationBase.cpp(262)
TheDAW.exe!WinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, char * __formal, int __formal) Line 103
at C:\AAXProjects2\TheDAW\Source\Main.cpp(103)
[External Code]
Thank you. I will probably do that after trying a few more things.
It is frustrating because it works just fine as a modal dialog, and has been for a couple of years. But I am refactoring my code to remove modal loops and this is one of the last ones to figure out.
I imagine the ThreadPoolJobWithProgress is getting deleted when the runTaskWithProgressBar function returns?
Do you have to make this non-modal now?
I appreciate modal loops are bad and we should not be using them in new code but this codebase has relied on them for 20 years so it’s not simple to just remove them. We’ll probably need to add alternative methods that request a task to be started and provide a callback to be notified when it ends.
There might be some way to hack it in the mean time but I don’t really have the bandwidth to look in to it right now I’m afraid.
I’m also stuck with this problem for different reasons - unit testing.
TEST_CASE("Testing async calls")
{
juce::ScopedJuceInitialiser_GUI gui;
int x = 0;
juce::MessageManager::callAsync([&x = x] { x = 3; });
//This following section used to be:
//juce::MessageManager::getInstance()->runDispatchLoopUntil(1);
//I tried to replace it with:
while(x == 0)
{
juce::MessageManager::getInstance()->runDispatchLoop();
juce::Thread::sleep(1);
//But, that loop never ends...
}
REQUIRE(x == 3);
}
My understanding is that there is no simple way to achieve the functionality of juce::MessageManager::getInstance()->runDispatchLoopUntil(1);, so you must use that, along with the JUCE_MODAL_LOOPS_PERMITTED = 1 flag for the time being.
Just tested and the above example isn’t working for me on Mac unfortunately. The message loop isn’t actually running when calling runDispatchLoop() so in the above example x remains at 0.
Can anyone from the JUCE team reply if there’s a way to manually do that without modal loops?
Yeah same here, works on Linux, not on my Mac. It seems [NSApp run] is not starting the loop properly, with NSApp being nil. This is problematic for me as I need to run unit tests in this way. If anyone has updates let me know