]>
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: | |
bac41a7b A |
145 | Mutex(bool log = true); |
146 | ~Mutex(); | |
147 | void lock(); | |
148 | bool tryLock(); | |
149 | void unlock(); | |
bac41a7b A |
150 | |
151 | private: | |
152 | pthread_mutex_t me; | |
153 | ||
bac41a7b A |
154 | bool debugLog; // log *this* mutex |
155 | unsigned long useCount; // number of locks succeeded | |
156 | unsigned long contentionCount; // number of contentions (valid only if debugLog) | |
157 | static bool debugHasInitialized; // global: debug state set up | |
158 | static bool loggingMutexi; // global: we are debug-logging mutexi | |
bac41a7b A |
159 | }; |
160 | ||
161 | #elif _USE_THREADS == _USE_NO_THREADS | |
162 | ||
163 | class Mutex { | |
164 | public: | |
165 | void lock(bool = true) { } | |
166 | void unlock() { } | |
167 | bool tryLock() { return true; } | |
168 | }; | |
169 | ||
170 | #else | |
171 | # error Unsupported threading model | |
172 | #endif //_USE_THREADS | |
173 | ||
174 | ||
175 | // | |
176 | // A CountingMutex adds a counter to a Mutex. | |
177 | // NOTE: This is not officially a semaphore, even if it happens to be implemented with | |
178 | // one on some platforms. | |
179 | // | |
180 | class CountingMutex : public Mutex { | |
181 | // note that this implementation works for any system implementing Mutex *somehow* | |
182 | public: | |
183 | CountingMutex() : mCount(0) { } | |
184 | ~CountingMutex() { assert(mCount == 0); } | |
185 | ||
186 | void enter(); | |
187 | bool tryEnter(); | |
188 | void exit(); | |
189 | ||
190 | // these methods do not lock - use only while you hold the lock | |
191 | unsigned int count() const { return mCount; } | |
192 | bool isIdle() const { return mCount == 0; } | |
193 | ||
194 | // convert Mutex lock to CountingMutex enter/exit. Expert use only | |
195 | void finishEnter(); | |
196 | void finishExit(); | |
197 | ||
198 | private: | |
199 | unsigned int mCount; | |
200 | }; | |
201 | ||
202 | ||
203 | // | |
204 | // A guaranteed-unlocker stack-based class. | |
205 | // By default, this will use lock/unlock methods, but you can provide your own | |
206 | // alternates (to, e.g., use enter/exit, or some more specialized pair of operations). | |
207 | // | |
208 | // NOTE: StLock itself is not thread-safe. It is intended for use (usually on the stack) | |
209 | // by a single thread. | |
210 | // | |
211 | template <class Lock, | |
212 | void (Lock::*_lock)() = &Lock::lock, | |
213 | void (Lock::*_unlock)() = &Lock::unlock> | |
214 | class StLock { | |
215 | public: | |
216 | StLock(Lock &lck) : me(lck) { (me.*_lock)(); mActive = true; } | |
217 | StLock(Lock &lck, bool option) : me(lck), mActive(option) { } | |
218 | ~StLock() { if (mActive) (me.*_unlock)(); } | |
219 | ||
220 | bool isActive() const { return mActive; } | |
221 | void lock() { if(!mActive) { (me.*_lock)(); mActive = true; }} | |
222 | void unlock() { if(mActive) { (me.*_unlock)(); mActive = false; }} | |
29654253 | 223 | void release() { assert(mActive); mActive = false; } |
bac41a7b A |
224 | |
225 | operator const Lock &() const { return me; } | |
226 | ||
227 | protected: | |
228 | Lock &me; | |
229 | bool mActive; | |
230 | }; | |
231 | ||
232 | ||
233 | // | |
234 | // Atomic increment/decrement operations. | |
235 | // The default implementation uses a Mutex. However, many architectures can do | |
236 | // much better than that. | |
237 | // Be very clear on the nature of AtomicCounter. It implies no memory barriers of | |
238 | // any kind. This means that (1) you cannot protect any other memory region with it | |
239 | // (use a Mutex for that), and (2) it may not enforce cross-processor ordering, which | |
240 | // means that you have no guarantee that you'll see modifications by other processors | |
241 | // made earlier (unless another mechanism provides the memory barrier). | |
242 | // On the other hand, if your compiler has brains, this is blindingly fast... | |
243 | // | |
244 | template <class Integer = int> | |
245 | class StaticAtomicCounter { | |
246 | protected: | |
247 | ||
248 | #if defined(_HAVE_ATOMIC_OPERATIONS) | |
249 | AtomicWord mValue; | |
250 | public: | |
251 | operator Integer() const { return mValue; } | |
252 | ||
253 | // infix versions (primary) | |
254 | Integer operator ++ () { return atomicIncrement(mValue); } | |
255 | Integer operator -- () { return atomicDecrement(mValue); } | |
256 | ||
257 | // postfix versions | |
258 | Integer operator ++ (int) { return atomicIncrement(mValue) - 1; } | |
259 | Integer operator -- (int) { return atomicDecrement(mValue) + 1; } | |
260 | ||
261 | // generic offset | |
262 | Integer operator += (int delta) { return atomicOffset(mValue, delta); } | |
263 | ||
264 | #else // no atomic integers, use locks | |
265 | ||
266 | Integer mValue; | |
267 | mutable Mutex mLock; | |
268 | public: | |
269 | StaticAtomicCounter(Integer init = 0) : mValue(init), mLock(false) { } | |
270 | operator Integer() const { StLock<Mutex> _(mLock); return mValue; } | |
271 | Integer operator ++ () { StLock<Mutex> _(mLock); return ++mValue; } | |
272 | Integer operator -- () { StLock<Mutex> _(mLock); return --mValue; } | |
273 | Integer operator ++ (int) { StLock<Mutex> _(mLock); return mValue++; } | |
274 | Integer operator -- (int) { StLock<Mutex> _(mLock); return mValue--; } | |
275 | Integer operator += (int delta) { StLock<Mutex> _(mLock); return mValue += delta; } | |
276 | #endif | |
277 | }; | |
278 | ||
279 | ||
280 | template <class Integer = int> | |
281 | class AtomicCounter : public StaticAtomicCounter<Integer> { | |
282 | public: | |
283 | AtomicCounter(Integer init = 0) { mValue = 0; } | |
284 | }; | |
285 | ||
286 | ||
287 | // | |
288 | // A class implementing a separate thread of execution. | |
289 | // Do not expect many high-level semantics to be portable. If you can, | |
290 | // restrict yourself to expect parallel execution and little else. | |
291 | // | |
292 | #if _USE_THREADS == _USE_PTHREADS | |
293 | ||
294 | class Thread { | |
295 | NOCOPY(Thread) | |
bac41a7b A |
296 | public: |
297 | class Identity { | |
298 | friend class Thread; | |
29654253 A |
299 | |
300 | Identity(pthread_t id) : mIdent(id) { } | |
bac41a7b A |
301 | public: |
302 | Identity() { } | |
303 | ||
304 | static Identity current() { return pthread_self(); } | |
305 | ||
306 | bool operator == (const Identity &other) const | |
307 | { return pthread_equal(mIdent, other.mIdent); } | |
308 | ||
309 | bool operator != (const Identity &other) const | |
310 | { return !(*this == other); } | |
311 | ||
df0e469f A |
312 | // visible thread identifiers are FOR DEBUGGING ONLY |
313 | // if you use this for production code, your code will rot after shipment :-) | |
bac41a7b A |
314 | static const int idLength = 10; |
315 | static void getIdString(char id[idLength]); | |
bac41a7b A |
316 | |
317 | private: | |
318 | pthread_t mIdent; | |
bac41a7b A |
319 | }; |
320 | ||
29654253 A |
321 | public: |
322 | Thread() { } // constructor | |
323 | virtual ~Thread(); // virtual destructor | |
324 | void run(); // begin running the thread | |
325 | ||
326 | public: | |
327 | static void yield(); // unstructured short-term processor yield | |
328 | ||
bac41a7b A |
329 | protected: |
330 | virtual void action() = 0; // the action to be performed | |
331 | ||
332 | private: | |
333 | Identity self; // my own identity (instance constant) | |
334 | ||
335 | static void *runner(void *); // argument to pthread_create | |
336 | }; | |
337 | ||
338 | #elif _USE_THREADS == _USE_NO_THREADS | |
339 | ||
340 | class Thread { | |
341 | NOCOPY(Thread) | |
342 | public: | |
343 | Thread() { } // constructor | |
344 | virtual ~Thread() { } // virtual destructor | |
345 | void run() { action(); } // just synchronously run the action | |
346 | ||
347 | public: | |
348 | class Identity { | |
349 | public: | |
350 | static Identity current() { return Identity(); } | |
351 | ||
352 | bool operator == (const Identity &) const { return true; } // all the same | |
353 | bool operator != (const Identity &) const { return false; } | |
354 | ||
355 | #if !defined(NDEBUG) | |
356 | static const idLength = 9; | |
357 | static void getIdString(char id[idLength]) { memcpy(id, "nothread", idLength); } | |
358 | #endif | |
359 | ||
360 | private: | |
361 | Identity() { } | |
362 | }; | |
29654253 A |
363 | |
364 | public: | |
365 | void yield() { assert(false); } | |
bac41a7b A |
366 | |
367 | protected: | |
368 | virtual void action() = 0; // implement action of thread | |
369 | }; | |
370 | ||
371 | #else | |
372 | # error Unsupported threading model | |
373 | #endif | |
374 | ||
375 | ||
376 | // | |
377 | // A "just run this function in a thread" variant of Thread | |
378 | // | |
379 | class ThreadRunner : public Thread { | |
380 | typedef void Action(); | |
381 | public: | |
382 | ThreadRunner(Action *todo); | |
383 | ||
384 | private: | |
385 | void action(); | |
386 | Action *mAction; | |
387 | }; | |
388 | ||
389 | ||
390 | // | |
391 | // A NestingMutex allows recursive re-entry by the same thread. | |
392 | // Some pthread implementations support this through a mutex attribute. | |
393 | // OSX's doesn't, naturally. This implementation works on all pthread platforms. | |
394 | // | |
395 | class NestingMutex { | |
396 | public: | |
397 | NestingMutex(); | |
398 | ||
399 | void lock(); | |
400 | bool tryLock(); | |
401 | void unlock(); | |
402 | ||
403 | private: | |
404 | Mutex mLock; | |
405 | Mutex mWait; | |
406 | Thread::Identity mIdent; | |
407 | uint32 mCount; | |
408 | }; | |
409 | ||
410 | } // end namespace Security | |
411 | ||
412 | #endif //_H_THREADING |