Here is a first version of my threading class. It works fine in windows but unfortunately does not compile in OS X. It uses the GetExitCodeThread() WIN32 API function that is not supported by swell. Is there an alternative way to check the thread status that can be implemented with IPlug/swell?
I need to know the thread status because otherwise the thread object might be destroyed before the tread is properly exited.
Any hints are most welcome. Thanks!
The sb_thread class encapsulates all the complicated stuff and makes thread management available through simple start() and end functions().
Usage is quite straightforward - All You have to do is inhertit from that class and implent the doStuff() member function that runs within the daughter thread.
Note that the thread function does not have to be a global function or a static member function. This is all sorted out by the thread object.
This implementation is lock free. Instead it uses atomic shared variables to send commands and to signal the state of the thread.
have fun
Andreas
As an example here is dummy sample loader that uses the sb_thread object.
I would recommend to add the sample loader (or your customized thread object) as a member object to the plugin class.
start() can be called in Plugin::Resume(). (do not bloat the Plugin constructor by calling loader.start() there.)
There is no need to explicitly end the daughter thread. This is taken care of by the Plugin destructor.
Code:
//-- sb_dummySampleLoader declaration------------------------------
class sb_dummySampleLoader : public sb_thread // inherit!
{
public:
sb_dummySampleLoader() : sampleIdx(0), sampleLoadDone(false) {isVirtual = false;}
~sb_dummySampleLoader(){isVirtual = true;}
// This function is called by the main thread to trigger a
// sample swap in the loader thread.
void setNewSampleIdx( int newSampleIdx );
// thread function - must be implemented.
void doStuff();
// all variables used by both threads need to be declared volatile
// _and_ they must be atomic (only then the communication can be lock free.)
volatile bool sampleLoadDone;
volatile int sampleIdx;
};
//-- sb_dummySampleLoader implementation --------------------------
void setNewSampleIdx( int newSampleIdx )
{
if( sampleIdx != newSampleIdx ){
sampleIdx = newSampleIdx;
// Signal to the loader thread that there is some work to do.
sampleLoadDone = false;
// TODO: mute the audio process - access to the
// sample table is not possible now.
}
}
void sb_dummySampleLoader::doStuff()
{
while(true){
// do stuff
if (!sampleLoadDone){
//
// Load new sample[sampleIdx]
//
MessageBox(
NULL,
"New Sample Now Loading.\n(err, not quite yet.)",
"IPlug Example Synth",
0);
sampleLoadDone = true; // was set false by the request.
}
Sleep(200);
if(switchOff) break;
}
}
Here is the sb_thread function that does the magic:
Code:
//-- sbThread declaration ----------------------------------------
class sb_thread
{
public:
sb_thread() : hnd(NULL), switchOff(true), isVirtual(true) {}
~sb_thread(){
end();
}
void start();
void end();
protected:
volatile bool isVirtual;
volatile bool switchOff;
virtual void doStuff() = 0;
private:
static DWORD WINAPI threadProc( LPVOID pItem_ );
HANDLE hnd;
DWORD exitCode;
};
//-- sb_thread implementation ----------------------------------
void sb_thread::start(){
// create the new thread only once
if (hnd == 0){
// reset state variables
exitCode = 0;
switchOff = false;
hnd = CreateThread( NULL, 0, threadProc, this, 0, NULL);
}
}
void sb_thread::end(){
switchOff = true;
// wait until thread is finished!
// This is crucial since otherwise it is not guaranteed that
// all objects created by the daughter thread are destroyed.
// (that is when end() is called from ~sb_thread().)
if (hnd != 0){
WaitForSingleObject(hnd ,INFINITE);
CloseHandle(hnd);
hnd = 0;
}
}
DWORD WINAPI sb_thread::threadProc( LPVOID pThis_ ){
sb_thread* pThis = (sb_thread*)pThis_;
if(!pThis->isVirtual) pThis->doStuff();
return 1;
}