1
// Package : omnithread
2
// omnithread.h Created : 7/94 tjr
4
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
6
// This file is part of the omnithread library
8
// The omnithread library is free software; you can redistribute it and/or
9
// modify it under the terms of the GNU Library General Public
10
// License as published by the Free Software Foundation; either
11
// version 2 of the License, or (at your option) any later version.
13
// This library is distributed in the hope that it will be useful,
14
// but WITHOUT ANY WARRANTY; without even the implied warranty of
15
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
// Library General Public License for more details.
18
// You should have received a copy of the GNU Library General Public
19
// License along with this library; if not, write to the Free
20
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25
// Interface to OMNI thread abstraction.
27
// This file declares classes for threads and synchronisation objects
28
// (mutexes, condition variables and counting semaphores).
30
// Wherever a seemingly arbitrary choice has had to be made as to the interface
31
// provided, the intention here has been to be as POSIX-like as possible. This
32
// is why there is no semaphore timed wait, for example.
35
#ifndef __omnithread_h_
36
#define __omnithread_h_
48
// OMNI_THREAD_EXPOSE can be defined as public or protected to expose the
49
// implementation class - this may be useful for debugging. Hopefully this
50
// won't change the underlying structure which the compiler generates so that
51
// this can work without recompiling the library.
54
#ifndef OMNI_THREAD_EXPOSE
55
#define OMNI_THREAD_EXPOSE private
60
// Include implementation-specific header file.
62
// This must define 4 CPP macros of the form OMNI_x_IMPLEMENTATION for mutex,
63
// condition variable, semaphore and thread. Each should define any
64
// implementation-specific members of the corresponding classes.
68
#if defined(__arm__) && defined(__atmos__)
69
#include <omnithread/posix.h>
71
#elif defined(__alpha__) && defined(__osf1__)
72
#include <omnithread/posix.h>
74
#elif defined(__powerpc__) && defined(__aix__)
75
#include <omnithread/posix.h>
77
#elif defined(__hpux__)
78
#include <omnithread/posix.h>
80
#elif defined(__WIN32__)
85
// Using MSVC++ to compile. If compiling library as a DLL,
86
// define _OMNITHREAD_DLL. If compiling as a statuc library, define
88
// If compiling an application that is to be statically linked to omnithread,
89
// define _WINSTATIC (if the application is to be dynamically linked,
90
// there is no need to define any of these macros).
92
#if defined (_OMNITHREAD_DLL) && defined(_WINSTATIC)
93
#error "Both _OMNITHREAD_DLL and _WINSTATIC are defined."
94
#elif defined(_OMNITHREAD_DLL)
95
#define _OMNITHREAD_NTDLL_ __declspec(dllexport)
96
#elif !defined(_WINSTATIC)
97
#define _OMNITHREAD_NTDLL_ __declspec(dllimport)
98
#elif defined(_WINSTATIC)
99
#define _OMNITHREAD_NTDLL_
101
// _OMNITHREAD_DLL && _WINSTATIC
105
// Not using MSVC++ to compile
106
#define _OMNITHREAD_NTDLL_
111
#elif defined(__sunos__) && (__OSVERSION__ == 5)
113
#include <omnithread/posix.h>
115
#include <omnithread/solaris.h>
118
#elif defined(__linux__)
119
#include <omnithread/posix.h>
121
#elif defined(__nextstep__)
122
#include <omnithread/mach.h>
125
#include <omnithread/posix.h>
127
#elif defined(__SINIX__)
128
#include <omnithread/posix.h>
130
#elif defined(__osr5__)
131
#include <omnithread/posix.h>
133
#elif defined(__irix__)
134
#include <omnithread/posix.h>
137
#error "No implementation header file"
140
#if !defined(__WIN32__)
141
#define _OMNITHREAD_NTDLL_
144
#if (!defined(OMNI_MUTEX_IMPLEMENTATION) || \
145
!defined(OMNI_CONDITION_IMPLEMENTATION) || \
146
!defined(OMNI_SEMAPHORE_IMPLEMENTATION) || \
147
!defined(OMNI_THREAD_IMPLEMENTATION))
148
#error "Implementation header file incomplete"
153
// This exception is thrown in the event of a fatal error.
156
class _OMNITHREAD_NTDLL_ omni_thread_fatal {
159
omni_thread_fatal(int e = 0) : error(e) {}
164
// This exception is thrown when an operation is invoked with invalid
168
class _OMNITHREAD_NTDLL_ omni_thread_invalid {};
171
///////////////////////////////////////////////////////////////////////////
175
///////////////////////////////////////////////////////////////////////////
177
class _OMNITHREAD_NTDLL_ omni_mutex {
185
void acquire(void) { lock(); }
186
void release(void) { unlock(); }
187
// the names lock and unlock are preferred over acquire and release
188
// since we are attempting to be as POSIX-like as possible.
190
friend class omni_condition;
193
// dummy copy constructor and operator= to prevent copying
194
omni_mutex(const omni_mutex&);
195
omni_mutex& operator=(const omni_mutex&);
198
OMNI_MUTEX_IMPLEMENTATION
202
// As an alternative to:
209
// you can use a single instance of the omni_mutex_lock class:
212
// omni_mutex_lock l(mutex);
216
// This has the advantage that mutex.unlock() will be called automatically
217
// when an exception is thrown.
220
class _OMNITHREAD_NTDLL_ omni_mutex_lock {
223
omni_mutex_lock(omni_mutex& m) : mutex(m) { mutex.lock(); }
224
~omni_mutex_lock(void) { mutex.unlock(); }
226
// dummy copy constructor and operator= to prevent copying
227
omni_mutex_lock(const omni_mutex_lock&);
228
omni_mutex_lock& operator=(const omni_mutex_lock&);
232
///////////////////////////////////////////////////////////////////////////
234
// Condition variable
236
///////////////////////////////////////////////////////////////////////////
238
class _OMNITHREAD_NTDLL_ omni_condition {
243
omni_condition(omni_mutex* m);
244
// constructor must be given a pointer to an existing mutex. The
245
// condition variable is then linked to the mutex, so that there is an
246
// implicit unlock and lock around wait() and timed_wait().
248
~omni_condition(void);
251
// wait for the condition variable to be signalled. The mutex is
252
// implicitly released before waiting and locked again after waking up.
253
// If wait() is called by multiple threads, a signal may wake up more
254
// than one thread. See POSIX threads documentation for details.
256
int timedwait(unsigned long secs, unsigned long nanosecs = 0);
257
// timedwait() is given an absolute time to wait until. To wait for a
258
// relative time from now, use omni_thread::get_time. See POSIX threads
259
// documentation for why absolute times are better than relative.
260
// Returns 1 (true) if successfully signalled, 0 (false) if time
264
// if one or more threads have called wait(), signal wakes up at least
265
// one of them, possibly more. See POSIX threads documentation for
268
void broadcast(void);
269
// broadcast is like signal but wakes all threads which have called
273
// dummy copy constructor and operator= to prevent copying
274
omni_condition(const omni_condition&);
275
omni_condition& operator=(const omni_condition&);
278
OMNI_CONDITION_IMPLEMENTATION
282
///////////////////////////////////////////////////////////////////////////
284
// Counting semaphore
286
///////////////////////////////////////////////////////////////////////////
288
class _OMNITHREAD_NTDLL_ omni_semaphore {
291
omni_semaphore(unsigned int initial = 1);
292
~omni_semaphore(void);
295
// if semaphore value is > 0 then decrement it and carry on. If it's
296
// already 0 then block.
299
// if semaphore value is > 0 then decrement it and return 1 (true).
300
// If it's already 0 then return 0 (false).
303
// if any threads are blocked in wait(), wake one of them up. Otherwise
304
// increment the value of the semaphore.
307
// dummy copy constructor and operator= to prevent copying
308
omni_semaphore(const omni_semaphore&);
309
omni_semaphore& operator=(const omni_semaphore&);
312
OMNI_SEMAPHORE_IMPLEMENTATION
316
// A helper class for semaphores, similar to omni_mutex_lock above.
319
class _OMNITHREAD_NTDLL_ omni_semaphore_lock {
322
omni_semaphore_lock(omni_semaphore& s) : sem(s) { sem.wait(); }
323
~omni_semaphore_lock(void) { sem.post(); }
325
// dummy copy constructor and operator= to prevent copying
326
omni_semaphore_lock(const omni_semaphore_lock&);
327
omni_semaphore_lock& operator=(const omni_semaphore_lock&);
331
///////////////////////////////////////////////////////////////////////////
335
///////////////////////////////////////////////////////////////////////////
337
class _OMNITHREAD_NTDLL_ omni_thread {
348
STATE_NEW, // thread object exists but thread hasn't
350
STATE_RUNNING, // thread is running.
351
STATE_TERMINATED // thread has terminated but storage has not
352
// been reclaimed (i.e. waiting to be joined).
356
// Constructors set up the thread object but the thread won't start until
357
// start() is called. The create method can be used to construct and start
358
// a thread in a single call.
361
omni_thread(void (*fn)(void*), void* arg = NULL,
362
priority_t pri = PRIORITY_NORMAL);
363
omni_thread(void* (*fn)(void*), void* arg = NULL,
364
priority_t pri = PRIORITY_NORMAL);
365
// these constructors create a thread which will run the given function
366
// when start() is called. The thread will be detached if given a
367
// function with void return type, undetached if given a function
368
// returning void*. If a thread is detached, storage for the thread is
369
// reclaimed automatically on termination. Only an undetached thread
373
// start() causes a thread created with one of the constructors to
374
// start executing the appropriate function.
378
omni_thread(void* arg = NULL, priority_t pri = PRIORITY_NORMAL);
379
// this constructor is used in a derived class. The thread will
380
// execute the run() or run_undetached() member functions depending on
381
// whether start() or start_undetached() is called respectively.
383
void start_undetached(void);
384
// can be used with the above constructor in a derived class to cause
385
// the thread to be undetached. In this case the thread executes the
386
// run_undetached member function.
388
virtual ~omni_thread(void);
389
// destructor cannot be called by user (except via a derived class).
390
// Use exit() or cancel() instead. This also means a thread object must
391
// be allocated with new - it cannot be statically or automatically
392
// allocated. The destructor of a class that inherits from omni_thread
393
// shouldn't be public either (otherwise the thread object can be
394
// destroyed while the underlying thread is still running).
399
// join causes the calling thread to wait for another's completion,
400
// putting the return value in the variable of type void* whose address
401
// is given (unless passed a null pointer). Only undetached threads
402
// may be joined. Storage for the thread will be reclaimed.
404
void set_priority(priority_t);
405
// set the priority of the thread.
407
static omni_thread* create(void (*fn)(void*), void* arg = NULL,
408
priority_t pri = PRIORITY_NORMAL);
409
static omni_thread* create(void* (*fn)(void*), void* arg = NULL,
410
priority_t pri = PRIORITY_NORMAL);
411
// create spawns a new thread executing the given function with the
412
// given argument at the given priority. Returns a pointer to the
413
// thread object. It simply constructs a new thread object then calls
416
static void exit(void* return_value = NULL);
417
// causes the calling thread to terminate.
419
static omni_thread* self(void);
420
// returns the calling thread's omni_thread object.
421
// If the calling thread is not the main thread and
422
// is not created using this library, returns 0.
424
static void yield(void);
425
// allows another thread to run.
427
static void sleep(unsigned long secs, unsigned long nanosecs = 0);
428
// sleeps for the given time.
430
static void get_time(unsigned long* abs_sec, unsigned long* abs_nsec,
431
unsigned long rel_sec = 0, unsigned long rel_nsec=0);
432
// calculates an absolute time in seconds and nanoseconds, suitable for
433
// use in timed_waits on condition variables, which is the current time
434
// plus the given relative offset.
438
virtual void run(void* arg) {}
439
virtual void* run_undetached(void* arg) { return NULL; }
440
// can be overridden in a derived class. When constructed using the
441
// the constructor omni_thread(void*, priority_t), these functions are
442
// called by start() and start_undetached() respectively.
444
void common_constructor(void* arg, priority_t pri, int det);
445
// implements the common parts of the constructors.
448
// used to protect any members which can change after construction,
449
// i.e. the following 2 members:
452
priority_t _priority;
454
static omni_mutex* next_id_mutex;
458
void (*fn_void)(void*);
459
void* (*fn_ret)(void*);
465
priority_t priority(void) {
467
// return this thread's priority.
469
omni_mutex_lock l(mutex);
473
state_t state(void) {
475
// return thread state (invalid, new, running or terminated).
477
omni_mutex_lock l(mutex);
481
int id(void) { return _id; }
482
// return unique thread id within the current process.
485
// This class plus the instance of it declared below allows us to execute
486
// some initialisation code before main() is called.
488
class _OMNITHREAD_NTDLL_ init_t {
497
OMNI_THREAD_IMPLEMENTATION
500
static omni_thread::init_t omni_thread_init;