Statistics
| Branch: | Tag: | Revision:

root / omnithread / gnuradio / omnithread.h @ c48f42b5

History | View | Annotate | Download (18.7 kB)

1
// -*- Mode: C++; -*-
2
//                                Package : omnithread
3
// omnithread.h                        Created : 7/94 tjr
4
//
5
//    Copyright (C) 2006 Free Software Foundation, Inc.
6
//    Copyright (C) 1994,1995,1996, 1997 Olivetti & Oracle Research Laboratory
7
//
8
//    This file is part of the omnithread library
9
//
10
//    The omnithread library is free software; you can redistribute it and/or
11
//    modify it under the terms of the GNU Library General Public
12
//    License as published by the Free Software Foundation; either
13
//    version 2 of the License, or (at your option) any later version.
14
//
15
//    This library is distributed in the hope that it will be useful,
16
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
17
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
//    Library General Public License for more details.
19
//
20
//    You should have received a copy of the GNU Library General Public
21
//    License along with this library; if not, write to the Free
22
//    Software Foundation, Inc., 51 Franklin Street, Boston, MA  
23
//    02110-1301, USA
24
//
25
26
//
27
// Interface to OMNI thread abstraction.
28
//
29
// This file declares classes for threads and synchronisation objects
30
// (mutexes, condition variables and counting semaphores).
31
//
32
// Wherever a seemingly arbitrary choice has had to be made as to the interface
33
// provided, the intention here has been to be as POSIX-like as possible.  This
34
// is why there is no semaphore timed wait, for example.
35
//
36
37
#ifndef __omnithread_h_
38
#define __omnithread_h_
39
40
#ifndef NULL
41
#define NULL 0
42
#endif
43
44
class omni_mutex;
45
class omni_condition;
46
class omni_semaphore;
47
class omni_thread;
48
49
//
50
// OMNI_THREAD_EXPOSE can be defined as public or protected to expose the
51
// implementation class - this may be useful for debugging.  Hopefully this
52
// won't change the underlying structure which the compiler generates so that
53
// this can work without recompiling the library.
54
//
55
56
#ifndef OMNI_THREAD_EXPOSE
57
#define OMNI_THREAD_EXPOSE private
58
#endif
59
60
//
61
// Include implementation-specific header file.
62
//
63
// This must define 4 CPP macros of the form OMNI_x_IMPLEMENTATION for mutex,
64
// condition variable, semaphore and thread.  Each should define any
65
// implementation-specific members of the corresponding classes.
66
//
67
68
69
//
70
// For now, we assume they've always got a Posix Threads implementation.
71
// If not, it'll take some configure hacking to sort it out, along with
72
// the relevant libraries to link with, etc.
73
//
74
75
#if !defined(OMNITHREAD_POSIX) && !defined(OMNITHREAD_NT) && defined HAVE_CONFIG_H
76
// #include <config.h>      // No, No, No!  Never include <config.h> from a header
77
#endif
78
79
#if defined(OMNITHREAD_POSIX)
80
#include <gnuradio/ot_posix.h>
81
82
#elif defined(OMNITHREAD_NT)
83
#include <gnuradio/ot_nt.h>
84
85
#ifdef _MSC_VER
86
87
// Using MSVC++ to compile. If compiling library as a DLL,
88
// define _OMNITHREAD_DLL. If compiling as a statuc library, define
89
// _WINSTATIC
90
// If compiling an application that is to be statically linked to omnithread,
91
// define _WINSTATIC (if the application is  to be dynamically linked, 
92
// there is no need to define any of these macros).
93
94
#if defined (_OMNITHREAD_DLL) && defined(_WINSTATIC)
95
#error "Both _OMNITHREAD_DLL and _WINSTATIC are defined."
96
#elif defined(_OMNITHREAD_DLL)
97
#define _OMNITHREAD_NTDLL_ __declspec(dllexport)
98
#elif !defined(_WINSTATIC)
99
#define _OMNITHREAD_NTDLL_ __declspec(dllimport)
100
#elif defined(_WINSTATIC)
101
#define _OMNITHREAD_NTDLL_
102
#endif
103
 // _OMNITHREAD_DLL && _WINSTATIC
104
105
#else
106
107
// Not using MSVC++ to compile
108
#define _OMNITHREAD_NTDLL_
109
110
#endif
111
 // _MSC_VER
112
 
