]> git.saurik.com Git - apple/security.git/blame - cdsa/cdsa_utilities/threading.h
Security-54.1.9.tar.gz
[apple/security.git] / cdsa / cdsa_utilities / threading.h
CommitLineData
bac41a7b
A
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
35namespace 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
62class ThreadStoreSlot {
63public:
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
76private:
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.
5a719ac8
A
86// Let's be clear on what gets destroyed when, here. Following the pthread lead,
87// when a thread dies its PerThreadPointer object(s) are properly destroyed.
88// However, if a PerThreadPointer itself is destroyed, NOTHING HAPPENS. Yes, there are
89// reasons for this. This is not (on its face) a bug, so don't yell. But be aware...
bac41a7b
A
90//
91#if _USE_THREADS == _USE_PTHREADS
92
93template <class T>
94class PerThreadPointer : public ThreadStoreSlot {
95public:
96 PerThreadPointer(bool cleanup = true) : ThreadStoreSlot(cleanup ? destructor : NULL) { }
97 operator bool() const { return get() != NULL; }
98 operator T * () const { return reinterpret_cast<T *>(get()); }
99 T *operator -> () const { return static_cast<T *>(*this); }
100 T &operator * () const { return *static_cast<T *>(get()); }
101 void operator = (T *t) { ThreadStoreSlot::operator = (t); }
102
103private:
104 static void destructor(void *element)
105 { delete reinterpret_cast<T *>(element); }
106};
107
108#elif _USE_THREADS == _USE_NO_THREADS
109
110template <class T>
111class PerThreadPointer {
112public:
113 PerThreadPointer(bool cleanup = true) : mCleanup(cleanup) { }
5a719ac8 114 ~PerThreadPointer() { /* no cleanup - see comment above */ }
bac41a7b
A
115 operator bool() const { return mValue != NULL; }
116 operator T * () const { return mValue; }
117 T *operator -> () const { return mValue; }
118 T &operator * () const { assert(mValue); return *mValue; }
119 void operator = (T *t) { mValue = t; }
120
121private:
122 T *mValue;
123 bool mCleanup;
124};
125
126#else
127# error Unsupported threading model
128#endif //_USE_THREADS
129
130
131//
132// Basic Mutex operations.
133// This will be some as-cheap-as-feasible locking primitive that only
134// controls one bit (locked/unlocked), plus whatever you contractually
135// put under its control.
136//
137#if _USE_THREADS == _USE_PTHREADS
138
139class Mutex {
140 NOCOPY(Mutex)
141
142 void check(int err) { if (err) UnixError::throwMe(err); }
143
144public:
145#if defined(THREAD_NDEBUG) && !defined(THREAD_MAKE_STUBS)
146 Mutex(bool = true) { check(pthread_mutex_init(&me, NULL)); }
147 void lock() { check(pthread_mutex_lock(&me)); }
148 bool tryLock() {
149 if (int err = pthread_mutex_trylock(&me))
150 if (err == EBUSY) return false; else UnixError::throwMe(err);
151 else return true;
152 }
153 void unlock() { check(pthread_mutex_unlock(&me)); }
154 ~Mutex() { check(pthread_mutex_destroy(&me)); }
155#else //THREAD_NDEBUG
156 Mutex(bool log = true);
157 ~Mutex();
158 void lock();
159 bool tryLock();
160 void unlock();
161#endif //THREAD_NDEBUG
162
163private:
164 pthread_mutex_t me;
165
166#if !defined(THREAD_CLEAN_NDEBUG)
167 bool debugLog; // log *this* mutex
168 unsigned long useCount; // number of locks succeeded
169 unsigned long contentionCount; // number of contentions (valid only if debugLog)
170 static bool debugHasInitialized; // global: debug state set up
171 static bool loggingMutexi; // global: we are debug-logging mutexi
172#endif //THREAD_CLEAN_NDEBUG
173};
174
175#elif _USE_THREADS == _USE_NO_THREADS
176
177class Mutex {
178public:
179 void lock(bool = true) { }
180 void unlock() { }
181 bool tryLock() { return true; }
182};
183
184#else
185# error Unsupported threading model
186#endif //_USE_THREADS
187
188
189//
190// A CountingMutex adds a counter to a Mutex.
191// NOTE: This is not officially a semaphore, even if it happens to be implemented with
192// one on some platforms.
193//
194class CountingMutex : public Mutex {
195 // note that this implementation works for any system implementing Mutex *somehow*
196public:
197 CountingMutex() : mCount(0) { }
198 ~CountingMutex() { assert(mCount == 0); }
199
200 void enter();
201 bool tryEnter();
202 void exit();
203
204 // these methods do not lock - use only while you hold the lock
205 unsigned int count() const { return mCount; }
206 bool isIdle() const { return mCount == 0; }
207
208 // convert Mutex lock to CountingMutex enter/exit. Expert use only
209 void finishEnter();
210 void finishExit();
211
212private:
213 unsigned int mCount;
214};
215
216
217//
218// A guaranteed-unlocker stack-based class.
219// By default, this will use lock/unlock methods, but you can provide your own
220// alternates (to, e.g., use enter/exit, or some more specialized pair of operations).
221//
222// NOTE: StLock itself is not thread-safe. It is intended for use (usually on the stack)
223// by a single thread.
224//
225template <class Lock,
226 void (Lock::*_lock)() = &Lock::lock,
227 void (Lock::*_unlock)() = &Lock::unlock>
228class StLock {
229public:
230 StLock(Lock &lck) : me(lck) { (me.*_lock)(); mActive = true; }
231 StLock(Lock &lck, bool option) : me(lck), mActive(option) { }
232 ~StLock() { if (mActive) (me.*_unlock)(); }
233
234 bool isActive() const { return mActive; }
235 void lock() { if(!mActive) { (me.*_lock)(); mActive = true; }}
236 void unlock() { if(mActive) { (me.*_unlock)(); mActive = false; }}
29654253 237 void release() { assert(mActive); mActive = false; }
bac41a7b
A
238
239 operator const Lock &() const { return me; }
240
241protected:
242 Lock &me;
243 bool mActive;
244};
245
246
247//
248// Atomic increment/decrement operations.
249// The default implementation uses a Mutex. However, many architectures can do
250// much better than that.
251// Be very clear on the nature of AtomicCounter. It implies no memory barriers of
252// any kind. This means that (1) you cannot protect any other memory region with it
253// (use a Mutex for that), and (2) it may not enforce cross-processor ordering, which
254// means that you have no guarantee that you'll see modifications by other processors
255// made earlier (unless another mechanism provides the memory barrier).
256// On the other hand, if your compiler has brains, this is blindingly fast...
257//
258template <class Integer = int>
259class StaticAtomicCounter {
260protected:
261
262#if defined(_HAVE_ATOMIC_OPERATIONS)
263 AtomicWord mValue;
264public:
265 operator Integer() const { return mValue; }
266
267 // infix versions (primary)
268 Integer operator ++ () { return atomicIncrement(mValue); }
269 Integer operator -- () { return atomicDecrement(mValue); }
270
271 // postfix versions
272 Integer operator ++ (int) { return atomicIncrement(mValue) - 1; }
273 Integer operator -- (int) { return atomicDecrement(mValue) + 1; }
274
275 // generic offset
276 Integer operator += (int delta) { return atomicOffset(mValue, delta); }
277
278#else // no atomic integers, use locks
279
280 Integer mValue;
281 mutable Mutex mLock;
282public:
283 StaticAtomicCounter(Integer init = 0) : mValue(init), mLock(false) { }
284 operator Integer() const { StLock<Mutex> _(mLock); return mValue; }
285 Integer operator ++ () { StLock<Mutex> _(mLock); return ++mValue; }
286 Integer operator -- () { StLock<Mutex> _(mLock); return --mValue; }
287 Integer operator ++ (int) { StLock<Mutex> _(mLock); return mValue++; }
288 Integer operator -- (int) { StLock<Mutex> _(mLock); return mValue--; }
289 Integer operator += (int delta) { StLock<Mutex> _(mLock); return mValue += delta; }
290#endif
291};
292
293
294template <class Integer = int>
295class AtomicCounter : public StaticAtomicCounter<Integer> {
296public:
297 AtomicCounter(Integer init = 0) { mValue = 0; }
298};
299
300
301//
302// A class implementing a separate thread of execution.
303// Do not expect many high-level semantics to be portable. If you can,
304// restrict yourself to expect parallel execution and little else.
305//
306#if _USE_THREADS == _USE_PTHREADS
307
308class Thread {
309 NOCOPY(Thread)
bac41a7b
A
310public:
311 class Identity {
312 friend class Thread;
29654253
A
313
314 Identity(pthread_t id) : mIdent(id) { }
bac41a7b
A
315 public:
316 Identity() { }
317
318 static Identity current() { return pthread_self(); }
319
320 bool operator == (const Identity &other) const
321 { return pthread_equal(mIdent, other.mIdent); }
322
323 bool operator != (const Identity &other) const
324 { return !(*this == other); }
325
326#if !defined(NDEBUG)
327 static const int idLength = 10;
328 static void getIdString(char id[idLength]);
329#endif //NDEBUG
330
331 private:
332 pthread_t mIdent;
bac41a7b
A
333 };
334
29654253
A
335public:
336 Thread() { } // constructor
337 virtual ~Thread(); // virtual destructor
338 void run(); // begin running the thread
339
340public:
341 static void yield(); // unstructured short-term processor yield
342
bac41a7b
A
343protected:
344 virtual void action() = 0; // the action to be performed
345
346private:
347 Identity self; // my own identity (instance constant)
348
349 static void *runner(void *); // argument to pthread_create
350};
351
352#elif _USE_THREADS == _USE_NO_THREADS
353
354class Thread {
355 NOCOPY(Thread)
356public:
357 Thread() { } // constructor
358 virtual ~Thread() { } // virtual destructor
359 void run() { action(); } // just synchronously run the action
360
361public:
362 class Identity {
363 public:
364 static Identity current() { return Identity(); }
365
366 bool operator == (const Identity &) const { return true; } // all the same
367 bool operator != (const Identity &) const { return false; }
368
369#if !defined(NDEBUG)
370 static const idLength = 9;
371 static void getIdString(char id[idLength]) { memcpy(id, "nothread", idLength); }
372#endif
373
374 private:
375 Identity() { }
376 };
29654253
A
377
378public:
379 void yield() { assert(false); }
bac41a7b
A
380
381protected:
382 virtual void action() = 0; // implement action of thread
383};
384
385#else
386# error Unsupported threading model
387#endif
388
389
390//
391// A "just run this function in a thread" variant of Thread
392//
393class ThreadRunner : public Thread {
394 typedef void Action();
395public:
396 ThreadRunner(Action *todo);
397
398private:
399 void action();
400 Action *mAction;
401};
402
403
404//
405// A NestingMutex allows recursive re-entry by the same thread.
406// Some pthread implementations support this through a mutex attribute.
407// OSX's doesn't, naturally. This implementation works on all pthread platforms.
408//
409class NestingMutex {
410public:
411 NestingMutex();
412
413 void lock();
414 bool tryLock();
415 void unlock();
416
417private:
418 Mutex mLock;
419 Mutex mWait;
420 Thread::Identity mIdent;
421 uint32 mCount;
422};
423
424} // end namespace Security
425
426#endif //_H_THREADING