]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/threading.h
Security-29.tar.gz
[apple/security.git] / cdsa / cdsa_utilities / threading.h
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // threading - generic thread support
21 //
22 #ifndef _H_THREADING
23 #define _H_THREADING
24
25 #include <Security/utilities.h>
26 #include <Security/debugging.h>
27
28 #if _USE_THREADS == _USE_PTHREADS
29 # include <pthread.h>
30 #endif
31
32 #include <Security/threading_internal.h>
33
34
35 namespace Security {
36
37
38 //
39 // Potentially, debug-logging all Mutex activity can really ruin your
40 // performance day. We take some measures to reduce the impact, but if
41 // you really can't stomach any overhead, define THREAD_NDEBUG to turn
42 // (only) thread debug-logging off. NDEBUG will turn this on automatically.
43 // On the other hand, throwing out all debug code will change the ABI of
44 // Mutexi in incompatible ways. Thus, we still generate the debug-style out-of-line
45 // code even with THREAD_NDEBUG, so that debug-style code will work with us.
46 // If you want to ditch it completely, #define THREAD_CLEAN_NDEBUG.
47 //
48 #if defined(NDEBUG) || defined(THREAD_CLEAN_NDEBUG)
49 # if !defined(THREAD_NDEBUG)
50 # define THREAD_NDEBUG
51 # endif
52 #endif
53
54
55 //
56 // An abstraction of a per-thread untyped storage slot of pointer size.
57 // Do not use this in ordinary code; this is for implementing other primitives only.
58 // Use a PerThreadPointer or ThreadNexus.
59 //
60 #if _USE_THREADS == _USE_PTHREADS
61
62 class ThreadStoreSlot {
63 public:
64 typedef void Destructor(void *);
65 ThreadStoreSlot(Destructor *destructor = NULL);
66 ~ThreadStoreSlot();
67
68 void *get() const { return pthread_getspecific(mKey); }
69 operator void * () const { return get(); }
70 void operator = (void *value) const
71 {
72 if (int err = pthread_setspecific(mKey, value))
73 UnixError::throwMe(err);
74 }
75
76 private:
77 pthread_key_t mKey;
78 };
79
80 #endif //_USE_PTHREADS
81
82
83 //
84 // Per-thread pointers are patterned after the pthread TLS (thread local storage)
85 // facility.
86 //
87 #if _USE_THREADS == _USE_PTHREADS
88
89 template <class T>
90 class PerThreadPointer : public ThreadStoreSlot {
91 public:
92 PerThreadPointer(bool cleanup = true) : ThreadStoreSlot(cleanup ? destructor : NULL) { }
93 operator bool() const { return get() != NULL; }
94 operator T * () const { return reinterpret_cast<T *>(get()); }
95 T *operator -> () const { return static_cast<T *>(*this); }
96 T &operator * () const { return *static_cast<T *>(get()); }
97 void operator = (T *t) { ThreadStoreSlot::operator = (t); }
98
99 private:
100 static void destructor(void *element)
101 { delete reinterpret_cast<T *>(element); }
102 };
103
104 #elif _USE_THREADS == _USE_NO_THREADS
105
106 template <class T>
107 class PerThreadPointer {
108 public:
109 PerThreadPointer(bool cleanup = true) : mCleanup(cleanup) { }
110 ~PerThreadPointer() { if (mCleanup) delete mValue; }
111 operator bool() const { return mValue != NULL; }
112 operator T * () const { return mValue; }
113 T *operator -> () const { return mValue; }
114 T &operator * () const { assert(mValue); return *mValue; }
115 void operator = (T *t) { mValue = t; }
116
117 private:
118 T *mValue;
119 bool mCleanup;
120 };
121
122 #else
123 # error Unsupported threading model
124 #endif //_USE_THREADS
125
126
127 //
128 // Basic Mutex operations.
129 // This will be some as-cheap-as-feasible locking primitive that only
130 // controls one bit (locked/unlocked), plus whatever you contractually
131 // put under its control.
132 //
133 #if _USE_THREADS == _USE_PTHREADS
134
135 class Mutex {
136 NOCOPY(Mutex)
137
138 void check(int err) { if (err) UnixError::throwMe(err); }
139
140 public:
141 #if defined(THREAD_NDEBUG) && !defined(THREAD_MAKE_STUBS)
142 Mutex(bool = true) { check(pthread_mutex_init(&me, NULL)); }
143 void lock() { check(pthread_mutex_lock(&me)); }
144 bool tryLock() {
145 if (int err = pthread_mutex_trylock(&me))
146 if (err == EBUSY) return false; else UnixError::throwMe(err);
147 else return true;
148 }
149 void unlock() { check(pthread_mutex_unlock(&me)); }
150 ~Mutex() { check(pthread_mutex_destroy(&me)); }
151 #else //THREAD_NDEBUG
152 Mutex(bool log = true);
153 ~Mutex();
154 void lock();
155 bool tryLock();
156 void unlock();
157 #endif //THREAD_NDEBUG
158
159 private:
160 pthread_mutex_t me;
161
162 #if !defined(THREAD_CLEAN_NDEBUG)
163 bool debugLog; // log *this* mutex
164 unsigned long useCount; // number of locks succeeded
165 unsigned long contentionCount; // number of contentions (valid only if debugLog)
166 static bool debugHasInitialized; // global: debug state set up
167 static bool loggingMutexi; // global: we are debug-logging mutexi
168 #endif //THREAD_CLEAN_NDEBUG
169 };
170
171 #elif _USE_THREADS == _USE_NO_THREADS
172
173 class Mutex {
174 public:
175 void lock(bool = true) { }
176 void unlock() { }
177 bool tryLock() { return true; }
178 };
179
180 #else
181 # error Unsupported threading model
182 #endif //_USE_THREADS
183
184
185 //
186 // A CountingMutex adds a counter to a Mutex.
187 // NOTE: This is not officially a semaphore, even if it happens to be implemented with
188 // one on some platforms.
189 //
190 class CountingMutex : public Mutex {
191 // note that this implementation works for any system implementing Mutex *somehow*
192 public:
193 CountingMutex() : mCount(0) { }
194 ~CountingMutex() { assert(mCount == 0); }
195
196 void enter();
197 bool tryEnter();
198 void exit();
199
200 // these methods do not lock - use only while you hold the lock
201 unsigned int count() const { return mCount; }
202 bool isIdle() const { return mCount == 0; }
203
204 // convert Mutex lock to CountingMutex enter/exit. Expert use only
205 void finishEnter();
206 void finishExit();
207
208 private:
209 unsigned int mCount;
210 };
211
212
213 //
214 // A guaranteed-unlocker stack-based class.
215 // By default, this will use lock/unlock methods, but you can provide your own
216 // alternates (to, e.g., use enter/exit, or some more specialized pair of operations).
217 //
218 // NOTE: StLock itself is not thread-safe. It is intended for use (usually on the stack)
219 // by a single thread.
220 //
221 template <class Lock,
222 void (Lock::*_lock)() = &Lock::lock,
223 void (Lock::*_unlock)() = &Lock::unlock>
224 class StLock {
225 public:
226 StLock(Lock &lck) : me(lck) { (me.*_lock)(); mActive = true; }
227 StLock(Lock &lck, bool option) : me(lck), mActive(option) { }
228 ~StLock() { if (mActive) (me.*_unlock)(); }
229
230 bool isActive() const { return mActive; }
231 void lock() { if(!mActive) { (me.*_lock)(); mActive = true; }}
232 void unlock() { if(mActive) { (me.*_unlock)(); mActive = false; }}
233
234 operator const Lock &() const { return me; }
235
236 protected:
237 Lock &me;
238 bool mActive;
239 };
240
241
242 //
243 // Atomic increment/decrement operations.
244 // The default implementation uses a Mutex. However, many architectures can do
245 // much better than that.
246 // Be very clear on the nature of AtomicCounter. It implies no memory barriers of
247 // any kind. This means that (1) you cannot protect any other memory region with it
248 // (use a Mutex for that), and (2) it may not enforce cross-processor ordering, which
249 // means that you have no guarantee that you'll see modifications by other processors
250 // made earlier (unless another mechanism provides the memory barrier).
251 // On the other hand, if your compiler has brains, this is blindingly fast...
252 //
253 template <class Integer = int>
254 class StaticAtomicCounter {
255 protected:
256
257 #if defined(_HAVE_ATOMIC_OPERATIONS)
258 AtomicWord mValue;
259 public:
260 operator Integer() const { return mValue; }
261
262 // infix versions (primary)
263 Integer operator ++ () { return atomicIncrement(mValue); }
264 Integer operator -- () { return atomicDecrement(mValue); }
265
266 // postfix versions
267 Integer operator ++ (int) { return atomicIncrement(mValue) - 1; }
268 Integer operator -- (int) { return atomicDecrement(mValue) + 1; }
269
270 // generic offset
271 Integer operator += (int delta) { return atomicOffset(mValue, delta); }
272
273 #else // no atomic integers, use locks
274
275 Integer mValue;
276 mutable Mutex mLock;
277 public:
278 StaticAtomicCounter(Integer init = 0) : mValue(init), mLock(false) { }
279 operator Integer() const { StLock<Mutex> _(mLock); return mValue; }
280 Integer operator ++ () { StLock<Mutex> _(mLock); return ++mValue; }
281 Integer operator -- () { StLock<Mutex> _(mLock); return --mValue; }
282 Integer operator ++ (int) { StLock<Mutex> _(mLock); return mValue++; }
283 Integer operator -- (int) { StLock<Mutex> _(mLock); return mValue--; }
284 Integer operator += (int delta) { StLock<Mutex> _(mLock); return mValue += delta; }
285 #endif
286 };
287
288
289 template <class Integer = int>
290 class AtomicCounter : public StaticAtomicCounter<Integer> {
291 public:
292 AtomicCounter(Integer init = 0) { mValue = 0; }
293 };
294
295
296 //
297 // A class implementing a separate thread of execution.
298 // Do not expect many high-level semantics to be portable. If you can,
299 // restrict yourself to expect parallel execution and little else.
300 //
301 #if _USE_THREADS == _USE_PTHREADS
302
303 class Thread {
304 NOCOPY(Thread)
305 public:
306 Thread() { } // constructor
307 virtual ~Thread(); // virtual destructor
308 void run(); // begin running the thread
309
310 public:
311 static void yield(); // unstructured short-term processor yield
312
313 public:
314 class Identity {
315 friend class Thread;
316 public:
317 Identity() { }
318
319 static Identity current() { return pthread_self(); }
320
321 bool operator == (const Identity &other) const
322 { return pthread_equal(mIdent, other.mIdent); }
323
324 bool operator != (const Identity &other) const
325 { return !(*this == other); }
326
327 #if !defined(NDEBUG)
328 static const int idLength = 10;
329 static void getIdString(char id[idLength]);
330 #endif //NDEBUG
331
332 private:
333 pthread_t mIdent;
334
335 Identity(pthread_t id) : mIdent(id) { }
336 };
337
338 protected:
339 virtual void action() = 0; // the action to be performed
340
341 private:
342 Identity self; // my own identity (instance constant)
343
344 static void *runner(void *); // argument to pthread_create
345 };
346
347 #elif _USE_THREADS == _USE_NO_THREADS
348
349 class Thread {
350 NOCOPY(Thread)
351 public:
352 Thread() { } // constructor
353 virtual ~Thread() { } // virtual destructor
354 void run() { action(); } // just synchronously run the action
355
356 public:
357 class Identity {
358 public:
359 static Identity current() { return Identity(); }
360
361 bool operator == (const Identity &) const { return true; } // all the same
362 bool operator != (const Identity &) const { return false; }
363
364 #if !defined(NDEBUG)
365 static const idLength = 9;
366 static void getIdString(char id[idLength]) { memcpy(id, "nothread", idLength); }
367 #endif
368
369 private:
370 Identity() { }
371 };
372
373 protected:
374 virtual void action() = 0; // implement action of thread
375 };
376
377 #else
378 # error Unsupported threading model
379 #endif
380
381
382 //
383 // A "just run this function in a thread" variant of Thread
384 //
385 class ThreadRunner : public Thread {
386 typedef void Action();
387 public:
388 ThreadRunner(Action *todo);
389
390 private:
391 void action();
392 Action *mAction;
393 };
394
395
396 //
397 // A NestingMutex allows recursive re-entry by the same thread.
398 // Some pthread implementations support this through a mutex attribute.
399 // OSX's doesn't, naturally. This implementation works on all pthread platforms.
400 //
401 class NestingMutex {
402 public:
403 NestingMutex();
404
405 void lock();
406 bool tryLock();
407 void unlock();
408
409 private:
410 Mutex mLock;
411 Mutex mWait;
412 Thread::Identity mIdent;
413 uint32 mCount;
414 };
415
416 } // end namespace Security
417
418 #endif //_H_THREADING