I have the following manager<->worker situation:
class Manager {
private:
pthread_attr_t workerSettings;
pthread_t worker;
pthread_cond_t condition;
pthread_mutex_t mutex;
bool workerRunning;
static void* worker_function(void* args) {
Manager* manager = (Manager*)args;
while(true) {
while(true) {
pthread_mutex_lock(&manager->mutex);
if(/* new data available */)
{
/* copy new data from shared to thread memory */
pthread_mutex_unlock(&manager->mutex);
}
else
{
pthread_mutex_unlock(&manager->mutex);
break;
}
/* process the data in thread memory */
pthread_mutex_lock(&manager->mutex);
/* copy results back to shared memory */
pthread_mutex_unlock(&manager->mutex);
}
pthread_mutex_lock(&manager->mutex);
// wait for new data to arrive
while(manager->workerRunning && !/* new data available*/)
pthread_cond_wait(&manager->condition, &manager->mutex);
// check if we should continue running
if(!manager->workerRunning)
{
pthread_mutex_unlock(&manager->mutex);
break;
}
pthread_mutex_unlock(&manager->mutex);
}
pthread_exit(NULL);
return NULL; // just to avoid the missing return statement compiler warning
}
public:
Manager() : workerRunning(true) {
pthread_cond_init(&condition, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_attr_init(&workerSettings);
pthread_attr_setdetachstate(&workerSettings, PTHREAD_CREATE_JOINABLE);
pthread_create(&worker, &workerSettings, worker_function, (void*)this);
}
// this *may* be called repeatedly or very seldom
void addData(void) {
pthread_mutex_lock(&mutex);
/* copy new data into shared memory */
pthread_cond_signal(&condition);
pthread_mutex_unlock(&mutex);
}
~Manager()
{
// set workerRunning to false and signal the worker
pthread_mutex_lock(&mutex);
workerRunning = false;
pthread_cond_signal(&condition);
pthread_mutex_unlock(&mutex);
// wait for the worker to exit
pthread_join(worker, NULL);
// cleanup
pthread_attr_destroy(&workerSettings);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&condition);
}
};
I'm not completely sure about this at several places:
Is the fact that Manager spawns a new thread in its constructor considered a bad practice? (I will only have one Manager object, so i guess that should be fine)
What about the pthread_exit - i see this in many tutorials but i don't quite get why it should be there? Can't i simply return the function to exit the thread? I also think the return NULL is dead code, but gcc warns when it's missing because it obviously can't know that pthread_exit already killed the thread at that point.
About the constructor - can i destroy the thread attr object (workerSettings) immediately after spawning the thread or does it have to stay valid for the entire lifetime of the thread?
About the destructor: Is this the right way to do this?
And most importantly:
Do your experienced eyes see any synchronization issues there?
Thanks for your help!
You ask...
Is the fact that Manager spawns a new thread in its constructor considered a bad practice?
In most cases, RAII is good enough to approach the object creation and resource acquisition. In some cases you may want to achieve the deferred resource initialization: when you first construct an object and later you proceed with the initialization. This can be achieved, for example, via a ctor (either default or parameterized) and open/start routines. Though you may also do it in the ctor and achieve the deffered object creation by allocating the object in the process heap (via operator new). It depends on your requirements, software design considerations and corporate software development standards.
So, you may create a thread in ctor, or may want or need to spawn it in the later stage of the application/object lifecycle.
What about the pthread_exit
It is not required. It terminates the calling thread, making its exit status available to any waiting threads (i.e. via pthread_join()). An implicit call to pthread_exit() occurs when any thread returns from its start routine. Basically, the pthread_exit() function provides an interface similar to exit() but on a per-thread basis (including cancelation cleanup handlers). But beware of calling pthread_exit() from cancelation cleanup handlers or from destructors of objects allocated in the TSD (thread-specific data area) - it can lead to undesirable side effects.
About the constructor - can i destroy the thread attr object (workerSettings) immediately after spawning the thread or does it have to stay valid for the entire lifetime of the thread?
Yes, you can destroy it right away: it will not affect already created threads.
About the destructor: Is this the right way to do this?
Same thing as for ctor: you may use dtor and close/stop routine or can do it all in the dtor: depends on your specific needs (e.g. object reusability etc). Just make the dtor not throw.
Do your experienced eyes see any synchronization issues there?
I may suggest using pthread_testcancel(), to introduce the explicit cancelation point in a thread, and issue pthread_cancel() + pthread_join() (should return PTHREAD_CANCELED) in the control thread to stop the child thread, instead of synch variable workerRunning. Of course, if it is applicable in your case.
You should check for new data as soon as pthread_cond_wait returns, and wait again if there's no new data. That can happen if you get a spurious wake (think of it as the kernel accidentally waking you up by dropping something heavy down the stairs), and it would be better to wait immediately instead of changing workerWaiting then unlocking and relocking the mutex twice before waiting again.
An RAII lock type would make the code so much cleaner:
while(true) {
while(true) {
{
scoped_lock l(&manager->mutex);
if(/* new data available */)
{
/* copy new data from shared to thread memory */
}
else
break;
}
/* process the data in thread memory */
scoped_lock l(&manager->mutex);
/* copy results back to shared memory */
}
scoped_lock l(&manager->mutex);
// check if we should continue running
if(!manager->workerRunning)
break;
// wait for new data to arrive
manager->workerWaiting = true;
while (!/* new data available */)
pthread_cond_wait(&manager->condition, &manager->mutex);
manager->workerWaiting = false;
}
Using pthread_cancel as Oleg suggests would simplify it even further.
Following your edit to the code to handle spurious wake-ups, it becomes much simpler if you use RAII and restructure it:
while(true)
{
{
scoped_lock l(&manager->mutex);
// wait for new data to arrive
while(manager->workerRunning && !/* new data available*/)
pthread_cond_wait(&manager->condition, &manager->mutex);
// check if we should continue running
if(!manager->workerRunning)
break;
/* copy new data from shared to thread memory */
}
/* process the data in thread memory */
scoped_lock l(&manager->mutex);
/* copy results back to shared memory */
}
return NULL;
Without something like scoped_lock, what happens if /* copy new data from shared to thread memory */ or /* process the data in thread memory */ throws an exception? You'll never unlock the mutex.
The RAII type could be as simple as:
struct scoped_lock {
explicit scoped_lock(pthrad_mutex_t* m) : mx(m) {
pthread_mutex_lock(mx);
}
~scoped_lock() { pthread_mutex_unlock(mx); }
private:
pthread_mutex_t* mx;
scoped_lock(const scoped_lock&);
scoped_lock operator=(const scoped_lock&);
};
Related
i'm currently writing a c/c++ dll for later use mostly in Delphi and i'm more familiar with threads in Delphi than c/c++ and especially boost. So i wonder how i can achieve the following scenario?
class CMyClass
{
private:
boost::thread* doStuffThread;
protected:
void doStuffExecute(void)
{
while(!isTerminationSignal()) // loop until termination signal
{
// do stuff
}
setTerminated(); // thread is finished
};
public:
CMyClass(void)
{
// create thread
this->doStuffThread = new boost::thread(boost::bind(&CMyClass::doStuffExecute, this));
};
~CMyClass(void)
{
// finish the thread
signalThreadTermination();
waitForThreadFinish();
delete this->doStuffThread;
// do other cleanup
};
}
I have red countless articles about boost threading, signals and mutexes but i don't get it, maybe because it's friday ;) or is it not doable how i think to do it?
Regards
Daniel
Just use an atomic boolean to tell the thread to stop:
class CMyClass
{
private:
boost::thread doStuffThread;
boost::atomic<bool> stop;
protected:
void doStuffExecute()
{
while(!stop) // loop until termination signal
{
// do stuff
}
// thread is finished
};
public:
CMyClass() : stop(false)
{
// create thread
doStuffThread = boost::thread(&CMyClass::doStuffExecute, this);
};
~CMyClass()
{
// finish the thread
stop = true;
doStuffThread.join();
// do other cleanup
};
}
To wait for the thread to finish you just join it, that will block until it is finished and can be joined. You need to join the thread anyway before you can destroy it, or it will terminate your program.
There is no need to use a pointer and create the thread with new, just use a boost::thread object directly. Creating everything on the heap is wasteful, unsafe and poor style.
There is no need to use boost::bind to pass arguments to the thread constructor. For many many years boost::thread has supported passing multiple arguments to its constructor directly and it does the binding internally.
It's important that stop has been initialized to false before the new thread is created, otherwise if the new thread is spawned very quickly it could check the value of stop before it is initialized, and might happen to read a true value from the uninitialized memory, and then it would never enter the loop.
On the subject of style, writing foo(void) is considered by many C++ programmers to be a disgusting abomination. If you want to say your function takes no arguments then just write foo().
So I have a multithreaded C++ console application in which I want to handle the console close event in order to perform cleanup.
I have something to this effect:
bool running = true;
ServerSocket* server;
std::mutex mutex;
BOOL WINAPI HandlerRoutine(DWORD)
{
running = false;
server->shutdown();
std::lock_guard<std::mutex> guard(mutex);
return TRUE;
}
int main()
{
std::lock_guard<std::mutex> guard(mutex);
SetConsoleCtrlHandler(&HandlerRoutine, TRUE);
try {
ServerSocket server(27015);
::server = &server;
while (running)
{
TCPSocket* client = server.accept(true);
}
}
catch (const ServerSocket::ServerShutdownException&)
{
return 0;
}
}
If I return from HandlerRoutine my program gets terminated unceremoniously, so I have to wait for main() to end.
However, after main ends I get an exception telling me a mutex was destroyed while busy, thrown from dynamic atexit destructor for 'mutex'(). This leads me to believe that static and global variables are destroyed as soon as main returns, leaving my handler function hanging around with invalid globals.
Is this the standard specified behaviour, and if so, any idea about how I can achieve my desired effect?
In this scenario I would simply leak the mutex object. You don't want the destructor called prior to termination of the last thread, and there's no point in calling it during termination of the last thread.
std::mutex& mutex = *new mutex; // freed by OS at process exit
You can try boost::application.
Here the example wait_for_termination_request.cpp
Yes, your deduction is correct. Seems like the best option would be to unregister your handler and then wait for it to finish before returning from main(). But if that's not an option for whatever reason, something else you could do is to wrap all your globals in a struct:
struct Globals
{
bool running;
ServerSocket* server;
std::mutex mutex;
};
Have a single, global shared_ptr to an instance of that struct:
std::shared_ptr<Globals> globals = std::make_shared<Globals>();
Make a copy of the shared_ptr in your handler:
BOOL WINAPI HandlerRoutine(DWORD)
{
std::shared_ptr<Globals> myGlobals = globals;
...
}
And rely exclusively on myGlobals within the handler (there is no guarantee that the globals pointer itself will remain valid for the entire lifetime of the thread). That way everything is kept alive until everyone is done with it.
This assumes, of course, that globals is still valid when HandlerRoutine begins. If that's not the case (i.e. if the system can call the handler after main returns but before the process ends), then I'll delete this answer.
I'd be tempted to play ping pong with mutexes. Have not one, but two mutexes.
The first is held by mymain (a copy of your main basically). main does nothing but call mymain.
The second is held by HandlerRoutine, and aquired by main after returning from mymain.
If you shut down without the HandlerRoutine being called, you simply fall off the end of main.
If you shut down after the HandlerRoutine is called, your main blocks on it finishing.
Simply planning to leak the mutex is insufficient, as if HandlerRoutine is called during the period that main was already planing to shutdown, its server->shutdown could be accessing invalid memory.
Some work on the second mutax (that HandlerRoutine accesses) needs to be done to deal with race conditions (being called -- or reaching the lock -- after main has already exited, and the process is cleaning up global variables?). Storing the HandlerRoutine mutex in a pointer, and using lock-free techniques to access it extremely carefully, possibly involving spin locks.
To expand on the comments mentioning that the mutex is unnecessary, this is one alternative:
BOOL WINAPI HandlerRoutine(DWORD)
{
running = false;
server->shutdown();
Sleep(INFINITE);
return TRUE; // just to stop the compiler complaining
}
As far as I can tell there is quite a bit of time between between the lock_guard getting deleted and when a function (run in another thread) actually returns. See the comment below in TEST(...)
bool bDone = false;
void run_worker(Foo* f) {
f->Compute();
bDone = true;
}
TEST(FooTest,ThreadFoo) {
Foo* f = makeFoo();
std::thread worker( run_worker, f );
worker.detach();
micro_wait(100); // wait for N microseconds
f->Reset(); // should block until Compute() is done
// !!?? Why is this necessary !?!?
int k=0;
while(++k<500 && !bDone)
micro_wait(100);**
EXPECT_TRUE(bDone); // Fails even with a single micro_wait(100)!
}
Is there a good explanation for when/why there can be such a time lapse
between when f->Compute() finishes and bDone gets set? My suspicion is that the mutex gets unlocked while there is still work to be done cleaning up stack-based variables allocated in Compute() but this is purely a hypothesis.
Stubs for Compute and Reset are below:
void Foo::Compute() {
std::lock_guard<std::mutex> guard(m_Mutex);
// ... allocate bunch of temporary stuff on stack, update *this
}
void Foo::Reset() {
std::lock_guard<std::mutex> guard(m_Mutex);
// ... simpler stuff, clear
}
There is no synchronization of bDone.
It's quite possible that the compiler loads bDone into a register while it's value is false, and then continues to use the register cached version, instead of acquiring the updated version from memory. Alternatively, your instructions may be reordered such that bDone is set to false after the lock is released.
The correct way to approach this is to use an std::atomic<bool>. The worker thread can update it with a call to bDone.store(true) and the waiting thread can read it's most current value with a call to bDone.load().
If you want to read into memory ordering to help understand why an atomic is needed, you can further improve this (though for a unit test, it really doesn't matter) by using acquire and release ordering.
Aside from this, what you really should be doing is joining your worker thread. A join blocks until the thread has ended, so you can be assured that your Compute function has completed execution. If you're afraid that it may run forever (or for too long), I'd suggest using boost::thread instead of std::thread, as it provides a timed_join function, which stops waiting for the thread after a specified period of time.
I'm refactoring some time consuming function so that it can be called from a thread, but I'm having trouble wrapping my head around the issue (not very familiar with thread programming).
At any point, the user can cancel and the function will stop. I do not want to kill the thread as soon as the user cancels since it could cause some data integrity problems. Instead, in several places in the function, I will check if the function has been cancelled and, if so, exit. I will only do that where I know it's safe to exit.
The whole code of the function will be within a mutex. This is the pseudo-code I have in mind:
SomeClass::SomeClass() {
cancelled_ = false;
}
void SomeClass::cancelBigSearch() {
cancelled_ = true;
}
void SomeClass::bigSearch() {
mutex.lock();
// ...
// Some code
// ...
// Safe to exit at this point
if (cancelled_) {
mutex.unlock();
cancelled_ = false;
return;
}
// ...
// Some more code
// ...
if (cancelled_) {
mutex.unlock();
cancelled_ = false;
return;
}
// ...
// Again more code
// ...
if (cancelled_) {
mutex.unlock();
cancelled_ = false;
return;
}
mutex.unlock();
}
So when the user starts a search, a new thread calls bigSearch(). If the user cancels, cancelBigSearch() is called and a cancelled_ flag is set. Then, when bigSearch() reaches a point where it's safe to exit, it will exit.
Any idea if this is all thread-safe?
You should lock access to cancelled_ with another mutex, so checking and setting does not happen simultaneously. Other than that, I think your approach is OK
Update: Also, make sure no exceptions can be thrown from SomeClass::bigSearch(), otherwise the mutex might remain in a locked state. To make sure that all return paths unlock the mutex, you might want to surround the processing parts of the code with if (!cancelled_) and return only at the very end of the method (where you have the one unlock() call on the mutex.
Better yet, wrap the mutex in a RAII (acronym for Resource Allocation Is Initialization) object, so no matter how the function ends (exception or otherwise), the mutex is guaranteed to be unlocked.
Yes, this is thread safe. But:
Processors can have separate cache and cache it's own copy of cancelled_, typically mutex synchronization functions applies proper cache synchronization.
Compiler generated code, can make invalid assumptions about Your data locality, this can lead to not update in time cancelled_. Some platform specific commands can help here, or you can simply use other mechanisms.
All these lead to a thread that isn't canceled in time as you wish.
Your code usage pattern is simple "signaling". So you need to transfer signal to thread. Signal patterns allows trigger multiple times same trigger (signal), and clear it later.
This can be simulated using:
atomic operations
mutex protected variables
signal synchronization primitives
It's not thread-safe, because one thread could read cancelled_ at the same time another thread writes to it, which is a data race, which is undefined behaviour.
As others suggested, either use an atomic type for cancelled_ or protect it with another mutex.
You should also use RAII types to lock the mutexes.
e.g.
void SomeClass::cancelBigSearch() {
std::lock_guard<std::mutex> lock(cxlMutex_);
cancelled_ = true;
}
bool SomeClass::cancelled() {
std::lock_guard<std::mutex> lock(cxlMutex_);
if (cancelled_) {
// reset to false, to avoid caller having to lock mutex again to reset it
cancelled_ = false;
return true;
}
return false;
}
void SomeClass::bigSearch() {
std::lock_guard<std::mutex> lock(mutex);
// ...
// Some code
// ...
// Safe to exit at this point
if (cancelled())
return;
// ...
// Some more code
// ...
if (cancelled())
return;
// ...
// Again more code
// ...
if (cancelled())
return;
}
Here is a skeleton of my thread class:
class MyThread {
public:
virutal ~MyThread();
// will start thread with svc() as thread entry point
void start() = 0;
// derive class will specialize what the thread should do
virtual void svc() = 0;
};
Somewhere in code I create an instance of MyThread and later I want to destroy it.
In this case MyThread~MyThread() is called. MyThread:svc() is still running and using the object's data members. So I need a way politely inform MyThread:svc() to stop spinning, before proceeding with the destructor.
What is the acceptable way to destroy the thread object?
Note: I'm looking for platform agnostic solution.
UPD: It's clear that the root of problem is that there's no relationship between C++ object representing thread and OS thread. So the question is: in context of object destuction, is there an acceptable way to make thread object behave like an ordinary C++ object or should it be treated as an unusual one (e.g. should we call join() before destoying it?
Considering your additional requirements posted as comment to Checkers' reply (which is the
most straightforward way to do that):
I agree that join in DTor is problematic for various reasons. But from that the lifetime of your thread object is unrelated to the lifetime of the OS thread object.
First, you need to separate the data the thread uses from the thread object itself. They are distinct entities with distinct lifetime requirements.
One approach is to make the data refcounted, and have any thread that wants to access it hold a strong reference to the data. This way, no thread will suddenly grab into the void, but the data will be destroyed as soon as noone touches it anymore.
Second, about the thread object being destroyed when the thread joins:
I am not sure if this is a good idea. The thread object is normally a way to query the state of a thread - but with a thread object that dies as soon as the thread finishes, noone can tell you wether the thread finished.
Generally, I'd completely decouple the lifetime of the thread object from the lifetime of the OS thread: Destroying your thread object should not affect the thread itself. I see two basic approaches to this:
Thread Handle Object - reference counted again, returned by thread creator, can be released as early as one likes without affecting the OS thread. It would expose methods such as Join, IsFinished, and can give access to the thread shared data.
(If the thread object holds relevant execution state, the threafFunc itself could hold a reference to it, thereby ensuring the instance won't be released before the thread ends)
Thin Wrapper - You simply create a temporary around an OS thread handle. You could not hold additional state for the thread easily, but it might be just enough to make it work: At any place, you can turn an OS thread handle into an thread object. The majority of communication - e.g. telling the thread to terminate - would be via the shared data.
For your code example, this means: separate the start() from the svc()
You'd roughly work with this API (XxxxPtr could be e.g. boost::shared_ptr):
class Thread
{
public:
bool IsFinished();
void Join();
bool TryJoin(long timeout);
WorkerPtr GetWorker();
static ThreadPtr Start(WorkerPtr worker); // creates the thread
};
class Worker
{
private:
virtual void Svc() = 0;
friend class Thread; // so thread can run Svc()
}
Worker could contain a ThreadPtr itself, giving you a guarantee that the thread object exists during execution of Svc(). If multiple threads are allowed to work on the same data, this would have to be a thread list. Otherwise, Thread::Start would have to reject Workers that are already associated with a thread.
Motivation: What to do with rogue threads that block?
Assuming a thread fails to terminate within time for one reason or another, even though you told it to. You simply have three choices:
Deadlock, your applicaiton hangs. That usually happens if you join in the destructor.
Violently terminate the thread. That's potentially a violent termination of the app.
Let the thread run to completion on it's own data - you can notify the user, who can safely save & exit. Or you simply let the rogue thread dance on it's own copy of the data (not reference by the main thread anymore) until it completes.
Usually any OS-specific threads API will allow you to "join" a thread. That is, to block indefinitely on a thread handle until the thread functions returns.
So,
Signal the thread function to return (e.g. by setting a flag in its loop to false).
Join the thread, to make sure the actual thread terminates before you try to delete the thread object.
Then you can proceed with destruction of the thread object (you may also join in the destructor, though some people object to blocking destructors.).
I've had a project before with a similar "thread worker" class and a corresponding "work item" class (a-la Java's Thread and Runnable, except thread does not terminate but waits for a new Runnable object to be executed).
In the end, there was no difference if you join in a separate "shutdown" function or in the destructor, except a separate function is a bit more clear.
If you join in a destructor and a thread blocks, you will wait indefinitely.
If you join in a separate function and a thread blocks, you will wait indefinitely.
If you detach the thread and let it finish on its own, it will usually block application from exiting, so you will wait indefinitely.
So there is no straightforward way to make a thread behave like a regular C++ object and ignore its OS thread semantics, unless you can guarantee that your thread code can terminate almost immediately when notified to do so.
You could havee somthing like this in your svc method
while (alive){ //loops}
//free resources after while.
In your destructor, you could set the alive member to false. Or, you could have a pleaseDie() method, that sets the alive member to false, and can be called from the outside requesting the Thread instance to stop processing.
void
Thread::pleaseDie()
{
this->alive = false;
}
You first need a way to communicate with the thread to tell it to shut down. The best mechanism to do this depends on what svc() is doing. If, for example, it is looping on a message queue, you could insert a "please stop" message in that queue. Otherwise, you could simply add a member bool variable (and synchronize access to it) that is periodically checked by the svc(), and set by the thread wanting to destroy the object. Your could add a pure virtual stop() function to your base class, giving the implementor a clear signal that it has to implement svc() to make its class "runnable", and to implement stop() to make it "stoppable".
After asking the thread to stop, you must wait for it to exit before destroying the object. Again, there are several ways to do this. One is to make the stop() function blocking. It could wait, for example, for a "ok, I'm really stopped now" condition variable to be set by the thread running svc(). Alternatively, the caller could "wait" on the thread running svc(). The way to "wait" is platform dependent.
Most thread systems allow you to send a signal to a thead.
Example: pthreads
pthread_kill(pthread_t thread, int sig);
This will send a signall to a thread.
You can use this to kill the thread. Though this can leave a few of the resources hanging in an undefined state.
A solution to the resource problem is to install a signall handler.
So that when the signal handler is called it throws an exception. This will cause the thread stack to unwind to the entry point where you can then get the thread to check a variable about weather it is sill alive.
NOTE: You should never allow an exception to propogate out of a thread (this is so undefined my eyes bleed thinking about it). Basically catch the exception at the thread entry point then check some state variable to see if the thread should really exit.
Meanwhile the thread that sends the signal should wait for the thread to die by doing a join.
The only issues are that when you throw out of signal handler function you need to be careful. You should not use a signal that is asynchronus (ie one that could have been generated by a signal in another thread). A good one to use is SIGSEGV. If this happens normally then you have accessed invalid memory any you thread should think about exiting anyway!
You may also need to specify an extra flag on some systems to cope.
See This article
A working example using pthreads:
#include <pthread.h>
#include <iostream>
extern "C" void* startThread(void*);
extern "C" void shouldIexit(int sig);
class Thread
{
public:
Thread();
virtual ~Thread();
private:
friend void* startThread(void*);
void start();
virtual void run() = 0;
bool running;
pthread_t thread;
};
// I have seen a lot of implementations use a static class method to do this.
// DON'T. It is not portable. This is because the C++ ABI is not defined.
//
// It currently works on several compilers but will break if these compilers
// change the ABI they use. To gurantee this to work you should use a
// function that is declared as extern "C" this guarantees that the ABI is
// correct for the callback. (Note this is true for all C callback functions)
void* startThread(void* data)
{
Thread* thread = reinterpret_cast<Thread*>(data);
thread->start();
}
void shouldIexit(int sig)
{
// You should not use std::cout in signal handler.
// This is for Demo purposes only.
std::cout << "Signal" << std::endl;
signal(sig,shouldIexit);
// The default handler would kill the thread.
// But by returning you can continue your code where you left off.
// Or by throwing you can cause the stack to unwind (if the exception is caught).
// If you do not catch the exception it is implementation defined weather the
// stack is unwound.
throw int(3); // use int for simplicity in demo
}
Thread::Thread()
:running(true)
{
// Note starting the thread in the constructor means that the thread may
// start before the derived classes constructor finishes. This may potentially
// be a problem. It is started here to make the code succinct and the derived
// class used has no constructor so it does not matter.
if (pthread_create(&thread,NULL,startThread,this) != 0)
{
throw int(5); // use int for simplicity in demo.
}
}
Thread::~Thread()
{
void* ignore;
running = false;
pthread_kill(thread,SIGSEGV); // Tell thread it may want to exit.
pthread_join(thread,&ignore); // Wait for it to finish.
// Do NOT leave before thread has exited.
std::cout << "Thread Object Destroyed" << std::endl;
}
void Thread::start()
{
while(running)
{
try
{
this->run();
}
catch(...)
{}
}
std::cout << "Thread exiting" << std::endl;
}
class MyTestThread:public Thread
{
public:
virtual void run()
{
// Unless the signal causes an exception
// this loop will never exit.
while(true)
{
sleep(5);
}
}
};
struct Info
{
Info() {std::cout << "Info" << std::endl;}
~Info() {std::cout << "Done: The thread Should have exited before this" << std::endl;}
};
int main()
{
signal(SIGSEGV,shouldIexit);
Info info;
MyTestThread test;
sleep(4);
std::cout << "Exiting About to Exit" << std::endl;
}
> ./a.exe
Info
Exiting About to Exit
Signal
Thread exiting
Thread Object Destroyed
Done: The thread Should have exited before this
>
You should add dedicated thread management class (i.e. MyThreadMngr), that handles this and other tasks, like book keeping, owning the thread handles etc. The Thread itself should somehow signal to the thread manager that its going to terminate and MyThreadMngr should i.e. have a loop like Tom proposed.
There will probably be more actions that suite into such a thread manager class.
I reckon the easiest way to do this is to wrap the thread execution code in a loop
while(isRunning())
{
... thread implementation ...
}
You can also stop your thread by doing specific calls, for instance when you're using a WIN32 thread you can call TerminateThread on the thread handle in the destructor.
i give a simple and clean design, no signal, no sync, no kill needed.
per your MyThread, i suggest renaming and adding as below:
class MyThread {
public:
virutal ~MyThread();
// will be called when starting a thread,
// could do some initial operations
virtual bool OnStart() = 0;
// will be called when stopping a thread, say calling join().
virtual bool OnStop() = 0;
// derive class will specialize what the thread should do,
// say the thread loop such as
// while (bRunning) {
// do the job.
// }
virtual int OnRun() = 0;
};
the thread interface user will control the lifetime of MyThread.
and actually the real thread object is as below:
class IThread
{
public:
virtual API ~IThread() {}
/* The real destructor. */
virtual void Destroy(void) = 0;
/* Starts this thread, it will call MyThread::OnStart()
* and then call MyThread::OnRun() just after created
* the thread. */
virtual bool Start(void) = 0;
/* Stops a thread. will call MyThread::OnStop(). */
virtual void Stop(void) = 0;
/* If Wait() called, thread won't call MyThread::OnStop().
* If could, it returns the value of MyThread::OnRun()
* returned */
virtual int Wait(void) = 0;
/* your staff */
virtual MyThread * Command(void) = 0;
};
/* The interface to create a thread */
extern IThread * ThrdCreate(MyThread *p);
See the complete interfaces
http://effoaddon.googlecode.com/svn/trunk/devel/effo/codebase/addons/thrd/include/thrd_i.h
Coding Examples
Case 1. Controlled thread loop
class ThreadLoop : public MyThread
{
private:
bool m_bRunning;
public:
virtual bool OnStart() { m_bRunning = true; }
virtual bool OnStop() { m_bRunning = false; }
virtual int OnRun()
{
while (m_bRunning) {
do your job;
}
}
};
int main(int argc, char **argv)
{
ThreadLoop oLoop;
IThread *pThread = ThrdCreate(&oLoop);
// Start the thread, it will call Loop::OnStart()
//and then call Loop::OnRun() internally.
pThread->Start();
do your things here. when it is time to stop the thread, call stop().
// Stop the thread, it will call Loop::OnStop(),
// so Loop::OnRun() will go to the end
pThread->Stop();
// done, destroy the thread
pThread->Destroy();
}
Case 2. Don't know when the thread will stop
class ThreadLoop : public MyThread
{
public:
virtual bool OnStart() { }
virtual bool OnStop() { }
virtual int OnRun()
{
do your job until finish.
}
};
int main(int argc, char **argv)
{
ThreadLoop oLoop;
IThread *pThread = ThrdCreate(&oLoop);
// Start the thread, it will call Loop::OnStart()
//and then call Loop::OnRun() internally.
pThread->Start();
do your things here. Since you don't know when the job will
finish in the thread loop. call wait().
// Wait the thread, it doesn't call Loop::OnStop()
pThread->Wait();
// done, destroy the thread
pThread->Destroy();
}
A complete IThread implementation:
see
http://effoaddon.googlecode.com/svn/trunk/devel/effo/codebase/addons/thrd/src/thrd/thrd.cpp