/*
* Copyright 1995, 2000 Perforce Software. All rights reserved.
*
* This file is part of Perforce - the FAST SCM System.
*/
/*
* threading.h -- handle multiple users at the same time
*
* These classes are meant to take the vicious ifdef hacking required
* to get threading (forking, whatever) to work on different platforms.
*
* Some terms:
*
* "thread": a process on UNIX; a thread on NT
* "leader": the parent process on UNIX; the only process on NT
*
* Classes defined:
*
* Threading: caller interface to launch multiple threads
*
* Threader: implementation of threading (not public)
*
* Thread (abstract): glues caller's execution object so that Threading
* can run and then delete it.
*
* Process (abstract): callbacks into caller's environment to signal
* process related changes.
*
* Public methods:
*
* Thread::Run() - do what was supposed to happen in the thread
* Thread::~Thread() - delete user's class, cleaning up thread exit
*
* Process::Child() - indicates that Run() will be a child process
* Process::Cancel() - indicates that leader should stop launching
*
* Threading::Launch() - create a thread/process and call Thread::Run().
*
* Threading::Cancelled() - returns true (in leader) if Cancel()
* Threading::Restarted() - returns true (in leader) if Restart()
*
* Threading::Cancel() - can be called from any thread to tell the
* leader to stop; leader calls Process::Cancel()
* Threading::Restart() - can be called from any thread to tell the
* leader to restart; leader calls Process::Restart()
*
* Threading::Reap() - called in leader to kill children
*
* Threading::GetThreadCount() - returns the current number of threads
* Only valid in the parent/main process!
*
* The current termination ritual:
*
* Someone, somewhere calls Threading::Cancel(), which is static
* and always available. It can get called by the leader
* catching SIGTERM, or by anyone at the user's request.
*
* If a child gets Threading::Cancel() on UNIX, it sends a SIGTERM
* to its parent so that the leader gets Threading::Cancel() called.
*
* In the leader, Threading::Cancel() sets the "cancelled" flag and
* calls Process::Cancel(), so that (in fact) the listen socket
* descriptor gets closed, breaking the accept() loop.
*
* The leader, out of its thread creation loop, can call Reap()
* in order to kill and collect all the child processes. It should
* only do that if the database is safely locked from child process
* access.
*
* Restart is just like Cancel but the leader re-starts all processing rather
* than exiting.
*/
enum ThreadMode {
TmbSingle, // just single threading
TmbMulti, // multi threading (fork, threads)
TmbDaemon // fork, then forking multi threading (UNIX)
} ;
class Thread {
public:
virtual ~Thread();
virtual void Run() = 0;
} ;
class Process {
public:
virtual ~Process();
virtual void Child() = 0;
virtual void Cancel() = 0;
} ;
class Threader {
protected:
friend class Threading;
Threader()
{ cancelled = 0; restarted = 0;
threadCount = 0; process = 0; }
virtual ~Threader();
virtual void Launch( Thread *t );
virtual void Cancel();
virtual void Restart();
virtual void Quiesce();
virtual void Reap();
virtual int GetThreadCount(); // varies on each system
int cancelled;
int restarted;
Process *process;
int threadCount; // not used by all implementations...
} ;
class Threading {
public:
Threading( ThreadMode tmb, Process *p );
~Threading() { delete threader; }
void Launch( Thread *t ) { threader->Launch( t ); }
int Cancelled() { return threader->cancelled; }
int Restarted() { return threader->restarted; }
void Quiesce() { threader->Quiesce(); }
void Reap() { threader->Reap(); }
static void Cancel() { if( current ) current->Cancel(); }
static void Restart() { if( current ) current->Restart(); }
static int WasCancelled() { if( current ) return current->cancelled; else return 0; }
static int WasRestarted() { if( current ) return current->restarted; else return 0; }
static int GetThreadCount()
{ return current ? current->GetThreadCount() : -1; }
private:
Threader *threader;
static Threader *current;
} ;