GNU Radio 2.x: omnithread.h Source File

Main Page | Modules | Namespace List | Class Hierarchy | Compound List | File List | Namespace Members | Compound Members | File Members

# omnithread.h

Go to the documentation of this file.

    00001 // -*- Mode: C++; -*-
    00002 //                              Package : omnithread
    00003 // omnithread.h                 Created : 7/94 tjr
    00004 //
    00005 //    Copyright (C) 1994,1995,1996, 1997 Olivetti & Oracle Research Laboratory
    00006 //
    00007 //    This file is part of the omnithread library
    00008 //
    00009 //    The omnithread library is free software; you can redistribute it and/or
    00010 //    modify it under the terms of the GNU Library General Public
    00011 //    License as published by the Free Software Foundation; either
    00012 //    version 2 of the License, or (at your option) any later version.
    00013 //
    00014 //    This library is distributed in the hope that it will be useful,
    00015 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
    00016 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00017 //    Library General Public License for more details.
    00018 //
    00019 //    You should have received a copy of the GNU Library General Public
    00020 //    License along with this library; if not, write to the Free
    00021 //    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    00022 //    02111-1307, USA
    00023 //
    00024
    00025 //
    00026 // Interface to OMNI thread abstraction.
    00027 //
    00028 // This file declares classes for threads and synchronisation objects
    00029 // (mutexes, condition variables and counting semaphores).
    00030 //
    00031 // Wherever a seemingly arbitrary choice has had to be made as to the interface
    00032 // provided, the intention here has been to be as POSIX-like as possible.  This
    00033 // is why there is no semaphore timed wait, for example.
    00034 //
    00035
    00036 #ifndef __omnithread_h_
    00037 #define __omnithread_h_
    00038
    00039 #ifndef NULL
    00040 #define NULL 0
    00041 #endif
    00042
    00043 class omni_mutex;
    00044 class omni_condition;
    00045 class omni_semaphore;
    00046 class omni_thread;
    00047
    00048 //
    00049 // OMNI_THREAD_EXPOSE can be defined as public or protected to expose the
    00050 // implementation class - this may be useful for debugging.  Hopefully this
    00051 // won't change the underlying structure which the compiler generates so that
    00052 // this can work without recompiling the library.
    00053 //
    00054
    00055 #ifndef OMNI_THREAD_EXPOSE
    00056 #define OMNI_THREAD_EXPOSE private
    00057 #endif
    00058
    00059 //
    00060 // Include implementation-specific header file.
    00061 //
    00062 // This must define 4 CPP macros of the form OMNI_x_IMPLEMENTATION for mutex,
    00063 // condition variable, semaphore and thread.  Each should define any
    00064 // implementation-specific members of the corresponding classes.
    00065 //
    00066
    00067
    00068 //
    00069 // For now, we assume they've always got a Posix Threads implementation.
    00070 // If not, it'll take some configure hacking to sort it out, along with
    00071 // the relevant libraries to link with, etc.
    00072 //
    00073
    00074 #if !defined(OMNITHREAD_POSIX) && !defined(OMNITHREAD_NT) && defined HAVE_CONFIG_H
    00075 #include <config.h>
    00076 #endif
    00077
    00078 #if defined(OMNITHREAD_POSIX)
    00079 #include <ot_posix.h>
    00080
    00081 #elif defined(OMNITHREAD_NT)
    00082 #include <ot_nt.h>
    00083
    00084 #ifdef _MSC_VER
    00085
    00086 // Using MSVC++ to compile. If compiling library as a DLL,
    00087 // define _OMNITHREAD_DLL. If compiling as a statuc library, define
    00088 // _WINSTATIC
    00089 // If compiling an application that is to be statically linked to omnithread,
    00090 // define _WINSTATIC (if the application is  to be dynamically linked,
    00091 // there is no need to define any of these macros).
    00092
    00093 #if defined (_OMNITHREAD_DLL) && defined(_WINSTATIC)
    00094 #error "Both _OMNITHREAD_DLL and _WINSTATIC are defined."
    00095 #elif defined(_OMNITHREAD_DLL)
    00096 #define _OMNITHREAD_NTDLL_ __declspec(dllexport)
    00097 #elif !defined(_WINSTATIC)
    00098 #define _OMNITHREAD_NTDLL_ __declspec(dllimport)
    00099 #elif defined(_WINSTATIC)
    00100 #define _OMNITHREAD_NTDLL_
    00101 #endif
    00102  // _OMNITHREAD_DLL && _WINSTATIC
    00103
    00104 #else
    00105
    00106 // Not using MSVC++ to compile
    00107 #define _OMNITHREAD_NTDLL_
    00108
    00109 #endif
    00110  // _MSC_VER
    00111
    00112 #elif defined(__vxWorks__)
    00113 #include <ot_VxThread.h>
    00114
    00115 #elif defined(__sunos__)
    00116 #if __OSVERSION__ != 5
    00117 // XXX Workaround for SUN C++ compiler (seen on 4.2) Template.DB code
    00118 //     regeneration bug. See omniORB2/CORBA_sysdep.h for details.
    00119 #if !defined(__SUNPRO_CC) || __OSVERSION__ != '5'
    00120 #error "Only SunOS 5.x or later is supported."
    00121 #endif
    00122 #endif
    00123 #ifdef UseSolarisThreads
    00124 #include <ot_solaris.h>
    00125 #else
    00126 #include <ot_posix.h>
    00127 #endif
    00128
    00129 #elif defined(__rtems__)
    00130 #include <ot_posix.h>
    00131 #include <sched.h>
    00132
    00133 #elif defined(__macos__)
    00134 #include <ot_posix.h>
    00135 #include <sched.h>
    00136
    00137 #else
    00138 #error "No implementation header file"
    00139 #endif
    00140
    00141
    00142 #if !defined(__WIN32__)
    00143 #define _OMNITHREAD_NTDLL_
    00144 #endif
    00145
    00146 #if (!defined(OMNI_MUTEX_IMPLEMENTATION)        || \
    00147      !defined(OMNI_MUTEX_LOCK_IMPLEMENTATION)   || \
    00148      !defined(OMNI_MUTEX_UNLOCK_IMPLEMENTATION) || \
    00149      !defined(OMNI_CONDITION_IMPLEMENTATION)    || \
    00150      !defined(OMNI_SEMAPHORE_IMPLEMENTATION)    || \
    00151      !defined(OMNI_THREAD_IMPLEMENTATION))
    00152 #error "Implementation header file incomplete"
    00153 #endif
    00154
    00155
    00156 //
    00157 // This exception is thrown in the event of a fatal error.
    00158 //
    00159
    00160 class _OMNITHREAD_NTDLL_ omni_thread_fatal {
    00161 public:
    00162     int error;
    00163     omni_thread_fatal(int e = 0) : error(e) {}
    00164 };
    00165
    00166
    00167 //
    00168 // This exception is thrown when an operation is invoked with invalid
    00169 // arguments.
    00170 //
    00171
    00172 class _OMNITHREAD_NTDLL_ omni_thread_invalid {};
    00173
    00174
    00176 //
    00177 // Mutex
    00178 //
    00180
    00181 class _OMNITHREAD_NTDLL_ omni_mutex {
    00182
    00183 public:
    00184     omni_mutex(void);
    00185     ~omni_mutex(void);
    00186
    00187     inline void lock(void)    { OMNI_MUTEX_LOCK_IMPLEMENTATION   }
    00188     inline void unlock(void)  { OMNI_MUTEX_UNLOCK_IMPLEMENTATION }
    00189     inline void acquire(void) { lock(); }
    00190     inline void release(void) { unlock(); }
    00191         // the names lock and unlock are preferred over acquire and release
    00192         // since we are attempting to be as POSIX-like as possible.
    00193
    00194     friend class omni_condition;
    00195
    00196 private:
    00197     // dummy copy constructor and operator= to prevent copying
    00198     omni_mutex(const omni_mutex&);
    00199     omni_mutex& operator=(const omni_mutex&);
    00200
    00201 OMNI_THREAD_EXPOSE:
    00202     OMNI_MUTEX_IMPLEMENTATION
    00203 };
    00204
    00205 //
    00206 // As an alternative to:
    00207 // {
    00208 //   mutex.lock();
    00209 //   .....
    00210 //   mutex.unlock();
    00211 // }
    00212 //
    00213 // you can use a single instance of the omni_mutex_lock class:
    00214 //
    00215 // {
    00216 //   omni_mutex_lock l(mutex);
    00217 //   ....
    00218 // }
    00219 //
    00220 // This has the advantage that mutex.unlock() will be called automatically
    00221 // when an exception is thrown.
    00222 //
    00223
    00224 class _OMNITHREAD_NTDLL_ omni_mutex_lock {
    00225     omni_mutex& mutex;
    00226 public:
    00227     omni_mutex_lock(omni_mutex& m) : mutex(m) { mutex.lock(); }
    00228     ~omni_mutex_lock(void) { mutex.unlock(); }
    00229 private:
    00230     // dummy copy constructor and operator= to prevent copying
    00231     omni_mutex_lock(const omni_mutex_lock&);
    00232     omni_mutex_lock& operator=(const omni_mutex_lock&);
    00233 };
    00234
    00235
    00237 //
    00238 // Condition variable
    00239 //
    00241
    00242 class _OMNITHREAD_NTDLL_ omni_condition {
    00243
    00244     omni_mutex* mutex;
    00245
    00246 public:
    00247     omni_condition(omni_mutex* m);
    00248         // constructor must be given a pointer to an existing mutex. The
    00249         // condition variable is then linked to the mutex, so that there is an
    00250         // implicit unlock and lock around wait() and timed_wait().
    00251
    00252     ~omni_condition(void);
    00253
    00254     void wait(void);
    00255         // wait for the condition variable to be signalled.  The mutex is
    00256         // implicitly released before waiting and locked again after waking up.
    00257         // If wait() is called by multiple threads, a signal may wake up more
    00258         // than one thread.  See POSIX threads documentation for details.
    00259
    00260     int timedwait(unsigned long secs, unsigned long nanosecs = 0);
    00261         // timedwait() is given an absolute time to wait until.  To wait for a
    00262         // relative time from now, use omni_thread::get_time. See POSIX threads
    00263         // documentation for why absolute times are better than relative.
    00264         // Returns 1 (true) if successfully signalled, 0 (false) if time
    00265         // expired.
    00266
    00267     void signal(void);
    00268         // if one or more threads have called wait(), signal wakes up at least
    00269         // one of them, possibly more.  See POSIX threads documentation for
    00270         // details.
    00271
    00272     void broadcast(void);
    00273         // broadcast is like signal but wakes all threads which have called
    00274         // wait().
    00275
    00276 private:
    00277     // dummy copy constructor and operator= to prevent copying
    00278     omni_condition(const omni_condition&);
    00279     omni_condition& operator=(const omni_condition&);
    00280
    00281 OMNI_THREAD_EXPOSE:
    00282     OMNI_CONDITION_IMPLEMENTATION
    00283 };
    00284
    00285
    00287 //
    00288 // Counting semaphore
    00289 //
    00291
    00292 class _OMNITHREAD_NTDLL_ omni_semaphore {
    00293
    00294 public:
    00295     omni_semaphore(unsigned int initial = 1);
    00296     ~omni_semaphore(void);
    00297
    00298     void wait(void);
    00299         // if semaphore value is > 0 then decrement it and carry on. If it's
    00300         // already 0 then block.
    00301
    00302     int trywait(void);
    00303         // if semaphore value is > 0 then decrement it and return 1 (true).
    00304         // If it's already 0 then return 0 (false).
    00305
    00306     void post(void);
    00307         // if any threads are blocked in wait(), wake one of them up. Otherwise
    00308         // increment the value of the semaphore.
    00309
    00310 private:
    00311     // dummy copy constructor and operator= to prevent copying
    00312     omni_semaphore(const omni_semaphore&);
    00313     omni_semaphore& operator=(const omni_semaphore&);
    00314
    00315 OMNI_THREAD_EXPOSE:
    00316     OMNI_SEMAPHORE_IMPLEMENTATION
    00317 };
    00318
    00319 //
    00320 // A helper class for semaphores, similar to omni_mutex_lock above.
    00321 //
    00322
    00323 class _OMNITHREAD_NTDLL_ omni_semaphore_lock {
    00324     omni_semaphore& sem;
    00325 public:
    00326     omni_semaphore_lock(omni_semaphore& s) : sem(s) { sem.wait(); }
    00327     ~omni_semaphore_lock(void) { sem.post(); }
    00328 private:
    00329     // dummy copy constructor and operator= to prevent copying
    00330     omni_semaphore_lock(const omni_semaphore_lock&);
    00331     omni_semaphore_lock& operator=(const omni_semaphore_lock&);
    00332 };
    00333
    00334
    00336 //
    00337 // Thread
    00338 //
    00340
    00341 class _OMNITHREAD_NTDLL_ omni_thread {
    00342
    00343 public:
    00344
    00345     enum priority_t {
    00346         PRIORITY_LOW,
    00347         PRIORITY_NORMAL,
    00348         PRIORITY_HIGH
    00349     };
    00350
    00351     enum state_t {
    00352         STATE_NEW,              // thread object exists but thread hasn't
    00353                                 // started yet.
    00354         STATE_RUNNING,          // thread is running.
    00355         STATE_TERMINATED        // thread has terminated but storage has not
    00356                                 // been reclaimed (i.e. waiting to be joined).
    00357     };
    00358
    00359     //
    00360     // Constructors set up the thread object but the thread won't start until
    00361     // start() is called. The create method can be used to construct and start
    00362     // a thread in a single call.
    00363     //
    00364
    00365     omni_thread(void (*fn)(void*), void* arg = NULL,
    00366                 priority_t pri = PRIORITY_NORMAL);
    00367     omni_thread(void* (*fn)(void*), void* arg = NULL,
    00368                 priority_t pri = PRIORITY_NORMAL);
    00369         // these constructors create a thread which will run the given function
    00370         // when start() is called.  The thread will be detached if given a
    00371         // function with void return type, undetached if given a function
    00372         // returning void*. If a thread is detached, storage for the thread is
    00373         // reclaimed automatically on termination. Only an undetached thread
    00374         // can be joined.
    00375
    00376     void start(void);
    00377         // start() causes a thread created with one of the constructors to
    00378         // start executing the appropriate function.
    00379
    00380 protected:
    00381
    00382     omni_thread(void* arg = NULL, priority_t pri = PRIORITY_NORMAL);
    00383         // this constructor is used in a derived class.  The thread will
    00384         // execute the run() or run_undetached() member functions depending on
    00385         // whether start() or start_undetached() is called respectively.
    00386
    00387     void start_undetached(void);
    00388         // can be used with the above constructor in a derived class to cause
    00389         // the thread to be undetached.  In this case the thread executes the
    00390         // run_undetached member function.
    00391
    00392     virtual ~omni_thread(void);
    00393         // destructor cannot be called by user (except via a derived class).
    00394         // Use exit() or cancel() instead. This also means a thread object must
    00395         // be allocated with new - it cannot be statically or automatically
    00396         // allocated. The destructor of a class that inherits from omni_thread
    00397         // shouldn't be public either (otherwise the thread object can be
    00398         // destroyed while the underlying thread is still running).
    00399
    00400 public:
    00401
    00402     void join(void**);
    00403         // join causes the calling thread to wait for another's completion,
    00404         // putting the return value in the variable of type void* whose address
    00405         // is given (unless passed a null pointer). Only undetached threads
    00406         // may be joined. Storage for the thread will be reclaimed.
    00407
    00408     void set_priority(priority_t);
    00409         // set the priority of the thread.
    00410
    00411     static omni_thread* create(void (*fn)(void*), void* arg = NULL,
    00412                                priority_t pri = PRIORITY_NORMAL);
    00413     static omni_thread* create(void* (*fn)(void*), void* arg = NULL,
    00414                                priority_t pri = PRIORITY_NORMAL);
    00415         // create spawns a new thread executing the given function with the
    00416         // given argument at the given priority. Returns a pointer to the
    00417         // thread object. It simply constructs a new thread object then calls
    00418         // start.
    00419
    00420     static void exit(void* return_value = NULL);
    00421         // causes the calling thread to terminate.
    00422
    00423     static omni_thread* self(void);
    00424         // returns the calling thread's omni_thread object.  If the
    00425         // calling thread is not the main thread and is not created
    00426         // using this library, returns 0. (But see create_dummy()
    00427         // below.)
    00428
    00429     static void yield(void);
    00430         // allows another thread to run.
    00431
    00432     static void sleep(unsigned long secs, unsigned long nanosecs = 0);
    00433         // sleeps for the given time.
    00434
    00435     static void get_time(unsigned long* abs_sec, unsigned long* abs_nsec,
    00436                          unsigned long rel_sec = 0, unsigned long rel_nsec=0);
    00437         // calculates an absolute time in seconds and nanoseconds, suitable for
    00438         // use in timed_waits on condition variables, which is the current time
    00439         // plus the given relative offset.
    00440
    00441
    00442     static void stacksize(unsigned long sz);
    00443     static unsigned long stacksize();
    00444         // Use this value as the stack size when spawning a new thread.
    00445         // The default value (0) means that the thread library default is
    00446         // to be used.
    00447
    00448
    00449     // Per-thread data
    00450     //
    00451     // These functions allow you to attach additional data to an
    00452     // omni_thread. First allocate a key for yourself with
    00453     // allocate_key(). Then you can store any object whose class is
    00454     // derived from value_t. Any values still stored in the
    00455     // omni_thread when the thread exits are deleted.
    00456     //
    00457     // These functions are NOT thread safe, so you should be very
    00458     // careful about setting/getting data in a different thread to the
    00459     // current thread.
    00460
    00461     typedef unsigned int key_t;
    00462     static key_t allocate_key();
    00463
    00464     class value_t {
    00465     public:
    00466       virtual ~value_t() {}
    00467     };
    00468
    00469     value_t* set_value(key_t k, value_t* v);
    00470         // Sets a value associated with the given key. The key must
    00471         // have been allocated with allocate_key(). If a value has
    00472         // already been set with the specified key, the old value_t
    00473         // object is deleted and replaced. Returns the value which was
    00474         // set, or zero if the key is invalid.
    00475
    00476     value_t* get_value(key_t k);
    00477         // Returns the value associated with the key. If the key is
    00478         // invalid, or there is no value for the key, returns zero.
    00479
    00480     value_t* remove_value(key_t k);
    00481         // Removes the value associated with the key and returns it.
    00482         // If the key is invalid, or there is no value for the key,
    00483         // returns zero.
    00484
    00485
    00486     // Dummy omni_thread
    00487     //
    00488     // Sometimes, an application finds itself with threads created
    00489     // outside of omnithread which must interact with omnithread
    00490     // features such as the per-thread data. In this situation,
    00491     // omni_thread::self() would normally return 0. These functions
    00492     // allow the application to create a suitable dummy omni_thread
    00493     // object.
    00494
    00495     static omni_thread* create_dummy(void);
    00496         // creates a dummy omni_thread for the calling thread. Future
    00497         // calls to self() will return the dummy omni_thread. Throws
    00498         // omni_thread_invalid if this thread already has an
    00499         // associated omni_thread (real or dummy).
    00500
    00501     static void release_dummy();
    00502         // release the dummy omni_thread for this thread. This
    00503         // function MUST be called before the thread exits. Throws
    00504         // omni_thread_invalid if the calling thread does not have a
    00505         // dummy omni_thread.
    00506
    00507     // class ensure_self should be created on the stack. If created in
    00508     // a thread without an associated omni_thread, it creates a dummy
    00509     // thread which is released when the ensure_self object is deleted.
    00510
    00511     class ensure_self {
    00512     public:
    00513       inline ensure_self() : _dummy(0)
    00514       {
    00515         _self = omni_thread::self();
    00516         if (!_self) {
    00517           _dummy = 1;
    00518           _self  = omni_thread::create_dummy();
    00519         }
    00520       }
    00521       inline ~ensure_self()
    00522       {
    00523         if (_dummy)
    00524           omni_thread::release_dummy();
    00525       }
    00526       inline omni_thread* self() { return _self; }
    00527     private:
    00528       omni_thread* _self;
    00529       int          _dummy;
    00530     };
    00531
    00532
    00533 private:
    00534
    00535     virtual void run(void* /*arg*/) {}
    00536     virtual void* run_undetached(void* /*arg*/) { return NULL; }
    00537         // can be overridden in a derived class.  When constructed using the
    00538         // the constructor omni_thread(void*, priority_t), these functions are
    00539         // called by start() and start_undetached() respectively.
    00540
    00541     void common_constructor(void* arg, priority_t pri, int det);
    00542         // implements the common parts of the constructors.
    00543
    00544     omni_mutex mutex;
    00545         // used to protect any members which can change after construction,
    00546         // i.e. the following 2 members.
    00547
    00548     state_t _state;
    00549     priority_t _priority;
    00550
    00551     static omni_mutex* next_id_mutex;
    00552     static int next_id;
    00553     int _id;
    00554
    00555     void (*fn_void)(void*);
    00556     void* (*fn_ret)(void*);
    00557     void* thread_arg;
    00558     int detached;
    00559     int _dummy;
    00560     value_t**     _values;
    00561     unsigned long _value_alloc;
    00562
    00563     omni_thread(const omni_thread&);
    00564     omni_thread& operator=(const omni_thread&);
    00565     // Not implemented
    00566
    00567 public:
    00568
    00569     priority_t priority(void) {
    00570
    00571         // return this thread's priority.
    00572
    00573         omni_mutex_lock l(mutex);
    00574         return _priority;
    00575     }
    00576
    00577     state_t state(void) {
    00578
    00579         // return thread state (invalid, new, running or terminated).
    00580
    00581         omni_mutex_lock l(mutex);
    00582         return _state;
    00583     }
    00584
    00585     int id(void) { return _id; }
    00586         // return unique thread id within the current process.
    00587
    00588
    00589     // This class plus the instance of it declared below allows us to execute
    00590     // some initialisation code before main() is called.
    00591
    00592     class _OMNITHREAD_NTDLL_ init_t {
    00593     public:
    00594         init_t(void);
    00595         ~init_t(void);
    00596     };
    00597
    00598     friend class init_t;
    00599     friend class omni_thread_dummy;
    00600
    00601 OMNI_THREAD_EXPOSE:
    00602     OMNI_THREAD_IMPLEMENTATION
    00603 };
    00604
    00605 #ifndef __rtems__
    00606 static omni_thread::init_t omni_thread_init;
    00607 #else
    00608 // RTEMS calls global Ctor/Dtor in a context that is not
    00609 // a posix thread. Calls to functions to pthread_self() in
    00610 // that context returns NULL.
    00611 // So, for RTEMS we will make the thread initialization at the
    00612 // beginning of the Init task that has a posix context.
    00613 #endif
    00614
    00615 #endif