Main Page | Namespace List | Class Hierarchy | Compound List | File List | Namespace Members | Compound Members | File Members | Related Pages

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

Generated on Tue Mar 30 21:31:49 2004 for GNU Radio by doxygen 1.3.2