Thursday, April 26, 2007

Threaded Functions

Here's an interesting thought experiment for functions that get executed in a separate thread.


void doit( int i ) {
printf( "Hello from doit! i: %d\n", i );
}




void doit2( Thread* th, int i ) {
printf( "Hello from doit2! i: %d, th: %p, tid: 0x%04x \n", i, th, th->getThreadID() );

}

class Snarfy {
public:
void thisBlows( int g ) {
printf( "Hello from thisBlows! i: %d, this ptr: %p\n", g, this );
}
};


class Swanky {
public:
void doit( double& d, const String& s ) {
printf( "Hello from Swanky::doit! d: %0.3f, s: %s, this ptr: %p\n",
d, s.ansi_c_str(), this );
}


void doit2( Thread* th, double& d, const String& s ) {
printf( "Hello from Swanky::doit! d: %0.3f, s: %s, this ptr: %p\n",
d, s.ansi_c_str(), this );

for (int i=0;i<10;i++){
th->sleep(1000);
}
}
};

int main( int argc, char** argv ){

FoundationKit::init( argc, argv );



Thread* th = ThreadedProcedure1<int>(10,doit);

th->wait();

th = ThreadedProcedure1<int>(231,doit2);

th->wait();


Snarfy sn;

th = ThreadedProcedure1<int,Snarfy >(&sn,38112,&Snarfy::thisBlows);
th->wait();



String s = "hello";
Swanky sk;

double d = 0.0332;

th = ThreadedProcedure2< double&,const String&, Swanky >(&sk,d,s,&Swanky::doit2);
th->wait();


printf( "Bye!\n");

FoundationKit::terminate();
return 0;
}


The actual implementation gets a bit long winded, but looks something like this:




template <typename ParamType1>
class NullClassType1 {
public:
void m(ParamType1){}
void m(Thread*, ParamType1){}
};


template <typename ParamType1, typename ClassType=NullClassType1<ParamType1> >
class ThreadedProcedure1: public Runnable {
public:

typedef NullClassType1<ParamType1> NullClassType;

typedef void (*ProcPtr1)(ParamType1 p1);
typedef void (*ProcThreadPtr1)(Thread* thread, ParamType1 p1);

typedef void (ClassType::*ClassProcPtr1)( ParamType1 p1 );
typedef void (ClassType::*ClassThreadProcPtr1)( Thread* thread, ParamType1 p1 );



ThreadedProcedure1( ParamType1 p1, ProcPtr1 procPtr ): param1_(p1),
runningThread_(NULL),
procPtr_(NULL),
procThreadPtr_(NULL),
classProcPtr_(NULL),
classThreadProcPtr_(NULL),
instancePtr_(NULL){

ThreadedProcedure1<ParamType1,ClassType>* params =
new ThreadedProcedure1<ParamType1,ClassType>(p1);

params->procPtr_ = procPtr;

runningThread_ = new Thread( params, true, true );
params->runningThread_ = runningThread_;

runningThread_->start();
}

ThreadedProcedure1( ParamType1 p1, ProcThreadPtr1 procPtr ): param1_(p1),
runningThread_(NULL),
procPtr_(NULL),
procThreadPtr_(NULL),
classProcPtr_(NULL),
classThreadProcPtr_(NULL),
instancePtr_(NULL){

ThreadedProcedure1<ParamType1,ClassType>* params =
new ThreadedProcedure1<ParamType1,ClassType>(p1);

params->procThreadPtr_ = procPtr;

runningThread_ = new Thread( params, true, true );
params->runningThread_ = runningThread_;

runningThread_->start();
}

ThreadedProcedure1( ClassType* src, ParamType1 p1, ClassProcPtr1 procPtr ): param1_(p1),
runningThread_(NULL),
procPtr_(NULL),
procThreadPtr_(NULL),
classProcPtr_(NULL),
classThreadProcPtr_(NULL),
instancePtr_(NULL) {

ThreadedProcedure1<ParamType1,ClassType>* params =
new ThreadedProcedure1<ParamType1,ClassType>(p1);

params->classProcPtr_ = procPtr;
params->instancePtr_ = src;

runningThread_ = new Thread( params, true, true );
params->runningThread_ = runningThread_;

runningThread_->start();
}


ThreadedProcedure1( ClassType* src, ParamType1 p1, ClassThreadProcPtr1 procPtr ): param1_(p1),
runningThread_(NULL),
procPtr_(NULL),
procThreadPtr_(NULL),
classProcPtr_(NULL),
classThreadProcPtr_(NULL),
instancePtr_(NULL){

ThreadedProcedure1<ParamType1,ClassType>* params =
new ThreadedProcedure1<ParamType1,ClassType>(p1);

params->classThreadProcPtr_ = procPtr;
params->instancePtr_ = src;

runningThread_ = new Thread( params, true, true );
params->runningThread_ = runningThread_;

runningThread_->start();
}







protected:

ThreadedProcedure1( ParamType1 p1 ): param1_(p1),
runningThread_(NULL),
procPtr_(NULL),
procThreadPtr_(NULL),
classProcPtr_(NULL),
classThreadProcPtr_(NULL),
instancePtr_(NULL){
}

public:
virtual bool run() {


if ( typeid(ClassType) == typeid(NullClassType) ) {
if ( NULL != procThreadPtr_ ) {
(*procThreadPtr_)( runningThread_, param1_ );
}
else if ( NULL != procPtr_ ) {
(*procPtr_)( param1_ );
}
else {
return false;
}
}
else {
if ( NULL != instancePtr_ ) {
if ( NULL != classThreadProcPtr_ ) {
(instancePtr_->*classThreadProcPtr_)( runningThread_, param1_ );
}
else if ( NULL != classProcPtr_ ) {
(instancePtr_->*classProcPtr_)( param1_ );
}
else {
return false;
}

}
}


return true;
}

virtual void stop(){}


operator Thread* () {
return runningThread_;
}
protected:

ParamType1 param1_;
Thread* runningThread_;
ProcPtr1 procPtr_;
ProcThreadPtr1 procThreadPtr_;
ClassProcPtr1 classProcPtr_;
ClassThreadProcPtr1 classThreadProcPtr_;
ClassType* instancePtr_;
};





This allows us to attach a function and execute it in a separate thread, using the various VCF thread classes, such as VCF::Thread and VCF::Runnable to implement it. I'll leave it as an exercise for the reader to add additional arguments. If people like this enough we'll probably put this into the FoundationKit proper.