source: NonGTP/glut/FLTK/include/fltk/Threads.h @ 814

Revision 814, 5.7 KB checked in by gumbau, 18 years ago (diff)

Glut initial import used by Geometry modules

Line 
1// Inline classes to provide a "toy" interface for threads and mutexes.
2// These are used by the fltk demo programs. They have been improved
3// quite a bit and may be useful for non-toy programs, too.
4
5#ifndef fltk_Threads_h
6#define fltk_Threads_h
7
8#ifndef _WIN32
9// pthreads:
10
11#include <pthread.h>
12
13namespace fltk {
14/*! \addtogroup multithreading
15  \{ */
16
17/** Hides whatever the system uses to identify a thread. Used so
18  the "toy" interface is portable. */
19typedef pthread_t Thread;
20
21/** Fork a new thread and make it run \a f(p). Returns negative number
22  on error, otherwise \a t is set to the new thread. */
23inline int create_thread(Thread& t, void *(*f) (void *), void* p) {
24  return pthread_create((pthread_t*)&t, 0, f, p);
25}
26
27/**
28  "Mutual-exclusion lock" for simple multithreaded programs.  Calling
29  lock() will wait until nobody else has the lock and then will
30  return. <i>Calling lock() more than once will "deadlock"!</i>
31  To avoid this, use RecursiveMutex.
32*/
33class Mutex {
34  friend class SignalMutex;
35  pthread_mutex_t mutex;
36  Mutex(const Mutex&);
37  Mutex& operator=(const Mutex&);
38protected:
39  Mutex(const pthread_mutexattr_t* a) {pthread_mutex_init(&mutex, a);}
40public:
41  Mutex() {pthread_mutex_init(&mutex, 0);}
42  void lock() {pthread_mutex_lock(&mutex);}
43  void unlock() {pthread_mutex_unlock(&mutex);}
44  bool trylock() {return pthread_mutex_trylock(&mutex) == 0;}
45  ~Mutex() {pthread_mutex_destroy(&mutex);}
46};
47
48/**
49  A portable "semaphore". A thread that holds this lock() can call
50  wait(), which will unlock it, then wait for another thread to
51  call signal(), then lock() it again.
52
53  The other thread can call signal() at any time, though usually
54  it will have called lock() as well, as the lock can be used to
55  protect the data that is actually being shared between the threads.
56
57  If more than one thread is in wait(), then calling signal_one()
58  will only wake one of them up. This may be more efficient, and
59  can be done safely if all threads that call wait() also call
60  signal_one() just before calling unlock().
61
62  Warning: wait() can return even if signal() was not called. You
63  must then check other data (protected by the lock()) to see if
64  the condition really is fulfilled. In many cases this is the
65  best implementation, it is also necessary to work around design
66  errors in Windows, where always returns after 1/2 second to
67  avoid a deadlock due to the non-atomic nature of Windows calls.
68*/
69class SignalMutex : public Mutex {
70  pthread_cond_t cond;
71public:
72  SignalMutex() : Mutex() {pthread_cond_init(&cond, 0);}
73  void signal() {pthread_cond_broadcast(&cond);}
74  void signal_one() {pthread_cond_signal(&cond);}
75  void wait() {pthread_cond_wait(&cond, &mutex);}
76};
77
78// Linux supports recursive locks, use them directly, with some cheating:
79#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) || defined(PTHREAD_MUTEX_RECURSIVE)
80
81extern pthread_mutexattr_t Mutex_attrib;
82
83class RecursiveMutex : public Mutex {
84public:
85  RecursiveMutex() : Mutex(&Mutex_attrib) {}
86};
87
88#else // standard pthread mutexes need a bit of work to be recursive:
89
90/**
91  "Mutual exclusion lock" to protect data in multithreaded programs.
92  This is a "recursive lock". Calling lock() will wait until nobody
93  else has the lock and then will take it. Calling lock() multiple
94  times by the same thread is allowed, and unlock() must then be
95  called the same number of times before another thread can get the
96  lock.
97*/
98class RecursiveMutex : public Mutex {
99  pthread_t owner;
100  int counter;
101public:
102  RecursiveMutex() : Mutex(), counter(0) {}
103  void lock() {
104    if (!counter || owner != pthread_self()) {
105      Mutex::lock();
106      owner = pthread_self();
107      counter = 1;
108    } else {
109      ++counter;
110    }
111  }
112  bool trylock() {
113    if (!counter || owner != pthread_self()) {
114      if (!Mutex::trylock()) return false;
115      owner = pthread_self();
116    }
117    counter++;
118    return true;
119  }
120  void unlock() {if (!--counter) Mutex::unlock();}
121};
122
123#endif
124
125#else // _WIN32:
126
127# define _WIN32_WINNT 0x0500
128# include <Windows.h>
129# include <process.h>
130// undefine some of the more annoying crap:
131# undef DELETE
132# undef ERROR
133# undef IN
134# undef OUT
135# undef POINT
136# undef far
137# undef max
138# undef min
139# undef near
140
141namespace fltk {
142
143typedef unsigned long Thread;
144
145inline int create_thread(Thread& t, void *(*f) (void *), void* p) {
146  return t = (Thread)_beginthread((void( __cdecl * )( void * ))f, 0, p);
147}
148
149class FL_API Mutex {
150  CRITICAL_SECTION cs;
151  Mutex(const Mutex&);
152  Mutex& operator=(const Mutex&);
153public:
154  Mutex() {InitializeCriticalSection(&cs);}
155  void lock() {while (!TryEnterCriticalSection(&cs)) SwitchToThread();}
156  void unlock() {LeaveCriticalSection(&cs);}
157  bool trylock() {return TryEnterCriticalSection(&cs);}
158  ~Mutex() {DeleteCriticalSection(&cs);}
159};
160
161// After many experiments we have determined that this very stupid
162// implementation has the lowest overhead:
163class FL_API SignalMutex : public Mutex {
164public:
165  SignalMutex() : Mutex() {}
166  void signal() {}
167  void signal_one() {}
168  void wait() {
169    // the following three calls should be atomic, sigh...
170    unlock();
171    SwitchToThread();
172    lock();
173  }
174};
175
176typedef Mutex RecursiveMutex;
177
178#endif
179
180/**
181   C++ convienence object for locking a Mutex.
182   Creating a local one of these will lock() the mutex and it means
183   unlock() will be called no matter how a function exits, because
184   the destructor ~Guard() does an unlock().
185
186\code
187   static fltk::Mutex mutex;
188   function() {
189     fltk::Guard guard(mutex);
190     do_stuff;
191     throw_exceptions;
192     if (test()) return;
193     etc;
194   }
195\endcode
196
197*/
198class FL_API Guard {
199  Mutex& lock;
200 public:
201  Guard(Mutex& m) : lock(m) {lock.lock();}
202  Guard(Mutex* m) : lock(*m) {lock.lock();}
203  ~Guard() {lock.unlock();}
204};
205
206/*! \} */
207
208}
209
210#endif
Note: See TracBrowser for help on using the repository browser.