503 lines
15 KiB
C++
503 lines
15 KiB
C++
// Package : omnithread
|
|
// omnithread.h Created : 7/94 tjr
|
|
//
|
|
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
|
//
|
|
// This file is part of the omnithread library
|
|
//
|
|
// The omnithread library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Library General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library 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
|
|
// Library General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Library General Public
|
|
// License along with this library; if not, write to the Free
|
|
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
// 02111-1307, USA
|
|
//
|
|
|
|
//
|
|
// Interface to OMNI thread abstraction.
|
|
//
|
|
// This file declares classes for threads and synchronisation objects
|
|
// (mutexes, condition variables and counting semaphores).
|
|
//
|
|
// Wherever a seemingly arbitrary choice has had to be made as to the interface
|
|
// provided, the intention here has been to be as POSIX-like as possible. This
|
|
// is why there is no semaphore timed wait, for example.
|
|
//
|
|
|
|
#ifndef __omnithread_h_
|
|
#define __omnithread_h_
|
|
|
|
#ifndef NULL
|
|
#define NULL (void*)0
|
|
#endif
|
|
|
|
class omni_mutex;
|
|
class omni_condition;
|
|
class omni_semaphore;
|
|
class omni_thread;
|
|
|
|
//
|
|
// OMNI_THREAD_EXPOSE can be defined as public or protected to expose the
|
|
// implementation class - this may be useful for debugging. Hopefully this
|
|
// won't change the underlying structure which the compiler generates so that
|
|
// this can work without recompiling the library.
|
|
//
|
|
|
|
#ifndef OMNI_THREAD_EXPOSE
|
|
#define OMNI_THREAD_EXPOSE private
|
|
#endif
|
|
|
|
|
|
//
|
|
// Include implementation-specific header file.
|
|
//
|
|
// This must define 4 CPP macros of the form OMNI_x_IMPLEMENTATION for mutex,
|
|
// condition variable, semaphore and thread. Each should define any
|
|
// implementation-specific members of the corresponding classes.
|
|
//
|
|
|
|
|
|
#if defined(__arm__) && defined(__atmos__)
|
|
#include <omnithread/posix.h>
|
|
|
|
#elif defined(__alpha__) && defined(__osf1__)
|
|
#include <omnithread/posix.h>
|
|
|
|
#elif defined(__powerpc__) && defined(__aix__)
|
|
#include <omnithread/posix.h>
|
|
|
|
#elif defined(__hpux__)
|
|
#include <omnithread/posix.h>
|
|
|
|
#elif defined(__WIN32__)
|
|
#include "nt.h"
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
// Using MSVC++ to compile. If compiling library as a DLL,
|
|
// define _OMNITHREAD_DLL. If compiling as a statuc library, define
|
|
// _WINSTATIC
|
|
// If compiling an application that is to be statically linked to omnithread,
|
|
// define _WINSTATIC (if the application is to be dynamically linked,
|
|
// there is no need to define any of these macros).
|
|
|
|
#if defined (_OMNITHREAD_DLL) && defined(_WINSTATIC)
|
|
#error "Both _OMNITHREAD_DLL and _WINSTATIC are defined."
|
|
#elif defined(_OMNITHREAD_DLL)
|
|
#define _OMNITHREAD_NTDLL_ __declspec(dllexport)
|
|
#elif !defined(_WINSTATIC)
|
|
#define _OMNITHREAD_NTDLL_ __declspec(dllimport)
|
|
#elif defined(_WINSTATIC)
|
|
#define _OMNITHREAD_NTDLL_
|
|
#endif
|
|
// _OMNITHREAD_DLL && _WINSTATIC
|
|
|
|
#else
|
|
|
|
// Not using MSVC++ to compile
|
|
#define _OMNITHREAD_NTDLL_
|
|
|
|
#endif
|
|
// _MSC_VER
|
|
|
|
#elif defined(__sunos__) && (__OSVERSION__ == 5)
|
|
#ifdef UsePthread
|
|
#include <omnithread/posix.h>
|
|
#else
|
|
#include <omnithread/solaris.h>
|
|
#endif
|
|
|
|
#elif defined(__linux__)
|
|
#include <omnithread/posix.h>
|
|
|
|
#elif defined(__nextstep__)
|
|
#include <omnithread/mach.h>
|
|
|
|
#elif defined(__VMS)
|
|
#include <omnithread/posix.h>
|
|
|
|
#elif defined(__SINIX__)
|
|
#include <omnithread/posix.h>
|
|
|
|
#elif defined(__osr5__)
|
|
#include <omnithread/posix.h>
|
|
|
|
#elif defined(__irix__)
|
|
#include <omnithread/posix.h>
|
|
|
|
#else
|
|
#error "No implementation header file"
|
|
#endif
|
|
|
|
#if !defined(__WIN32__)
|
|
#define _OMNITHREAD_NTDLL_
|
|
#endif
|
|
|
|
#if (!defined(OMNI_MUTEX_IMPLEMENTATION) || \
|
|
!defined(OMNI_CONDITION_IMPLEMENTATION) || \
|
|
!defined(OMNI_SEMAPHORE_IMPLEMENTATION) || \
|
|
!defined(OMNI_THREAD_IMPLEMENTATION))
|
|
#error "Implementation header file incomplete"
|
|
#endif
|
|
|
|
|
|
//
|
|
// This exception is thrown in the event of a fatal error.
|
|
//
|
|
|
|
class _OMNITHREAD_NTDLL_ omni_thread_fatal {
|
|
public:
|
|
int error;
|
|
omni_thread_fatal(int e = 0) : error(e) {}
|
|
};
|
|
|
|
|
|
//
|
|
// This exception is thrown when an operation is invoked with invalid
|
|
// arguments.
|
|
//
|
|
|
|
class _OMNITHREAD_NTDLL_ omni_thread_invalid {};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Mutex
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
class _OMNITHREAD_NTDLL_ omni_mutex {
|
|
|
|
public:
|
|
omni_mutex(void);
|
|
~omni_mutex(void);
|
|
|
|
void lock(void);
|
|
void unlock(void);
|
|
void acquire(void) { lock(); }
|
|
void release(void) { unlock(); }
|
|
// the names lock and unlock are preferred over acquire and release
|
|
// since we are attempting to be as POSIX-like as possible.
|
|
|
|
friend class omni_condition;
|
|
|
|
private:
|
|
// dummy copy constructor and operator= to prevent copying
|
|
omni_mutex(const omni_mutex&);
|
|
omni_mutex& operator=(const omni_mutex&);
|
|
|
|
OMNI_THREAD_EXPOSE:
|
|
OMNI_MUTEX_IMPLEMENTATION
|
|
};
|
|
|
|
//
|
|
// As an alternative to:
|
|
// {
|
|
// mutex.lock();
|
|
// .....
|
|
// mutex.unlock();
|
|
// }
|
|
//
|
|
// you can use a single instance of the omni_mutex_lock class:
|
|
//
|
|
// {
|
|
// omni_mutex_lock l(mutex);
|
|
// ....
|
|
// }
|
|
//
|
|
// This has the advantage that mutex.unlock() will be called automatically
|
|
// when an exception is thrown.
|
|
//
|
|
|
|
class _OMNITHREAD_NTDLL_ omni_mutex_lock {
|
|
omni_mutex& mutex;
|
|
public:
|
|
omni_mutex_lock(omni_mutex& m) : mutex(m) { mutex.lock(); }
|
|
~omni_mutex_lock(void) { mutex.unlock(); }
|
|
private:
|
|
// dummy copy constructor and operator= to prevent copying
|
|
omni_mutex_lock(const omni_mutex_lock&);
|
|
omni_mutex_lock& operator=(const omni_mutex_lock&);
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Condition variable
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
class _OMNITHREAD_NTDLL_ omni_condition {
|
|
|
|
omni_mutex* mutex;
|
|
|
|
public:
|
|
omni_condition(omni_mutex* m);
|
|
// constructor must be given a pointer to an existing mutex. The
|
|
// condition variable is then linked to the mutex, so that there is an
|
|
// implicit unlock and lock around wait() and timed_wait().
|
|
|
|
~omni_condition(void);
|
|
|
|
void wait(void);
|
|
// wait for the condition variable to be signalled. The mutex is
|
|
// implicitly released before waiting and locked again after waking up.
|
|
// If wait() is called by multiple threads, a signal may wake up more
|
|
// than one thread. See POSIX threads documentation for details.
|
|
|
|
int timedwait(unsigned long secs, unsigned long nanosecs = 0);
|
|
// timedwait() is given an absolute time to wait until. To wait for a
|
|
// relative time from now, use omni_thread::get_time. See POSIX threads
|
|
// documentation for why absolute times are better than relative.
|
|
// Returns 1 (true) if successfully signalled, 0 (false) if time
|
|
// expired.
|
|
|
|
void signal(void);
|
|
// if one or more threads have called wait(), signal wakes up at least
|
|
// one of them, possibly more. See POSIX threads documentation for
|
|
// details.
|
|
|
|
void broadcast(void);
|
|
// broadcast is like signal but wakes all threads which have called
|
|
// wait().
|
|
|
|
private:
|
|
// dummy copy constructor and operator= to prevent copying
|
|
omni_condition(const omni_condition&);
|
|
omni_condition& operator=(const omni_condition&);
|
|
|
|
OMNI_THREAD_EXPOSE:
|
|
OMNI_CONDITION_IMPLEMENTATION
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Counting semaphore
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
class _OMNITHREAD_NTDLL_ omni_semaphore {
|
|
|
|
public:
|
|
omni_semaphore(unsigned int initial = 1);
|
|
~omni_semaphore(void);
|
|
|
|
void wait(void);
|
|
// if semaphore value is > 0 then decrement it and carry on. If it's
|
|
// already 0 then block.
|
|
|
|
int trywait(void);
|
|
// if semaphore value is > 0 then decrement it and return 1 (true).
|
|
// If it's already 0 then return 0 (false).
|
|
|
|
void post(void);
|
|
// if any threads are blocked in wait(), wake one of them up. Otherwise
|
|
// increment the value of the semaphore.
|
|
|
|
private:
|
|
// dummy copy constructor and operator= to prevent copying
|
|
omni_semaphore(const omni_semaphore&);
|
|
omni_semaphore& operator=(const omni_semaphore&);
|
|
|
|
OMNI_THREAD_EXPOSE:
|
|
OMNI_SEMAPHORE_IMPLEMENTATION
|
|
};
|
|
|
|
//
|
|
// A helper class for semaphores, similar to omni_mutex_lock above.
|
|
//
|
|
|
|
class _OMNITHREAD_NTDLL_ omni_semaphore_lock {
|
|
omni_semaphore& sem;
|
|
public:
|
|
omni_semaphore_lock(omni_semaphore& s) : sem(s) { sem.wait(); }
|
|
~omni_semaphore_lock(void) { sem.post(); }
|
|
private:
|
|
// dummy copy constructor and operator= to prevent copying
|
|
omni_semaphore_lock(const omni_semaphore_lock&);
|
|
omni_semaphore_lock& operator=(const omni_semaphore_lock&);
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Thread
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
class _OMNITHREAD_NTDLL_ omni_thread {
|
|
|
|
public:
|
|
|
|
enum priority_t {
|
|
PRIORITY_LOW,
|
|
PRIORITY_NORMAL,
|
|
PRIORITY_HIGH
|
|
};
|
|
|
|
enum state_t {
|
|
STATE_NEW, // thread object exists but thread hasn't
|
|
// started yet.
|
|
STATE_RUNNING, // thread is running.
|
|
STATE_TERMINATED // thread has terminated but storage has not
|
|
// been reclaimed (i.e. waiting to be joined).
|
|
};
|
|
|
|
//
|
|
// Constructors set up the thread object but the thread won't start until
|
|
// start() is called. The create method can be used to construct and start
|
|
// a thread in a single call.
|
|
//
|
|
|
|
omni_thread(void (*fn)(void*), void* arg = NULL,
|
|
priority_t pri = PRIORITY_NORMAL);
|
|
omni_thread(void* (*fn)(void*), void* arg = NULL,
|
|
priority_t pri = PRIORITY_NORMAL);
|
|
// these constructors create a thread which will run the given function
|
|
// when start() is called. The thread will be detached if given a
|
|
// function with void return type, undetached if given a function
|
|
// returning void*. If a thread is detached, storage for the thread is
|
|
// reclaimed automatically on termination. Only an undetached thread
|
|
// can be joined.
|
|
|
|
void start(void);
|
|
// start() causes a thread created with one of the constructors to
|
|
// start executing the appropriate function.
|
|
|
|
protected:
|
|
|
|
omni_thread(void* arg = NULL, priority_t pri = PRIORITY_NORMAL);
|
|
// this constructor is used in a derived class. The thread will
|
|
// execute the run() or run_undetached() member functions depending on
|
|
// whether start() or start_undetached() is called respectively.
|
|
|
|
void start_undetached(void);
|
|
// can be used with the above constructor in a derived class to cause
|
|
// the thread to be undetached. In this case the thread executes the
|
|
// run_undetached member function.
|
|
|
|
virtual ~omni_thread(void);
|
|
// destructor cannot be called by user (except via a derived class).
|
|
// Use exit() or cancel() instead. This also means a thread object must
|
|
// be allocated with new - it cannot be statically or automatically
|
|
// allocated. The destructor of a class that inherits from omni_thread
|
|
// shouldn't be public either (otherwise the thread object can be
|
|
// destroyed while the underlying thread is still running).
|
|
|
|
public:
|
|
|
|
void join(void**);
|
|
// join causes the calling thread to wait for another's completion,
|
|
// putting the return value in the variable of type void* whose address
|
|
// is given (unless passed a null pointer). Only undetached threads
|
|
// may be joined. Storage for the thread will be reclaimed.
|
|
|
|
void set_priority(priority_t);
|
|
// set the priority of the thread.
|
|
|
|
static omni_thread* create(void (*fn)(void*), void* arg = NULL,
|
|
priority_t pri = PRIORITY_NORMAL);
|
|
static omni_thread* create(void* (*fn)(void*), void* arg = NULL,
|
|
priority_t pri = PRIORITY_NORMAL);
|
|
// create spawns a new thread executing the given function with the
|
|
// given argument at the given priority. Returns a pointer to the
|
|
// thread object. It simply constructs a new thread object then calls
|
|
// start.
|
|
|
|
static void exit(void* return_value = NULL);
|
|
// causes the calling thread to terminate.
|
|
|
|
static omni_thread* self(void);
|
|
// returns the calling thread's omni_thread object.
|
|
// If the calling thread is not the main thread and
|
|
// is not created using this library, returns 0.
|
|
|
|
static void yield(void);
|
|
// allows another thread to run.
|
|
|
|
static void sleep(unsigned long secs, unsigned long nanosecs = 0);
|
|
// sleeps for the given time.
|
|
|
|
static void get_time(unsigned long* abs_sec, unsigned long* abs_nsec,
|
|
unsigned long rel_sec = 0, unsigned long rel_nsec=0);
|
|
// calculates an absolute time in seconds and nanoseconds, suitable for
|
|
// use in timed_waits on condition variables, which is the current time
|
|
// plus the given relative offset.
|
|
|
|
private:
|
|
|
|
virtual void run(void* arg) {}
|
|
virtual void* run_undetached(void* arg) { return NULL; }
|
|
// can be overridden in a derived class. When constructed using the
|
|
// the constructor omni_thread(void*, priority_t), these functions are
|
|
// called by start() and start_undetached() respectively.
|
|
|
|
void common_constructor(void* arg, priority_t pri, int det);
|
|
// implements the common parts of the constructors.
|
|
|
|
omni_mutex mutex;
|
|
// used to protect any members which can change after construction,
|
|
// i.e. the following 2 members:
|
|
|
|
state_t _state;
|
|
priority_t _priority;
|
|
|
|
static omni_mutex* next_id_mutex;
|
|
static int next_id;
|
|
int _id;
|
|
|
|
void (*fn_void)(void*);
|
|
void* (*fn_ret)(void*);
|
|
void* thread_arg;
|
|
int detached;
|
|
|
|
public:
|
|
|
|
priority_t priority(void) {
|
|
|
|
// return this thread's priority.
|
|
|
|
omni_mutex_lock l(mutex);
|
|
return _priority;
|
|
}
|
|
|
|
state_t state(void) {
|
|
|
|
// return thread state (invalid, new, running or terminated).
|
|
|
|
omni_mutex_lock l(mutex);
|
|
return _state;
|
|
}
|
|
|
|
int id(void) { return _id; }
|
|
// return unique thread id within the current process.
|
|
|
|
|
|
// This class plus the instance of it declared below allows us to execute
|
|
// some initialisation code before main() is called.
|
|
|
|
class _OMNITHREAD_NTDLL_ init_t {
|
|
static int count;
|
|
public:
|
|
init_t(void);
|
|
};
|
|
|
|
friend class init_t;
|
|
|
|
OMNI_THREAD_EXPOSE:
|
|
OMNI_THREAD_IMPLEMENTATION
|
|
};
|
|
|
|
static omni_thread::init_t omni_thread_init;
|
|
|
|
#endif
|