// Copyright (C) 1999 Open Source Telecom Corporation.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception to the GNU General Public License, permission is
// granted for additional uses of the text contained in its release
// of APE.
//
// The exception is that, if you link the APE library with other files
// to produce an executable, this does not by itself cause the
// resulting executable to be covered by the GNU General Public License.
// Your use of that executable is in no way restricted on account of
// linking the APE library code into it.
//
// This exception does not however invalidate any other reasons why
// the executable file might be covered by the GNU General Public License.
//
// This exception applies only to the code released under the
// name APE. If you copy code from other releases into a copy of
// APE, as the General Public License permits, the exception does
// not apply to the code that you add in this way. To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for APE, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
#ifndef __APE_THREAD_H__
#define __APE_THREAD_H__
#define __APE_POSIX
#ifndef _REENTRANT
#define _REENTRANT
#endif
#ifndef _THREAD_SAFE
#define _THREAD_SAFE
#include <APE/config.h>
#else
#ifndef HAVE_CONFIG_H
#include <APE/config.h>
#endif
#endif
#ifndef HAVE_PTHREAD_H
#include <pthread.h>
#include <semaphore.h>
#endif
#ifndef __APE_MACROS_H__
#include <APE/macros.h>
#endif
#include <time.h>
#include <signal.h>
#include <setjmp.h>
#ifdef __linux__
#define _APE_THREAD_STOPCONT
#define _APE_THREAD_ALARM
#endif
typedef pthread_t tid_t;
typedef unsigned long timeout_t;
typedef int signo_t;
#define ENTER_CRITICAL EnterMutex();
#define LEAVE_CRITICAL LeaveMutex();
#define ENTER_DEFERRED setCancel(THREAD_CANCEL_DEFERRED);
#define LEAVE_DEFERRED setCancel(THREAD_CANCEL_IMMEDIATE);
// These macros override common functions with thread-safe versions. In
// particular the common "libc" sleep() has problems since it normally
// uses SIGARLM (as actually defined by "posix"). The pthread_delay and
// usleep found in libpthread are gaurenteed not to use SIGALRM and offer
// higher resolution. psleep() is defined to call the old process sleep.
#define sleep(x) ape_sleep((x) * 1000)
#define yield() ape_yield()
#define psleep(x) (sleep)(x)
enum
{
THREAD_CANCEL_INITIAL=0,
THREAD_CANCEL_DEFERRED=1,
THREAD_CANCEL_IMMEDIATE,
THREAD_CANCEL_DISABLED,
THREAD_CANCEL_DEFAULT=THREAD_CANCEL_DEFERRED
};
enum
{
THREAD_SUSPEND_ENABLE,
THREAD_SUSPEND_DISABLE
};
#define THREAD_SIGNAL_BLOCKED false
#define THREAD_SIGNAL_UNBLOCK true
#ifdef _APE_THREAD_STOPCONT
#define _SIG_THREAD_SUSPEND SIGSTOP
#define _SIG_THREAD_RESUME SIGCONT
#else
#ifndef SIGUSR3
#ifdef SIGWINCH
#define SIGUSR3 SIGWINCH
#else
#define SIGUSR3 SIGINT
#endif
#endif
#define _SIG_THREAD_SUSPEND SIGUSR3
#define _SIG_THREAD_RESUME SIGUSR3
#endif
class Thread;
Thread *getAPE(void);
/**
* The Mutex class is used to protect a section of code so that at any
* given time only a single thread can perform the protected operation.
* The APE Mutex is always recursive in that if the same thread invokes
* the same mutex lock multiple times, it must release it multiple times.
* This allows a function to call another function which also happens to
* use the same mutex lock when called directly.
*
* The Mutex can be used as a base class to protect access in a derived
* class. When used in this manner, the ENTER_CRITICAL and LEAVE_CRITICAL
* macros can be used to specify when code written for the derived class
* needs to be protected by the default Mutex of the derived class, and
* hence is presumed to be 'thread safe' from multiple instance execution.
*
* @author David Sugar <dyfet@ostel.com>
* @short Mutex lock for protected access.
*/
class Mutex
{
private:
#ifndef PTHREAD_MUTEXTYPE_RECURSIVE
int _level;
#endif
protected:
/**
* Pthread mutex object. This is protected rather than private
* because some mixed mode pthread operations require a mutex as
* well as their primary pthread object. A good example of this
* is the Event class, as waiting on a conditional object must be
* associated with an accessable mutex. An alternative would be
* to make such classes "friend" classes of the Mutex.
*/
pthread_mutex_t _mutex;
public:
/**
* The mutex is always initialized as a recursive entity.
*/
Mutex();
/**
* Destroying the mutex removes any system resources associated
* with it. If a mutex lock is currently in place, it is presumed
* to terminate when the Mutex is destroyed.
*/
~Mutex()
{pthread_mutex_destroy(&_mutex);};
/**
* Entering a Mutex locks the mutex for the current thread. This
* also can be done using the ENTER_CRITICAL macro or by using the
* ++ operator on a mutex.
*
* @see #LeaveMutex
*/
#ifdef PTHREAD_MUTEXTYPE_RECURSIVE
inline void EnterMutex(void)
{pthread_mutex_lock(&_mutex);};
#else
void EnterMutex(void);
#endif
/**
* Leaving a mutex frees that mutex for use by another thread. If
* the mutex has been entered (invoked) multiple times (recursivily)
* by the same thread, then it will need to be exited the same number
* of instances before it is free for re-use. This operation can
* also be done using the LEAVE_CRITICAL macro or by the -- operator
* on a mutex.
*
* @see #EnterMutex
*/
#ifdef PTHREAD_MUTEXTYPE_RECURSIVE
inline void LeaveMutex(void)
{pthread_mutex_unlock(&_mutex);};
#else
void LeaveMutex(void);
#endif
};
/**
* The Mutex Counter is a counter variable which can safely be incremented
* or decremented by multiple threads. A Mutex is used to protect access
* to the counter variable (an integer). An initial value can be specified
* for the counter, and it can be manipulated with the ++ and -- operators.
*
* @author David Sugar <dyfet@ostel.com>
* @short Thread protected integer counter.
*/
class MutexCounter.html">MutexCounter : public Mutex
{
private:
int counter;
public:
MutexCounter(int initial = 0);
friend int operator ++(MutexCounter &mc);
friend int operator --(MutexCounter &mc);
};
/**
* A semaphore is generally used as a synchronization object between multiple
* threads or to protect a limited and finite resource such as a memory or
* thread pool. The semaphore has a counter which only permits access by
* one or more threads when the value of the semaphore is non-zero. Each
* access reduces the current value of the semaphore by 1. One or more
* threads can wait on a semaphore until it is no longer 0, and hence the
* semaphore can be used as a simple thread synchronization object to enable
* one thread to pause others until the thread is ready or has provided data
* for them.
*
* @author David Sugar <dyfet@ostel.com>
* @short Semaphore counter for thread synchronization.
*/
class Semaphore
{
protected:
sem_t _semaphore;
public:
/**
* The initial value of the semaphore can be specified. An initial
* value is often used When used to lock a finite resource or to
* specify the maximum number of thread instances that can access a
* specified resource.
*
* @param resource specify initial resource count or 0 default.
*/
Semaphore(size_t resource = 0);
/**
* Destroying a semaphore also removes any system resources
* associated with it. If a semaphore has threads currently waiting
* on it, those threads will all continue when a semaphore is
* destroyed.
*/
~Semaphore()
{sem_destroy(&_semaphore);};
/**
* Wait is used to keep a thread held until the semaphore counter
* is greater than 0. If the current thread is held, then another
* thread must increment the semaphore. Once the thread is accepted,
* the semaphore is automatically decremented, and the thread
* continues execution.
*
* The pthread semaphore object does not support a timed "wait", and
* hence to maintain consistancy, neither the posix nor win32 source
* trees support "timed" semaphore objects.
*
* @see #Post
*/
inline void Wait(void)
{sem_wait(&_semaphore);};
/**
* Posting to a semaphore increments its current value and releases
* the first thread waiting for the semaphore if it is currently at
* 0. Interestingly, there is no support to increment a semaphore by
* any value greater than 1 to release multiple waiting threads in
* either pthread or the win32 API. Hence, if one wants to release
* a semaphore to enable multiple threads to execute, one must perform
* multiple post operations.
*
* @see #Wait
*/
inline void Post(void)
{sem_post(&_semaphore);};
};
/**
* The APE Event class implements a feature originally found in the WIN32 API;
* event notification. A target thread waits on a resetable Event, and one
* or more other threads can then signal the waiting thread to resume
* execution. A timeout can be used to specify a wait duration in
* milliseconds. The Event class must be reset before it can be used again
* as a trigger.
*
* @author: David Sugar <dyfet@ostel.com>
* @short Thread synchornization on event notification.
*/
class Event : public Mutex
{
protected:
pthread_cond_t _cond;
bool _signaled;
int _count;
public:
Event();
~Event()
{pthread_cond_destroy(&_cond);};
/**
* Once signaled, the Event class must be "reset" before responding
* to a new signal.
*
* @see #Signal
*/
void Reset(void)
{_signaled = false;};
/**
* Signal the event for the waiting thread.
*/
void Signal(void);
/**
* Wait either for the event to be signaled by another thread or
* for the specified timeout duration.
*
* @see #Signal
* @return true if signaled, false if timed out.
* @param timer timeout in milliseconds to wait for a signal.
*/
bool Wait(timeout_t timer = 0);
};
/**
* The buffer class represents an IPC service that is built upon a buffer
* of fixed capacity that can be used to transfer objects between one or
* more producer and consumer threads. Producer threads post objects
* into the buffer, and consumer threads wait for and receive objects from
* the buffer. Semaphores are used to to block the buffer from overflowing
* and indicate when there is data available, and mutexes are used to protect
* multiple consumers and producer threads from stepping over each other.
*
* The buffer class is an abstract class in that the actual data being
* buffered is not directly specified within the buffer class itself. The
* buffer class should be used as a base class for a class that actually
* impliments buffering and which may be aware of the data types actually
* are being buffered. A template class could be created based on buffer
* for this purpose. Another possibility is to create a class derived
* from both Thread and Buffer which can be used to implement message passing
* threads.
*
* @author David Sugar <dyfet@ostel.com>
* @short Producer/Consumer buffer for use between threads.
*/
class Buffer
{
private:
Mutex lock_head, lock_tail;
Semaphore size_head, size_tail;
size_t _size;
size_t _used;
protected:
/**
* Invoke derived class buffer peeking method.
* @return size of object found.
* @param buf pointer to copy contents of head of buffer to.
*/
virtual int OnPeek(void *buf) = 0;
/**
* Invoke derived class object request from buffer.
* @return size of object returned.
* @param buf pointer to hold object returned from the buffer.
*/
virtual int OnWait(void *buf) = 0;
/**
* Invoke derived class posting of object to buffer.
* @return size of object posted.
* @param buf pointer to object being posted to the buffer.
*/
virtual int OnPost(void *buf) = 0;
public:
/**
* Create a buffer object of known capacity.
* @param capcity is the integer capacity of the buffer.
*/
Buffer(size_t capacity);
/**
* In derived functions, may be used to free the actual memory
* used to hold buffered data.
*/
virtual ~Buffer()
{return;};
/**
* Return the capacity of the buffer as specified at creation.
* @return size of buffer.
*/
inline size_t getSize(void)
{return _size;};
/**
* Return the current capacity in use for the buffer. Free space
* is technically getSize() - getUsed().
* @return integer used capacity of the buffer.
* @see #getSize
*/
inline size_t getUsed(void)
{return _used;};
/**
* Let one or more threads wait for an object to become available
* in the buffer. The waiting thread(s) will wait forever if no
* object is ever placed into the buffer.
*
* @return size of object passed by buffer in bytes.
* @param buf pointer to store object retrieved from the buffer.
*/
int Wait(void *buf);
/**
* Post an object into the buffer and enable a waiting thread to
* receive it.
*
* @return size of object posted in bytes.
* @param buf pointer to object to store in the buffer.
*/
int Post(void *buf);
/**
* Peek at the current content (first object) in the buffer.
*
* @return size of object in the buffer.
* @param buf pointer to store object found in the buffer.
*/
int Peek(void *buf);
};
/**
* A buffer class that holds a known capacity of fixed sized objects defined
* during creation.
*
* @author David Sugar <dyfet@ostel.com>.
* @short producer/consumer buffer for fixed size objects.
*/
class Buffer.html">FixedBuffer : public Buffer
{
private:
char *buf, *head, *tail;
size_t objsize;
protected:
/**
* Return the first object in the buffer.
* @return predefined size of this buffers objects.
* @param buf pointer to copy contents of head of buffer to.
*/
int OnPeek(void *buf);
/**
* Wait for and return a fixed object in the buffer.
* @return predefined size of this buffers objects.
* @param buf pointer to hold object returned from the buffer.
*/
int OnWait(void *buf);
/**
* Post an object of the appropriate size into the buffer.
* @return predefined size of this buffers objects.
* @param buf pointer to data to copy into the buffer.
*/
int OnPost(void *buf);
public:
/**
* Create a buffer of known capacity for objects of a specified
* size.
*
* @param capacity of the buffer.
* @param objsize for each object held in the buffer.
*/
FixedBuffer(size_t capacity, size_t objsize);
/**
* Create a copy of an existing fixed size buffer and duplicate
* it's contents.
*
* @param fb existing FixedBuffer object.
*/
FixedBuffer(const FixedBuffer &fb);
/**
* Destroy the fixed buffer and free the memory used to store objects.
*/
~FixedBuffer();
FixedBuffer &operator=(const FixedBuffer &fb);
};
/**
* The Pipe uses system kernel buffering to hold data being passed either
* between two execution contexts within the same process, or between
* different processes. Unlike Buffer, Pipe uses system descriptors and
* kernel memory. Under Posix, the size of the pipe and associated kernel
* memory is always a fixed constant as defined by _PC_PIPE_BUF. Since
* the pipe does not deal with fixed objects, any data can be read from or
* written to the kernel pipe buffer.
*
* @author David Sugar <dyfet@ostel.com>.
* @short kernel buffering between processes and/or threads.
*/
class Pipe
{
private:
int fd[2];
protected:
/**
* Sender is often used for implementing a fork()'d message port
* between processes. By defining the current pipe as only used
* for sending, the receiver is presumed to be in the other half
* of a fork()'d process.
*
* @see #Receiver
*/
inline void Sender(void)
{close(fd[0]);};
/**
* Receiver is often used for implementing a fork()'d message port
* between processes. By defining the current pipe as only used
* for receiving, the sender is presumed to be in the other half
* of a fork()'d process.
*
* @see #Sender
*/
inline void Receiver(void)
{close(fd[1]);};
public:
/**
* Create a kernel pipe descriptor set using pipe().
*/
Pipe();
/**
* Destroy the pipe and kernel descriptor resources.
*/
~Pipe();
/**
* Create a pipe as a duplicate of an existing pipe.
*
* @param orig pipe to duplicate.
*/
Pipe(const Pipe &orig);
Pipe &operator=(const Pipe &orig);
/**
* Read an arbitrary number of bytes from the pipe buffer.
*
* @return number of bytes actually read if successful.
* @param addr pointer to store read data.
* @param len number of bytes to read.
*/
inline int Read(void *addr, size_t len)
{return read(fd[0], (char *)addr, len);};
/**
* Write an arbitrary number of butes to the pipe buffer.
*
* @return number of bytes read if successful.
* @param addr pointer to write data from.
* @param len number of butes to write.
*/
inline int Write(void *addr, size_t len)
{return write(fd[1], (char *)addr, len);};
friend inline int read(Pipe &p, void *addr, size_t len)
{return read(p.fd[0], (char *)addr, len);};
friend inline int write(Pipe &p, void *addr, size_t len)
{return write(p.fd[1], (char *)addr, len);};
};
/**
* Every thread of execution in an APE application is created by deriving
* a unique class from the APE Thread class and by implementing the Run
* method. The base Thread class supports encapsulation of the generic
* APE threading methods implemented on various target operating systems.
* This includes the ability to start and stop threads in a synchronized
* and controllable manner, the ability to specify thread execution priority,
* and thread specific "system call" wrappers, such as for sleep and yield.
* A thread exception is thrown if the thread cannot be created.
*
* @author David Sugar <dyfet@tycho.com>
* @short base class used to derive all APE threads of execution.
*/
class Thread
{
private:
static Thread *_main;
#ifndef _APE_THREAD_ALARM
static Thread *_timer;
static Mutex _arm;
#endif
Thread *_parent;
pthread_t _tid;
pthread_attr_t _attr;
int _cancel;
jmp_buf _env;
time_t _alarm;
Semaphore *_start;
static void Execute(Thread *th);
static void SignalHandler(int signo);
protected:
/**
* All APE threads execute by deriving the Run method of Thread.
* This method is called after Initial to begin normal operation
* of the thread. If the method terminates, then the thread will
* also terminate after notifying it's parent and calling it's
* Final() method.
*
* @see #Initial
*/
virtual void Run(void) = 0;
/**
* This method is called for the very first instance of a new thread
* being created in a multi-threaded application. Hence, it is only
* called once, and by the derived Thread class that happens to be
* created first.
*/
virtual void First(void)
{return;};
/**
* A thread that is self terminating, either by invoking Exit() or
* leaving it's Run(), will have this method called. It can be used
* to self delete the current object assuming the object was created
* with new on the heap rather than stack local, hence one may often
* see Final defined as "delete this" in a derived thread class. A
* Final method, while running, cannot be terminated or cancelled by
* another thread.
*
* @see #Exit
* @see #Run
*/
virtual void Final(void)
{return;};
/**
* The initial method is called by a newly created thread when it
* starts execution. This method is ran with deferred cancellation
* disabled by default. The Initial method is given a seperate
* handler so that it can create temporary objects on it's own
* stack frame, rather than having objects created on Run() that
* are only needed by startup and yet continue to consume stack space.
*
* @see #Run
*/
virtual void Initial(void)
{return;};
/**
* Since getParent() and getAPE() only refer to an object of the
* Thread "base" type, this virtual method can be replaced in a
* derived class with something that returns data specific to the
* derived class that can still be accessed through the pointer
* returned by getParent() and getAPE().
*
* @return pointer to derived class specific data.
*/
virtual void *getExtended(void)
{return NULL;};
/**
* When a thread terminates, it now sends a notification message
* to the parent thread which created it. The actual use of this
* notification is left to be defined in a derived class.
*
* @param th the thread that has terminated.
*/
virtual void Notify(Thread *th)
{return;};
/**
* In the Posix version of APE, this can be used to send a
* signal into the parent thread of the current object.
*
* @param signo a posix signal id.
*/
inline void SignalParent(signo_t signo)
{_parent->SignalThread(signo);};
/**
* In the Posix version of APE, this can be used to send a
* signal into the nain application thread.
*
* @param signo a posix signal id.
*/
inline void SignalMain(signo_t signo)
{_main->SignalThread(signo);};
/**
* A derivable method to call when a SIGALRM is being delivered
* to a specific thread.
*/
virtual void OnTimer(void)
{return;};
/**
* A derived method to handle asynchronous I/O requests delivered
* to the specified thread.
*/
virtual void OnPolling(void)
{return;};
/**
* A derivable method to call for delivering a signal event to
* a specified thread.
*
* @param signo posix signal id.
*/
virtual void OnSignal(int signo)
{return;};
/**
* A thread-safe sleep call. On most Posix systems, "sleep()"
* is implimented with SIGALRM making it unusable from multipe
* threads. Pthread libraries often define an alternate "sleep"
* handler such as usleep(), nanosleep(), or nap(), that is thread
* safe, and also offers a higher timer resolution.
*
* @param msec timeout in milliseconds.
*/
inline void Sleep(timeout_t msec)
{ape_sleep(msec);};
/**
* Used to properly exit from a Thread derived Run() or Initial()
* method. Terminates execution of the current thread and calls
* the derived classes Final() method.
*/
inline void Exit(void)
{longjmp(_env, 1);};
/**
* Used to specify a timeout event that can be delivered to the
* current thread via SIGALRM. When the timer expires, the OnTimer()
* method is called for the thread. At present, only one thread
* timer can be active at any given time. On some operating
* systems (including Linux) a timer can be active on each thread.
*
* @param timer timeout in milliseconds.
*/
void setTimer(timeout_t timer);
/**
* Gets the time remaining for the current threads timer before
* it expires.
*
* @return time remaining before timer expires in milliseconds.
*/
timeout_t getTimer(void);
/**
* Terminates the timer before the timeout period has expired.
* This prevents the timer from sending it's SIGALRM and makes
* the timer available to other threads.
*/
void endTimer(void);
/**
* Used to wait on a Posix signal from another thread. This can be
* used as a crude rondevious/synchronization method between threads.
*
* @param signo a posix signal id.
*/
void WaitSignal(signo_t signo);
/**
* Yeilds the current thread's CPU time slice to allow another thread to
* begin immediate execution.
*/
void Yield(void);
/**
* Sets thread cancellation mode. Threads can either be set immune to
* termination (THREAD_CANCEL_DISABLED), can be set to terminate when
* reaching specific "thread cancellation points" (THREAD_CANCEL_DEFERRED)
* or immediately when Terminate is requested (THREAD_CANCEL_IMMEDIATE).
*
* @param mode for cancellation of the current thread.
*/
void setCancel(int mode);
/**
* Sets the thread's ability to be suspended from execution. The
* thread may either have suspend enabled (THREAD_SUSPEND_ENABLE) or
* disabled (THREAD_SUSPEND_DISABLE).
*
* @param mode for suspend.
*/
void setSuspend(int mode);
/**
* Used to enable or disable a signal within the current thread.
*
* @param signo posix signal id.
* @param active set to true to enable.
*/
void setSignal(int signo, bool mode);
/**
* Used by another thread to terminate the current thread. Termination
* actually occurs based on the current setCancel() mode. When the
* current thread does terminate, control is returned to the requesting
* thread. Terminate() should always be called at the start of any
* destructor of a class derived from Thread to assure the remaining
* part of the destructor is called without the thread still executing.
*/
void Terminate(void);
public:
/**
* This is actually a special constructor that is used to create a
* thread "object" for the current execution context when that context
* is not created via an instance of a derived Thread object itself.
* This constructor does not support First.
*
* @param bool used if the main "thread" of the application.
*/
Thread(bool flag);
/**
* When a thread object is contructed, a new thread of execution
* context is created. This constructor allows basic properties
* of that context (thread priority, stack space, etc) to be defined.
* The starting condition is also specified for whether the thread
* is to wait on a semaphore before begining execution or wait until
* it's start method is called.
*
* @param start semaphore to wait before executing thread.
* @param pri thread base priority relative to it's parent.
* @param stack space as needed in some implementations.
*/
Thread(Semaphore *start = NULL, int pri = 0, size_t stack = 0);
/**
* A thread of execution can also be specified by cloning an existing
* thread. The existing thread's properties (cancel mode, priority,
* etc), are also duplicated.
*
* @param th currently executing thread object to clone.
*/
Thread(const Thread &th);
/**
* The thread destructor should clear up any resources that have
* been allocated by the thread. The desctructor of a derived
* thread should begin with Terminate() and is presumed to then
* execute within the context of the thread causing terminaton.
*/
virtual ~Thread()
{Terminate();};
/**
* When a new thread is created, it does not begin immediate
* execution. This is because the derived class virtual tables
* are not properly loaded at the time the C++ object is created
* within the constructor itself, at least in some compiler/system
* combinations. The thread can either be told to wait for an
* external semaphore, or it can be started directly after the
* constructor completes by calling the Start() method.
*
* @return error code if execution fails.
* @param start optional starting semaphore to alternately use.
*/
int Start(Semaphore *start = NULL);
/**
* Gets the pointer to the Thread class which created the current
* thread object.
*
* @return a Thread *, or "(Thread *)this" if no parent.
*/
inline Thread *getParent(void)
{return _parent;};
/**
* Delivers a Posix signal to the current thread.
*
* @param signo a posix signal id.
*/
inline void SignalThread(int signo)
{pthread_kill(_tid, signo);};
/**
* Suspends execution of the selected thread. Pthreads do not
* normally support suspendable threads, so the behavior is
* simulated with signals. On systems such as Linux that
* define threads as processes, SIGSTOP and SIGCONT may be used.
*/
#ifdef _THR_SUNOS5
inline void Suspend(void)
{thr_suspend((thread_t)_tid);};
#else
inline void Suspend(void)
{pthread_kill(_tid, _SIG_THREAD_SUSPEND);};
#endif
/**
* Resumes execution of the selected thread.
*/
#ifdef _THR_SUNOS5
inline void Resume(void)
{thr_continue((thread_t)_tid);};
#else
inline void Resume(void)
{pthread_kill(_tid, _SIG_THREAD_RESUME);};
#endif
/**
* Used to retrieve the cancellation mode in effect for the
* selected thread.
*
* @return cancellation mode constant.
*/
inline int getCancel(void)
{return _cancel;};
/**
* Verifies if the thread is still running or has already been
* terminated but not yet deleted.
*
* @return true if the thread is still executing.
*/
inline bool isRunning(void)
{return _tid != 0;};
/**
* Tests to see if the current execution context is the same as
* the specified thread object.
*
* @return true if the current context is this object.
*/
inline bool isThread(void)
{return _tid == pthread_self();};
/**
* Thread safe sleep call replacement. This is mapped into sleep().
*
* @param msec timeout in millisecond time range.
*/
friend void ape_sleep(timeout_t msec);
/**
* Suspend the execution of the specified thread.
*
* @param th specified thread.
*/
friend void suspend(Thread &th)
{pthread_kill(th._tid, _SIG_THREAD_SUSPEND);};
/**
* Resume execution of the specified thread.
*
* @param th specified thread.
*/
friend void resume(Thread &th)
{pthread_kill(th._tid, _SIG_THREAD_RESUME);};
/**
* Signal the semaphore that the specified thread is waiting for
* before beginning execution.
*
* @param th specified thread.
*/
friend inline void operator++(Thread &th)
{th._start->Post();};
/**
* Start execution of a specified thread.
*/
friend inline int start(Thread &th, Semaphore *start)
{return th.Start(start);};
/**
* Install a signal handler for use by APE threads and
* the OnSignal() event notification handler.
*
* @param signo posix signal id.
*/
friend void siginstall(int signo);
};
/**
* This class allows the creation of a thread context unique "pointer"
* that can be set and retrieved and can be used to create thread specific
* data areas for implementing "thread safe" library routines.
*
* @author David Sugar <dyfet@ostel.com>
* @short container for thread specific data storage.
*/
class ThreadKey
{
private:
pthread_key_t key;
public:
/**
* Create a unique thread specific container.
*/
ThreadKey();
/**
* Destroy a thread specific container and any contents reserved.
*/
~ThreadKey();
/**
* Get the value of the pointer for the thread specific data
* container. A unique pointer can be set for each execution
* context.
*
* @return a unique void * for each execution context.
*/
void *getKey(void);
/**
* Set the value of the pointer for the current thread specific
* execution context. This can be used to store thread context
* specific data.
*
* @param ptr to thread context specific data.
*/
void setKey(void *);
};
inline void *getKey(ThreadKey &tk)
{return tk.getKey();};
inline void setKey(ThreadKey &tk, void *ptr)
{tk.setKey(ptr);};
inline void operator ++(Mutex &m)
{m.EnterMutex();};
inline void operator --(Mutex &m)
{m.LeaveMutex();};
inline void operator ++(Semaphore &s)
{s.Post();};
inline void operator --(Semaphore &s)
{s.Wait();};
inline void operator ++(Event &s)
{s.Signal();};
inline void operator --(Event &s)
{s.Wait();};
inline void signal(Thread &th, int signo)
{th.SignalThread(signo);};
inline void signal(Event &ev)
{ev.Signal();};
inline void signal(Semaphore &sem)
{sem.Post();};
inline void wait(Semaphore &sem)
{sem.Wait();};
inline void wait(Event &ev, timeout_t timer)
{ev.Wait(timer);};
inline void reset(Event &ev)
{ev.Reset();};
inline int get(Buffer &b, void *o)
{return b.Wait(o);};
inline int put(Buffer &b, void *o)
{return b.Post(o);};
inline int peek(Buffer &b, void *o)
{return b.Peek(o);};
int operator++(MutexCounter &mc);
int operator--(MutexCounter &mc);
struct timespec *gettimeout(struct timespec *spec, timeout_t timeout);
void ape_sleep(timeout_t msec);
void ape_yield(void);
void wait(signo_t signo);
void pdetach(void);
#endif
Documentation generated by dyfet@home.sys on Thu Dec 16 09:54:26 EST 1999