113
#elif defined(__vxWorks__)
114
#include <gnuradio/ot_VxThread.h>
115
116
#elif defined(__sunos__)
117
#if __OSVERSION__ != 5
118
// XXX Workaround for SUN C++ compiler (seen on 4.2) Template.DB code
119
//     regeneration bug. See omniORB2/CORBA_sysdep.h for details.
120
#if !defined(__SUNPRO_CC) || __OSVERSION__ != '5'
121
#error "Only SunOS 5.x or later is supported."
122
#endif
123
#endif
124
#ifdef UseSolarisThreads
125
#include <gnuradio/ot_solaris.h>
126
#else
127
#include <gnuradio/ot_posix.h>
128
#endif
129
130
#elif defined(__rtems__)
131
#include <gnuradio/ot_posix.h>
132
#include <sched.h>
133
134
#elif defined(__macos__)
135
#include <gnuradio/ot_posix.h>
136
#include <sched.h>
137
138
#else
139
#error "No implementation header file"
140
#endif
141
142
143
#if !defined(__WIN32__)
144
#define _OMNITHREAD_NTDLL_
145
#endif
146
147
#if (!defined(OMNI_MUTEX_IMPLEMENTATION)        || \
148
     !defined(OMNI_MUTEX_LOCK_IMPLEMENTATION)   || \
149
     !defined(OMNI_MUTEX_TRYLOCK_IMPLEMENTATION)|| \
150
     !defined(OMNI_MUTEX_UNLOCK_IMPLEMENTATION) || \
151
     !defined(OMNI_CONDITION_IMPLEMENTATION)    || \
152
     !defined(OMNI_SEMAPHORE_IMPLEMENTATION)    || \
153
     !defined(OMNI_THREAD_IMPLEMENTATION))
