There are many ways to skin this particular cat…
We do something a little differently, mostly because of the addition of many other threads to perform other tasks as well (comms, non-blocking logging, status monitoring, multiple controllers, etc.). Firstly, we implement a state machine (based on code from M. Samek’s “Practical Statecharts in C++”) so that all threads can access a threadsafe (mutex protected) method GetCurrentState(). Also implemented is a WaitForStateChange() which uses a pthread condition to block any threads until a state transition occurs. When the state machine makes a transition it calls a pthread broadcast on the condition to unblock all threads waiting on it.
Each thread performing stateful behaviours (while it is still running) checks to see if the system is currently in a state which requires it to operate. If it is in an operating state it performs whatever task is appropriate for that state, if it is not, it calls WaitForStateChange() to block pending a state transition.
The equivalent of your “main” thread (or any other source of an event which affects the state machine) then triggers the changes as required.
Depending on the complexity of the system this solution is very flexible, but may be unnecessarily complicated for your simple application described.
Yet another alternative would be to use a conditon and a state variable. You will need a mutex, a condition and a bool either in global memory (yuk!) or in a singleton object which can be owned by the two threads simultaneously. The child thread should look something like (we wrap the pthread calls into C++ classes for coding simplicity):
while( ThreadStillRunning ) // or something like this
while( sharedMemory.controllerRunning == false ) // see the pthreads docs for details of why a while loop
sharedMemory.controllerCondition.Wait( sharedMemory.controllerMutex );
// do controlling stuff
The drawback is that you lock/unlock the mutex every time you pass through the control loop. You might consider only checking the controller status every n steps of the control loop, with the reduced checking of the status variable, but this will reduce the responsiveness of the controller to a stop signal (which may not be a problem for you). The controller will start as soon as the scheduler lets it once the condition has been signaled with the appropriate value for controllerRunning.
The main thread will then do the following to start the thread:
sharedMemory.controllerRunning = true;
and to stop it, simply changes the bool controllerRunning to false.
Caveat: unless I have screwed up with the ordering of the locks and unlocks this example should not deadlock - but I have been known to screw these up before