]>
Commit | Line | Data |
---|---|---|
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 | ||
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. | |
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 | ||
93 | template <class T> | |
94 | class PerThreadPointer : public ThreadStoreSlot { | |
95 | public: | |
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 | ||
103 | private: | |
104 | static void destructor(void *element) | |
105 | { delete reinterpret_cast<T *>(element); } | |
106 | }; | |
107 | ||
108 | #elif _USE_THREADS == _USE_NO_THREADS | |
109 | ||
110 | template <class T> | |
111 | class PerThreadPointer { | |
112 | public: | |
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 | ||
121 | private: | |
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 | ||
139 | class Mutex { | |
140 | NOCOPY(Mutex) | |
141 | ||
142 | void check(int err) { if (err) UnixError::throwMe(err); } | |
143 | ||
144 | public: | |
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 | ||
163 | private: | |
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 | ||
177 | class Mutex { | |
178 | public: | |
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 | // | |
194 | class CountingMutex : public Mutex { | |
195 | // note that this implementation works for any system implementing Mutex *somehow* | |
196 | public: | |
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 | ||
212 | private: | |
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 | // | |
225 | template <class Lock, | |
226 | void (Lock::*_lock)() = &Lock::lock, | |
227 | void (Lock::*_unlock)() = &Lock::unlock> | |
228 | class StLock { | |
229 | public: | |
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 | ||
241 | protected: | |
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 | // | |
258 | template <class Integer = int> | |
259 | class StaticAtomicCounter { | |
260 | protected: | |
261 | ||
262 | #if defined(_HAVE_ATOMIC_OPERATIONS) | |
263 | AtomicWord mValue; | |
264 | public: | |
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; | |
282 | public: | |
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 | ||
294 | template <class Integer = int> | |
295 | class AtomicCounter : public StaticAtomicCounter<Integer> { | |
296 | public: | |
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 | ||
308 | class Thread { | |
309 | NOCOPY(Thread) | |
bac41a7b A |
310 | public: |
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 |
335 | public: |
336 | Thread() { } // constructor | |
337 | virtual ~Thread(); // virtual destructor | |
338 | void run(); // begin running the thread | |
339 | ||
340 | public: | |
341 | static void yield(); // unstructured short-term processor yield | |
342 | ||
bac41a7b A |
343 | protected: |
344 | virtual void action() = 0; // the action to be performed | |
345 | ||
346 | private: | |
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 | ||
354 | class Thread { | |
355 | NOCOPY(Thread) | |
356 | public: | |
357 | Thread() { } // constructor | |
358 | virtual ~Thread() { } // virtual destructor | |
359 | void run() { action(); } // just synchronously run the action | |
360 | ||
361 | public: | |
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 | |
378 | public: | |
379 | void yield() { assert(false); } | |
bac41a7b A |
380 | |
381 | protected: | |
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 | // | |
393 | class ThreadRunner : public Thread { | |
394 | typedef void Action(); | |
395 | public: | |
396 | ThreadRunner(Action *todo); | |
397 | ||
398 | private: | |
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 | // | |
409 | class NestingMutex { | |
410 | public: | |
411 | NestingMutex(); | |
412 | ||
413 | void lock(); | |
414 | bool tryLock(); | |
415 | void unlock(); | |
416 | ||
417 | private: | |
418 | Mutex mLock; | |
419 | Mutex mWait; | |
420 | Thread::Identity mIdent; | |
421 | uint32 mCount; | |
422 | }; | |
423 | ||
424 | } // end namespace Security | |
425 | ||
426 | #endif //_H_THREADING |