I’m experimenting with setting up one-shot threads (threads that only execute something once then quit) and am having trouble getting it to work. the threads run correctly, but i’m not sure how to shut them down after they complete. The idea was that I could run lambdas on their own threads (great for client-server messaging) without needing to write classes for each type of thread. Perhaps this is a noob approach but i’m curious how to do it properly.
class Receiver {
public:
Receiver() {}
~Receiver() {}
void callback(String result) { DBG( "Receiver::callback() result: \n" + result ); }
};
class AutoDeleteThread : public Thread {
public:
AutoDeleteThread(std::function<bool()> task ) :Thread("AutoDeleteThread") {
m_task = task;
startThread();
}
~AutoDeleteThread() {
DBG("Shutting down AutoDeleteThread");
stopThread(500);
}
void run() override {
if( m_task() ) { DBG("OK"); }
else { DBG("failed"); }
delete this; //throws JUCE assertion, because you can't call stopThread() on yourself
}
private:
std::function<bool()> m_task;
};
You probably need to have a ChangeListener in the class which creates AutoDeleteThread then have AutoDeleteThread sendMessage() when it’s done and in the changeListenerCallback() delete the thread…
when I stepped thru the code in the xcode debugger, the AutoDeleteThread was deleted without doing that. I’m wondering about using that ‘new’ operator like that, and if there’s a way to do it without it. The whole idea is to not have to even worry about using threads as class members if they’re only executing one time, but I need it to not execute on the Message Thread.
edit. spoke too soon. leaks when I shut down the test. The thread stops running, but it isn’t deleted. hmm…
Ok, I post my ReferenceCountObject solution as well:
class OneShotThread : public Thread, public ReferenceCountedObject {
// identical except run:
void run() override
{
OneShotThread::Ptr holdMe (this);
while (! threadShouldExit())
{
doSomething();
}
sendChangeMessage();
}
// and the typedef for the ReferenceCountedObject:
typedef ReferenceCountedObjectPtr<OneShotThread> Ptr;
You have two references, one where you actually create it and the other one inside the execution you want to finish. Only when both references are gone, the object will self destruct. So you are on the safe side, no matter how long the thread runs (could be very short as well?)
No manual delete, no timer or anything needed…
EDIT: sorry, works for matkatmusic’s version as well:
And no point in calling “signalThreadShouldExit();” if you don’t use the while loop, because all it does is to set the threadShouldExit() semaphore to allow a clean exit of the threads loop.
But you don’t have to loop, you just cannot interrupt then, only killing…
Rail, can you explain your need for doSomething()?
In your code, doSomething() would never be called, because no outside objects change m_bRunning from false to true;
Well you can just call the task from run() but how will you handle if the thread is asked to quit externally? My code only runs the task once then stops the thread. In a perfect world the task should have a callback to quit prematurely too.
if you look at my original post, you’ll see that i’m talking to a server with a 10-second timeout as the initial task I’m trying to queue up and run once. AFAIK, there’s no way to shut down URL::createInputStream() once you start it running.
I’m all for making this a generic tool anyone can use, but in my particular use case, I just need it to fire off these server connections one time and if they go thru, pass it to a receiver. if they don’t, then report ‘false’