Threads completes

Hey, I’m creating a Thread that send a request to an external API with uploading and retrieving some files, I wondered how to know when the Thread is finished:

ApiThread* apiThread = new ApiThread(userAudioFile, apiResponseFile);
apiThread->startThread();
txtFileName = "Analyzing...";
resized();
apiThread->waitForThreadToExit(-1);
globalVariables.setRetrievedApiFile(apiResponseFile.getFullPathName());
repFunction(ComponentType::OutputMIDI);

now insted of this i would like to use a function like
whenFinished(){all the code after resized()}

how can i do it? here is my thread:

#pragma once
#include <JuceHeader.h>
#include "UtilityFunctions.h"

class ApiThread : public juce::Thread
{
public:
    // Constructor with parameters
    ApiThread(const File& userFile, File& responseFile)
        : Thread("API Thread"), userAudioFile(userFile), fileApi(responseFile) {}

    void run() override
    {
        // Call the sendToApi function within this thread
        bool result = UtilityFunctions::sendToApi(userAudioFile, fileApi);
        Thread::sleep(1000);

        // Handle the result, e.g., notify the UI
        if (result)
        {
            DBG("API call succeeded.");
        }
        else
        {
            DBG("API call failed.");
        }
    }

private:
    File userAudioFile;
    File fileApi;
};
1 Like

Fixed this using a MessageThread calling a giving function when done:

/*
  ==============================================================================
    ApiFunctions.h
    Created: 25 Aug 2024 8:14:10pm
    Author:  hilai
  ==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "UtilityFunctions.h"

class ApiThread : public juce::Thread
{
public:
    // Constructor with parameters
    ApiThread(const File& userFile, File& responseFile, std::function<void(bool)> callback)
        : Thread("API Thread"), userAudioFile(userFile), fileApi(responseFile), onFinishedCallback(callback) {}

    void run() override
    {
        // Call the sendToApi function within this thread
        bool result = UtilityFunctions::sendToApi(userAudioFile, fileApi);
        Thread::sleep(1000);

        // Handle the result
        if (result)
        {
            DBG("API call succeeded.");
        }
        else
        {
            DBG("API call failed.");
        }

        // Call the callback function on the message thread
        MessageManager::callAsync([this, result]() {
            if (onFinishedCallback)
                onFinishedCallback(result);
            });
    }

private:
    File userAudioFile;
    File fileApi;
    std::function<void(bool)> onFinishedCallback;
};

here:

    // Create and start the ApiThread
    apiThread = make_unique<ApiThread>(userAudioFile, apiResponseFile,
        [this, apiResponseFile](bool success) {
            whenThreadFinished(success, apiResponseFile);
        });

    apiThread->startThread();
    txtFileName = "Analyzing...";
    resized();



void ImportVocals::whenThreadFinished(bool success, const File& apiResponseFile)
{
    if (success)
    {
        DBG("API call succeeded. moving to output");
        globalVariables.setRetrievedApiFile(apiResponseFile.getFullPathName());
        repFunction(ComponentType::OutputMIDI);
    }
    else
    {
        // Handle failure
        txtFileName = "Analysis failed";
        resized();
    }
}
1 Like

Look into using std::future or juce::WaitableEvent. These both allow you to suspend a thread until it is woken-up by another one.

std::async might be a better option for your threading needs here–it will allow you to write things a lot more succinctly.

std::future<int> f = std::async([]()  // async returns a future automatically
   {
      // do stuff here
      return 4;
   });

  int i = f.get();   // .get() waits for the std::future to resolve and returns the value

Also, look out for the memory leak on the first line of your code; use std::unique_ptr here instead of raw pointers.

1 Like

f.get() blocks the thread until the task is done if i remember right. This is the same as a synchronous call in my opinion.
Some kind of await that waits without blocking the thread would be required.

so what should i use? the thread itself is just an upload and download of files… i did it so in the meantime i could still update the ui

I think Kunz is right, waiting for the thread to finish is not the right approach here (I was misunderstanding what you were trying to do when I suggested std::future). Using MessageManager::callAsync is probably the easiest approach here. If it were me, I would still use std::async instead of juce::Thread here, just because the setup is much easier–you don’t need to define a class, and can just use a simple lambda.

Be careful though because this line is unsafe:


        MessageManager::callAsync([this, result]() {
            if (onFinishedCallback)
                onFinishedCallback(result);
            });

You’re capturing this and then running it at a later date, but it’s possible that this has been deleted since then. To solve this, you’ll probably need to use some shared_ptr or weak_ptr.

2 Likes

This problem is solved when using a juce::ThreadPool instance on the class that has the callback function. It will wait until the thread/job is finished when destruction happens.