]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2000-2004,2011,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | // | |
26 | // threading - multi-threading support | |
27 | // | |
28 | // Once upon a time, this file provided a system-independent abstraction layer | |
29 | // for various thread models. These times are long gone, and we might as well | |
30 | // admit that we're sitting on top of pthreads (plus certain other system facilities). | |
31 | // | |
32 | #ifndef _H_THREADING | |
33 | #define _H_THREADING | |
34 | ||
35 | #include <security_utilities/utilities.h> | |
36 | #include <security_utilities/errors.h> | |
37 | #include <security_utilities/debugging.h> | |
38 | # include <pthread.h> | |
39 | ||
40 | #include <security_utilities/threading_internal.h> | |
41 | ||
42 | ||
43 | namespace Security { | |
44 | ||
45 | ||
46 | // | |
47 | // Potentially, debug-logging all Mutex activity can really ruin your | |
48 | // performance day. We take some measures to reduce the impact, but if | |
49 | // you really can't stomach any overhead, define THREAD_NDEBUG to turn | |
50 | // (only) thread debug-logging off. NDEBUG will turn this on automatically. | |
51 | // On the other hand, throwing out all debug code will change the ABI of | |
52 | // Mutexi in incompatible ways. Thus, we still generate the debug-style out-of-line | |
53 | // code even with THREAD_NDEBUG, so that debug-style code will work with us. | |
54 | // If you want to ditch it completely, #define THREAD_CLEAN_NDEBUG. | |
55 | // | |
56 | #if defined(NDEBUG) || defined(THREAD_CLEAN_NDEBUG) | |
57 | # if !defined(THREAD_NDEBUG) | |
58 | # define THREAD_NDEBUG | |
59 | # endif | |
60 | #endif | |
61 | ||
62 | ||
63 | // | |
64 | // An abstraction of a per-thread untyped storage slot of pointer size. | |
65 | // Do not use this in ordinary code; this is for implementing other primitives only. | |
66 | // Use a PerThreadPointer or ThreadNexus. | |
67 | // | |
68 | class ThreadStoreSlot { | |
69 | public: | |
70 | typedef void Destructor(void *); | |
71 | ThreadStoreSlot(Destructor *destructor = NULL); | |
72 | ~ThreadStoreSlot(); | |
73 | ||
74 | void *get() const { return pthread_getspecific(mKey); } | |
75 | operator void * () const { return get(); } | |
76 | void operator = (void *value) const | |
77 | { | |
78 | if (int err = pthread_setspecific(mKey, value)) | |
79 | UnixError::throwMe(err); | |
80 | } | |
81 | ||
82 | private: | |
83 | pthread_key_t mKey; | |
84 | }; | |
85 | ||
86 | ||
87 | // | |
88 | // Per-thread pointers are implemented using the pthread TLS (thread local storage) | |
89 | // facility. | |
90 | // Let's be clear on what gets destroyed when, here. Following the pthread lead, | |
91 | // when a thread dies its PerThreadPointer object(s) are properly destroyed. | |
92 | // However, if a PerThreadPointer itself is destroyed, NOTHING HAPPENS. Yes, there are | |
93 | // reasons for this. This is not (on its face) a bug, so don't yell. But be aware... | |
94 | // | |
95 | template <class T> | |
96 | class PerThreadPointer : public ThreadStoreSlot { | |
97 | public: | |
98 | PerThreadPointer(bool cleanup = true) : ThreadStoreSlot(cleanup ? destructor : NULL) { } | |
99 | operator bool() const { return get() != NULL; } | |
100 | operator T * () const { return reinterpret_cast<T *>(get()); } | |
101 | T *operator -> () const { return static_cast<T *>(*this); } | |
102 | T &operator * () const { return *static_cast<T *>(get()); } | |
103 | void operator = (T *t) { ThreadStoreSlot::operator = (t); } | |
104 | ||
105 | private: | |
106 | static void destructor(void *element) | |
107 | { delete reinterpret_cast<T *>(element); } | |
108 | }; | |
109 | ||
110 | ||
111 | // | |
112 | // Pthread Synchronization primitives. | |
113 | // These have a common header, strictly for our convenience. | |
114 | // | |
115 | class LockingPrimitive { | |
116 | protected: | |
117 | LockingPrimitive() { } | |
118 | ||
119 | void check(int err) { if (err) UnixError::throwMe(err); } | |
120 | }; | |
121 | ||
122 | ||
123 | // | |
124 | // Mutexi | |
125 | // | |
126 | class Mutex : public LockingPrimitive { | |
127 | NOCOPY(Mutex) | |
128 | friend class Condition; | |
129 | ||
130 | public: | |
131 | enum Type { | |
132 | normal, | |
133 | recursive | |
134 | }; | |
135 | ||
136 | Mutex(); // normal | |
137 | Mutex(Type type); // recursive | |
138 | ~Mutex(); // destroy (must be unlocked) | |
139 | void lock(); // lock and wait | |
140 | bool tryLock(); // instantaneous lock (return false if busy) | |
141 | void unlock(); // unlock (must be locked) | |
142 | ||
143 | private: | |
144 | pthread_mutex_t me; | |
145 | }; | |
146 | ||
147 | ||
148 | class RecursiveMutex : public Mutex | |
149 | { | |
150 | public: | |
151 | RecursiveMutex() : Mutex(recursive) {} | |
152 | ~RecursiveMutex() {} | |
153 | }; | |
154 | ||
6b200bc3 A |
155 | class NormalMutex : public Mutex |
156 | { | |
157 | public: | |
158 | NormalMutex() : Mutex(normal) {} | |
159 | ~NormalMutex() {} | |
160 | }; | |
161 | ||
b1ab9ed8 A |
162 | // |
163 | // Condition variables | |
164 | // | |
165 | class Condition : public LockingPrimitive { | |
166 | NOCOPY(Condition) | |
167 | ||
168 | public: | |
169 | Condition(Mutex &mutex); // create with specific Mutex | |
170 | ~Condition(); | |
171 | void wait(); // wait for signal | |
172 | void signal(); // signal one | |
173 | void broadcast(); // signal all | |
174 | ||
175 | Mutex &mutex; // associated Mutex | |
176 | ||
177 | private: | |
178 | pthread_cond_t me; | |
179 | }; | |
180 | ||
181 | ||
182 | // | |
183 | // A CountingMutex adds a counter to a Mutex. | |
184 | // NOTE: This is not officially a semaphore - it's an automatically managed | |
185 | // counter married to a Mutex. | |
186 | // | |
187 | class CountingMutex : public Mutex { | |
188 | public: | |
189 | CountingMutex() : mCount(0) { } | |
190 | ~CountingMutex() { assert(mCount == 0); } | |
191 | ||
192 | void enter(); // lock, add one, unlock | |
193 | bool tryEnter(); // enter or return false | |
194 | void exit(); // lock, subtract one, unlock | |
195 | ||
196 | // these methods do not lock - use only while you hold the lock | |
197 | unsigned int count() const { return mCount; } | |
198 | bool isIdle() const { return mCount == 0; } | |
199 | ||
200 | // convert Mutex lock to CountingMutex enter/exit. Expert use only | |
201 | void finishEnter(); // all but the initial lock | |
202 | void finishExit(); // all but the initial lock | |
203 | ||
204 | private: | |
205 | unsigned int mCount; // counter level | |
206 | }; | |
e3d460c9 A |
207 | |
208 | // | |
209 | // A ReadWriteLock is a wrapper around a pthread_rwlock | |
210 | // | |
211 | class ReadWriteLock : public Mutex { | |
212 | public: | |
213 | ReadWriteLock(); | |
214 | ~ReadWriteLock() { check(pthread_rwlock_destroy(&mLock)); } | |
215 | ||
216 | // Takes the read lock | |
217 | bool lock(); | |
218 | bool tryLock(); | |
219 | void unlock(); | |
220 | ||
221 | bool writeLock(); | |
222 | bool tryWriteLock(); | |
223 | ||
224 | private: | |
225 | pthread_rwlock_t mLock; | |
226 | }; | |
227 | ||
b1ab9ed8 A |
228 | |
229 | // | |
230 | // A guaranteed-unlocker stack-based class. | |
231 | // By default, this will use lock/unlock methods, but you can provide your own | |
232 | // alternates (to, e.g., use enter/exit, or some more specialized pair of operations). | |
233 | // | |
234 | // NOTE: StLock itself is not thread-safe. It is intended for use (usually on the stack) | |
235 | // by a single thread. | |
236 | // | |
237 | template <class Lock, | |
238 | void (Lock::*_lock)() = &Lock::lock, | |
239 | void (Lock::*_unlock)() = &Lock::unlock> | |
240 | class StLock { | |
241 | public: | |
242 | StLock(Lock &lck) : me(lck) { (me.*_lock)(); mActive = true; } | |
243 | StLock(Lock &lck, bool option) : me(lck), mActive(option) { } | |
244 | ~StLock() { if (mActive) (me.*_unlock)(); } | |
245 | ||
246 | bool isActive() const { return mActive; } | |
247 | void lock() { if(!mActive) { (me.*_lock)(); mActive = true; }} | |
248 | void unlock() { if(mActive) { (me.*_unlock)(); mActive = false; }} | |
249 | void release() { assert(mActive); mActive = false; } | |
250 | ||
251 | operator const Lock &() const { return me; } | |
252 | ||
253 | protected: | |
254 | Lock &me; | |
255 | bool mActive; | |
256 | }; | |
257 | ||
fa7225c8 A |
258 | // |
259 | // This class behaves exactly as StLock above, but accepts a pointer to a mutex instead of a reference. | |
260 | // If the pointer is NULL, this class does nothing. Otherwise, it behaves as StLock. | |
261 | // Try not to use this. | |
262 | // | |
263 | template <class Lock, | |
264 | void (Lock::*_lock)() = &Lock::lock, | |
265 | void (Lock::*_unlock)() = &Lock::unlock> | |
266 | class StMaybeLock { | |
267 | public: | |
268 | StMaybeLock(Lock *lck) : me(lck), mActive(false) | |
269 | { if(me) { (me->*_lock)(); mActive = true; } } | |
270 | StMaybeLock(Lock *lck, bool option) : me(lck), mActive(option) { } | |
271 | ~StMaybeLock() { if (me) { if(mActive) (me->*_unlock)(); } else {mActive = false;} } | |
272 | ||
273 | bool isActive() const { return mActive; } | |
274 | void lock() { if(me) { if(!mActive) { (me->*_lock)(); mActive = true; }}} | |
275 | void unlock() { if(me) { if(mActive) { (me->*_unlock)(); mActive = false; }}} | |
276 | void release() { if(me) { assert(mActive); mActive = false; } } | |
277 | ||
278 | operator const Lock &() const { return me; } | |
279 | ||
280 | protected: | |
281 | Lock *me; | |
282 | bool mActive; | |
283 | }; | |
284 | ||
e3d460c9 A |
285 | // Note: if you use the TryRead or TryWrite modes, you must check if you |
286 | // actually have the lock before proceeding | |
287 | class StReadWriteLock { | |
288 | public: | |
289 | enum Type { | |
290 | Read, | |
291 | TryRead, | |
292 | Write, | |
293 | TryWrite | |
294 | }; | |
295 | StReadWriteLock(ReadWriteLock &lck, Type type) : mType(type), mIsLocked(false), mRWLock(lck) | |
296 | { lock(); } | |
b04fe171 | 297 | ~StReadWriteLock() { if(mIsLocked) unlock(); } |
e3d460c9 A |
298 | |
299 | bool lock(); | |
300 | void unlock(); | |
301 | bool isLocked(); | |
302 | ||
303 | protected: | |
304 | Type mType; | |
305 | bool mIsLocked; | |
306 | ReadWriteLock& mRWLock; | |
307 | }; | |
308 | ||
309 | ||
b1ab9ed8 A |
310 | template <class TakeLock, class ReleaseLock, |
311 | void (TakeLock::*_lock)() = &TakeLock::lock, | |
312 | void (TakeLock::*_unlock)() = &TakeLock::unlock, | |
313 | void (ReleaseLock::*_rlock)() = &ReleaseLock::lock, | |
314 | void (ReleaseLock::*_runlock)() = &ReleaseLock::unlock> | |
315 | class StSyncLock { | |
316 | public: | |
317 | StSyncLock(TakeLock &tlck, ReleaseLock &rlck) : taken(tlck), released(rlck) { | |
318 | (released.*_unlock)(); | |
319 | (taken.*_lock)(); | |
320 | mActive = true; | |
321 | } | |
322 | StSyncLock(TakeLock &tlck, ReleaseLock &rlck, bool option) : taken(tlck), released(rlck), mActive(option) { } | |
323 | ~StSyncLock() { if (mActive) { (taken.*_unlock)(); (released.*_rlock)(); }} | |
324 | ||
325 | bool isActive() const { return mActive; } | |
326 | void lock() { if(!mActive) { (released.*_runlock)(); (taken.*_lock)(); mActive = true; }} | |
327 | void unlock() { if(mActive) { (taken.*_unlock)(); (released.*_rlock)(); mActive = false; }} | |
328 | void release() { assert(mActive); mActive = false; } | |
329 | ||
330 | protected: | |
331 | TakeLock &taken; | |
332 | ReleaseLock &released; | |
333 | bool mActive; | |
334 | }; | |
335 | ||
336 | ||
337 | // | |
338 | // Atomic increment/decrement operations. | |
339 | // The default implementation uses a Mutex. However, many architectures can do | |
340 | // much better than that. | |
341 | // Be very clear on the nature of AtomicCounter. It implies no memory barriers of | |
342 | // any kind. This means that (1) you cannot protect any other memory region with it | |
343 | // (use a Mutex for that), and (2) it may not enforce cross-processor ordering, which | |
344 | // means that you have no guarantee that you'll see modifications by other processors | |
345 | // made earlier (unless another mechanism provides the memory barrier). | |
346 | // On the other hand, if your compiler has brains, this is blindingly fast... | |
347 | // | |
348 | template <class Integer = uint32_t> | |
349 | class StaticAtomicCounter { | |
350 | protected: | |
351 | Integer mValue; | |
352 | ||
353 | public: | |
354 | operator Integer() const { return mValue; } | |
355 | ||
356 | // infix versions (primary) | |
357 | Integer operator ++ () { return Atomic<Integer>::increment(mValue); } | |
358 | Integer operator -- () { return Atomic<Integer>::decrement(mValue); } | |
359 | ||
360 | // postfix versions | |
361 | Integer operator ++ (int) { return Atomic<Integer>::increment(mValue) - 1; } | |
362 | Integer operator -- (int) { return Atomic<Integer>::decrement(mValue) + 1; } | |
363 | ||
364 | // generic offset | |
365 | Integer operator += (int delta) { return Atomic<Integer>::add(delta, mValue); } | |
366 | }; | |
367 | ||
368 | ||
369 | template <class Integer = int> | |
370 | class AtomicCounter : public StaticAtomicCounter<Integer> { | |
371 | public: | |
372 | AtomicCounter(Integer init = 0) { StaticAtomicCounter<Integer>::mValue = init; } | |
373 | }; | |
374 | ||
375 | ||
376 | // | |
377 | // A class implementing a separate thread of execution. | |
378 | // Do not expect many high-level semantics to be portable. If you can, | |
379 | // restrict yourself to expect parallel execution and little else. | |
380 | // | |
381 | class Thread { | |
382 | NOCOPY(Thread) | |
b1ab9ed8 A |
383 | |
384 | public: | |
385 | Thread() { } // constructor | |
386 | virtual ~Thread(); // virtual destructor | |
387 | void run(); // begin running the thread | |
388 | ||
389 | public: | |
390 | static void yield(); // unstructured short-term processor yield | |
391 | ||
392 | protected: | |
393 | virtual void action() = 0; // the action to be performed | |
394 | ||
395 | private: | |
b1ab9ed8 A |
396 | static void *runner(void *); // argument to pthread_create |
397 | }; | |
398 | ||
399 | ||
400 | // | |
401 | // A "just run this function in a thread" variant of Thread | |
402 | // | |
403 | class ThreadRunner : public Thread { | |
404 | typedef void Action(); | |
405 | public: | |
406 | ThreadRunner(Action *todo); | |
407 | ||
408 | private: | |
409 | void action(); | |
410 | Action *mAction; | |
411 | }; | |
412 | ||
413 | ||
414 | } // end namespace Security | |
415 | ||
416 | #endif //_H_THREADING |