154
#error "Implementation header file incomplete"
155
#endif
156
157
158
//
159
// This exception is thrown in the event of a fatal error.
160
//
161
162
class _OMNITHREAD_NTDLL_ omni_thread_fatal {
163
public:
164
    int error;
165
    omni_thread_fatal(int e = 0) : error(e) {}
166
};
167
168
169
//
170
// This exception is thrown when an operation is invoked with invalid
171
// arguments.
172
//
173
174
class _OMNITHREAD_NTDLL_ omni_thread_invalid {};
175
176
177
///////////////////////////////////////////////////////////////////////////
178
//
179
// Mutex
180
//
181
///////////////////////////////////////////////////////////////////////////
182
183
class _OMNITHREAD_NTDLL_ omni_mutex {
184
185
public:
186
    omni_mutex(void);
187
    ~omni_mutex(void);
188
189
    inline void lock(void)    { OMNI_MUTEX_LOCK_IMPLEMENTATION   }
190
    inline void unlock(void)  { OMNI_MUTEX_UNLOCK_IMPLEMENTATION }
191
    inline int trylock(void)  { return OMNI_MUTEX_TRYLOCK_IMPLEMENTATION }
192
        // if mutex is unlocked, lock it and return 1 (true).
193
        // If it's already locked then return 0 (false).
194
195
    inline void acquire(void) { lock(); }
196
    inline void release(void) { unlock(); }
197
        // the names lock and unlock are preferred over acquire and release
198
        // since we are attempting to be as POSIX-like as possible.
199
200
    friend class omni_condition;
201
202
private:
203
    // dummy copy constructor and operator= to prevent copying
204
    omni_mutex(const omni_mutex&);
205
    omni_mutex& operator=(const omni_mutex&);
206
207
OMNI_THREAD_EXPOSE:
208
    OMNI_MUTEX_IMPLEMENTATION
209
};
210
211
//
212
// As an alternative to:
213
// {
214
//   mutex.lock();
215
//   .....
216
//   mutex.unlock();
217
// }
218
//
219
// you can use a single instance of the omni_mutex_lock class:
220
//
221
// {
222
//   omni_mutex_lock l(mutex);
223
//   ....
224
// }
225
//
226
// This has the advantage that mutex.unlock() will be called automatically
227
// when an exception is thrown.
228
//
229
230
class _OMNITHREAD_NTDLL_ omni_mutex_lock {
231
    omni_mutex& mutex;
232
public:
233
    omni_mutex_lock(omni_mutex& m) : mutex(m) { mutex.lock(); }
234
    ~omni_mutex_lock(void) { mutex.unlock(); }
235
private:
236
    // dummy copy constructor and operator= to prevent copying
237
    omni_mutex_lock(const omni_mutex_lock&);
238
    omni_mutex_lock& operator=(const omni_mutex_lock&);
239
};
240
241
242
///////////////////////////////////////////////////////////////////////////
243
//
244
// Condition variable
245
//
246
///////////////////////////////////////////////////////////////////////////
247
248
class _OMNITHREAD_NTDLL_ omni_condition {
249
250
    omni_mutex* mutex;
251
252
public:
253
    omni_condition(omni_mutex* m);
254
        // constructor must be given a pointer to an existing mutex. The
255
        // condition variable is then linked to the mutex, so that there is an
256
        // implicit unlock and lock around wait() and timed_wait().
257
258
    ~omni_condition(void);
259
260
    void wait(void);
261
        // wait for the condition variable to be signalled.  The mutex is
262
        // implicitly released before waiting and locked again after waking up.
263
        // If wait() is called by multiple threads, a signal may wake up more
264
        // than one thread.  See POSIX threads documentation for details.
265
266
    int timedwait(unsigned long secs, unsigned long nanosecs = 0);
267
        // timedwait() is given an absolute time to wait until.  To wait for a
268
        // relative time from now, use omni_thread::get_time. See POSIX threads
269
        // documentation for why absolute times are better than relative.
270
        // Returns 1 (true) if successfully signalled, 0 (false) if time
271
        // expired.
272
273
    void signal(void);
274
        // if one or more threads have called wait(), signal wakes up at least
275
        // one of them, possibly more.  See POSIX threads documentation for
276
        // details.
277
278
    void broadcast(void);
279
        // broadcast is like signal but wakes all threads which have called
280
        // wait().
281
282
private:
283
    // dummy copy constructor and operator= to prevent copying
284
    omni_condition(const omni_condition&);
285
    omni_condition& operator=(const omni_condition&);
286
287
OMNI_THREAD_EXPOSE:
288
    OMNI_CONDITION_IMPLEMENTATION
289
};
290
291
292
///////////////////////////////////////////////////////////////////////////
293
//
294
// Counting (or binary) semaphore
295
//
296
///////////////////////////////////////////////////////////////////////////
297
298
class _OMNITHREAD_NTDLL_ omni_semaphore {
299
300
public:
301
    // if max_count == 1, you've got a binary semaphore.
302
    omni_semaphore(unsigned int initial = 1, unsigned int max_count = 0x7fffffff);
303
    ~omni_semaphore(void);
304
305
    void wait(void);
306
        // if semaphore value is > 0 then decrement it and carry on. If it's
307
        // already 0 then block.
308
309
    int trywait(void);
310
        // if semaphore value is > 0 then decrement it and return 1 (true).
311
        // If it's already 0 then return 0 (false).
312
313
    void post(void);
314
        // if any threads are blocked in wait(), wake one of them up. Otherwise
315
        // increment the value of the semaphore.
316
317
private:
318
    // dummy copy constructor and operator= to prevent copying
319
    omni_semaphore(const omni_semaphore&);
320
    omni_semaphore& operator=(const omni_semaphore&);
321
322
OMNI_THREAD_EXPOSE:
323
    OMNI_SEMAPHORE_IMPLEMENTATION
324
};
325
326
//
327
// A helper class for semaphores, similar to omni_mutex_lock above.
328
//
329
330
class _OMNITHREAD_NTDLL_ omni_semaphore_lock {
331
    omni_semaphore& sem;
332
public:
333
    omni_semaphore_lock(omni_semaphore& s) : sem(s) { sem.wait(); }
334
    ~omni_semaphore_lock(void) { sem.post(); }
335
private:
336
    // dummy copy constructor and operator= to prevent copying
337
    omni_semaphore_lock(const omni_semaphore_lock&);
338
    omni_semaphore_lock& operator=(const omni_semaphore_lock&);
339
};
340
341
342
///////////////////////////////////////////////////////////////////////////
343
//
344
// Thread
345
//
346
///////////////////////////////////////////////////////////////////////////
347
348
class _OMNITHREAD_NTDLL_ omni_thread {
349
350
public:
351
352
    enum priority_t {
353
        PRIORITY_LOW,
354
        PRIORITY_NORMAL,
355
        PRIORITY_HIGH
356
    };
357
358
    enum state_t {
359
        STATE_NEW,                // thread object exists but thread hasn't
360
                                // started yet.
361
        STATE_RUNNING,                // thread is running.
362
        STATE_TERMINATED        // thread has terminated but storage has not
363
                                // been reclaimed (i.e. waiting to be joined).
364
    };
365
366
    //
367
    // Constructors set up the thread object but the thread won't start until
368
    // start() is called. The create method can be used to construct and start
369
    // a thread in a single call.
370
    //
371
372
    omni_thread(void (*fn)(void*), void* arg = NULL,
373
                priority_t pri = PRIORITY_NORMAL);
374
    omni_thread(void* (*fn)(void*), void* arg = NULL,
375
                priority_t pri = PRIORITY_NORMAL);
376
        // these constructors create a thread which will run the given function
377
        // when start() is called.  The thread will be detached if given a
378
        // function with void return type, undetached if given a function
379
        // returning void*. If a thread is detached, storage for the thread is
380
        // reclaimed automatically on termination. Only an undetached thread
381
        // can be joined.
382
383
    void start(void);
384
        // start() causes a thread created with one of the constructors to
385
        // start executing the appropriate function.
386
387
protected:
388
389
    omni_thread(void* arg = NULL, priority_t pri = PRIORITY_NORMAL);
390
        // this constructor is used in a derived class.  The thread will
391
        // execute the run() or run_undetached() member functions depending on
392
        // whether start() or start_undetached() is called respectively.
393
394
public:
395
396
    void start_undetached(void);
397
        // can be used with the above constructor in a derived class to cause
398
        // the thread to be undetached.  In this case the thread executes the
399
        // run_undetached member function.
400
401
protected:
402
403
    virtual ~omni_thread(void);
404
        // destructor cannot be called by user (except via a derived class).
405
        // Use exit() or cancel() instead. This also means a thread object must
406
        // be allocated with new - it cannot be statically or automatically
407
        // allocated. The destructor of a class that inherits from omni_thread
408
        // shouldn't be public either (otherwise the thread object can be
409
        // destroyed while the underlying thread is still running).
410
411
public:
412
413
    void join(void**);
414
        // join causes the calling thread to wait for another's completion,
415
        // putting the return value in the variable of type void* whose address
416
        // is given (unless passed a null pointer). Only undetached threads
417
        // may be joined. Storage for the thread will be reclaimed.
418
419
    void set_priority(priority_t);
420
        // set the priority of the thread.
421
422
    static omni_thread* create(void (*fn)(void*), void* arg = NULL,
423
                               priority_t pri = PRIORITY_NORMAL);
424
    static omni_thread* create(void* (*fn)(void*), void* arg = NULL,
425
                               priority_t pri = PRIORITY_NORMAL);
426
        // create spawns a new thread executing the given function with the
427
        // given argument at the given priority. Returns a pointer to the
428
        // thread object. It simply constructs a new thread object then calls
429
        // start.
430
431
    static void exit(void* return_value = NULL);
432
        // causes the calling thread to terminate.
433
434
    static omni_thread* self(void);
435
        // returns the calling thread's omni_thread object.  If the
436
        // calling thread is not the main thread and is not created
437
        // using this library, returns 0. (But see create_dummy()
438
        // below.)
439
440
    static void yield(void);
441
        // allows another thread to run.
442
443
    static void sleep(unsigned long secs, unsigned long nanosecs = 0);
444
        // sleeps for the given time.
445
446
    static void get_time(unsigned long* abs_sec, unsigned long* abs_nsec,
447
                         unsigned long rel_sec = 0, unsigned long rel_nsec=0);
448
        // calculates an absolute time in seconds and nanoseconds, suitable for
449
        // use in timed_waits on condition variables, which is the current time
450
        // plus the given relative offset.
451
452
453
    static void stacksize(unsigned long sz);
454
    static unsigned long stacksize();
455
        // Use this value as the stack size when spawning a new thread.
456
        // The default value (0) means that the thread library default is
457
        // to be used.
458
459
460
    // Per-thread data
461
    //
462
    // These functions allow you to attach additional data to an
463
    // omni_thread. First allocate a key for yourself with
464
    // allocate_key(). Then you can store any object whose class is
465
    // derived from value_t. Any values still stored in the
466
    // omni_thread when the thread exits are deleted.
467
    //
468
    // These functions are NOT thread safe, so you should be very
469
    // careful about setting/getting data in a different thread to the
470
    // current thread.
471
472
    typedef unsigned int key_t;
473
    static key_t allocate_key();
474
475
    class value_t {
476
    public:
477
      virtual ~value_t() {}
478
    };
479
480
    value_t* set_value(key_t k, value_t* v);
481
        // Sets a value associated with the given key. The key must
482
        // have been allocated with allocate_key(). If a value has
483
        // already been set with the specified key, the old value_t
484
        // object is deleted and replaced. Returns the value which was
485
        // set, or zero if the key is invalid.
486
487
    value_t* get_value(key_t k);
488
        // Returns the value associated with the key. If the key is
489
        // invalid, or there is no value for the key, returns zero.
490
491
    value_t* remove_value(key_t k);
492
        // Removes the value associated with the key and returns it.
493
        // If the key is invalid, or there is no value for the key,
494
        // returns zero.
495
496
497
    // Dummy omni_thread
498
    //
499
    // Sometimes, an application finds itself with threads created
500
    // outside of omnithread which must interact with omnithread
501
    // features such as the per-thread data. In this situation,
502
    // omni_thread::self() would normally return 0. These functions
503
    // allow the application to create a suitable dummy omni_thread
504
    // object.
505
506
    static omni_thread* create_dummy(void);
507
        // creates a dummy omni_thread for the calling thread. Future
508
        // calls to self() will return the dummy omni_thread. Throws
509
        // omni_thread_invalid if this thread already has an
510
        // associated omni_thread (real or dummy).
511
512
    static void release_dummy();
513
        // release the dummy omni_thread for this thread. This
514
        // function MUST be called before the thread exits. Throws
515
        // omni_thread_invalid if the calling thread does not have a
516
        // dummy omni_thread.
517
518
    // class ensure_self should be created on the stack. If created in
519
    // a thread without an associated omni_thread, it creates a dummy
520
    // thread which is released when the ensure_self object is deleted.
521
522
    class ensure_self {
523
    public:
524
      inline ensure_self() : _dummy(0)
525
      {
526
        _self = omni_thread::self();
527
        if (!_self) {
528
          _dummy = 1;
529
          _self  = omni_thread::create_dummy();
530
        }
531
      }
532
      inline ~ensure_self()
533
      {
534
        if (_dummy)
535
          omni_thread::release_dummy();
536
      }
537
      inline omni_thread* self() { return _self; }
538
    private:
539
      omni_thread* _self;
540
      int          _dummy;
541
    };
542
543
544
private:
545
546
    virtual void run(void* /*arg*/) {}
547
    virtual void* run_undetached(void* /*arg*/) { return NULL; }
548
        // can be overridden in a derived class.  When constructed using the
549
        // the constructor omni_thread(void*, priority_t), these functions are
550
        // called by start() and start_undetached() respectively.
551
552
    void common_constructor(void* arg, priority_t pri, int det);
553
        // implements the common parts of the constructors.
554
555
    omni_mutex mutex;
556
        // used to protect any members which can change after construction,
557
        // i.e. the following 2 members.
558
559
    state_t _state;
560
    priority_t _priority;
561
562
    static omni_mutex* next_id_mutex;
563
    static int next_id;
564
    int _id;
565
566
    void (*fn_void)(void*);
567
    void* (*fn_ret)(void*);
568
    void* thread_arg;
569
    int detached;
570
    int _dummy;
571
    value_t**     _values;
572
    unsigned long _value_alloc;
573
574
    omni_thread(const omni_thread&);
575
    omni_thread& operator=(const omni_thread&);
576
    // Not implemented
577
578
public:
579
580
    priority_t priority(void) {
581
582
        // return this thread's priority.
583
584
        omni_mutex_lock l(mutex);
585
        return _priority;
586
    }
587
588
    state_t state(void) {
589
590
        // return thread state (invalid, new, running or terminated).
591
592
        omni_mutex_lock l(mutex);
593
        return _state;
594
    }
595
596
    int id(void) { return _id; }
597
        // return unique thread id within the current process.
598
599
600
    // This class plus the instance of it declared below allows us to execute
601
    // some initialisation code before main() is called.
602
603
    class _OMNITHREAD_NTDLL_ init_t {
604
    public:
605
        init_t(void);
606
        ~init_t(void);
607
    };
608
609
    friend class init_t;
610
    friend class omni_thread_dummy;
611
612
OMNI_THREAD_EXPOSE:
613
    OMNI_THREAD_IMPLEMENTATION
614
};
615
616
#ifndef __rtems__
617
static omni_thread::init_t omni_thread_init;
618
#else
619
// RTEMS calls global Ctor/Dtor in a context that is not
620
// a posix thread. Calls to functions to pthread_self() in
621
// that context returns NULL. 
622
// So, for RTEMS we will make the thread initialization at the
623
// beginning of the Init task that has a posix context.
624
#endif
625
626
#endif