]>
git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/threading.h
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // threading - generic thread support
25 #include <Security/utilities.h>
26 #include <Security/debugging.h>
28 #if _USE_THREADS == _USE_PTHREADS
32 #include <Security/threading_internal.h>
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.
48 #if defined(NDEBUG) || defined(THREAD_CLEAN_NDEBUG)
49 # if !defined(THREAD_NDEBUG)
50 # define THREAD_NDEBUG
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.
60 #if _USE_THREADS == _USE_PTHREADS
62 class ThreadStoreSlot
{
64 typedef void Destructor(void *);
65 ThreadStoreSlot(Destructor
*destructor
= NULL
);
68 void *get() const { return pthread_getspecific(mKey
); }
69 operator void * () const { return get(); }
70 void operator = (void *value
) const
72 if (int err
= pthread_setspecific(mKey
, value
))
73 UnixError::throwMe(err
);
80 #endif //_USE_PTHREADS
84 // Per-thread pointers are patterned after the pthread TLS (thread local storage)
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...
91 #if _USE_THREADS == _USE_PTHREADS
94 class PerThreadPointer
: public ThreadStoreSlot
{
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
); }
104 static void destructor(void *element
)
105 { delete reinterpret_cast<T
*>(element
); }
108 #elif _USE_THREADS == _USE_NO_THREADS
111 class PerThreadPointer
{
113 PerThreadPointer(bool cleanup
= true) : mCleanup(cleanup
) { }
114 ~PerThreadPointer() { /* no cleanup - see comment above */ }
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
; }
127 # error Unsupported threading model
128 #endif //_USE_THREADS
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.
137 #if _USE_THREADS == _USE_PTHREADS
142 void check(int err
) { if (err
) UnixError::throwMe(err
); }
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
)); }
149 if (int err
= pthread_mutex_trylock(&me
))
150 if (err
== EBUSY
) return false; else UnixError::throwMe(err
);
153 void unlock() { check(pthread_mutex_unlock(&me
)); }
154 ~Mutex() { check(pthread_mutex_destroy(&me
)); }
155 #else //THREAD_NDEBUG
156 Mutex(bool log
= true);
161 #endif //THREAD_NDEBUG
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
175 #elif _USE_THREADS == _USE_NO_THREADS
179 void lock(bool = true) { }
181 bool tryLock() { return true; }
185 # error Unsupported threading model
186 #endif //_USE_THREADS
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.
194 class CountingMutex
: public Mutex
{
195 // note that this implementation works for any system implementing Mutex *somehow*
197 CountingMutex() : mCount(0) { }
198 ~CountingMutex() { assert(mCount
== 0); }
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; }
208 // convert Mutex lock to CountingMutex enter/exit. Expert use only
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).
222 // NOTE: StLock itself is not thread-safe. It is intended for use (usually on the stack)
223 // by a single thread.
225 template <class Lock
,
226 void (Lock::*_lock
)() = &Lock::lock
,
227 void (Lock::*_unlock
)() = &Lock::unlock
>
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
)(); }
234 bool isActive() const { return mActive
; }
235 void lock() { if(!mActive
) { (me
.*_lock
)(); mActive
= true; }}
236 void unlock() { if(mActive
) { (me
.*_unlock
)(); mActive
= false; }}
237 void release() { assert(mActive
); mActive
= false; }
239 operator const Lock
&() const { return me
; }
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...
258 template <class Integer
= int>
259 class StaticAtomicCounter
{
262 #if defined(_HAVE_ATOMIC_OPERATIONS)
265 operator Integer() const { return mValue
; }
267 // infix versions (primary)
268 Integer
operator ++ () { return atomicIncrement(mValue
); }
269 Integer
operator -- () { return atomicDecrement(mValue
); }
272 Integer
operator ++ (int) { return atomicIncrement(mValue
) - 1; }
273 Integer
operator -- (int) { return atomicDecrement(mValue
) + 1; }
276 Integer
operator += (int delta
) { return atomicOffset(mValue
, delta
); }
278 #else // no atomic integers, use locks
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
; }
294 template <class Integer
= int>
295 class AtomicCounter
: public StaticAtomicCounter
<Integer
> {
297 AtomicCounter(Integer init
= 0) { mValue
= 0; }
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.
306 #if _USE_THREADS == _USE_PTHREADS
314 Identity(pthread_t id
) : mIdent(id
) { }
318 static Identity
current() { return pthread_self(); }
320 bool operator == (const Identity
&other
) const
321 { return pthread_equal(mIdent
, other
.mIdent
); }
323 bool operator != (const Identity
&other
) const
324 { return !(*this == other
); }
327 static const int idLength
= 10;
328 static void getIdString(char id
[idLength
]);
336 Thread() { } // constructor
337 virtual ~Thread(); // virtual destructor
338 void run(); // begin running the thread
341 static void yield(); // unstructured short-term processor yield
344 virtual void action() = 0; // the action to be performed
347 Identity self
; // my own identity (instance constant)
349 static void *runner(void *); // argument to pthread_create
352 #elif _USE_THREADS == _USE_NO_THREADS
357 Thread() { } // constructor
358 virtual ~Thread() { } // virtual destructor
359 void run() { action(); } // just synchronously run the action
364 static Identity
current() { return Identity(); }
366 bool operator == (const Identity
&) const { return true; } // all the same
367 bool operator != (const Identity
&) const { return false; }
370 static const idLength
= 9;
371 static void getIdString(char id
[idLength
]) { memcpy(id
, "nothread", idLength
); }
379 void yield() { assert(false); }
382 virtual void action() = 0; // implement action of thread
386 # error Unsupported threading model
391 // A "just run this function in a thread" variant of Thread
393 class ThreadRunner
: public Thread
{
394 typedef void Action();
396 ThreadRunner(Action
*todo
);
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.
420 Thread::Identity mIdent
;
424 } // end namespace Security
426 #endif //_H_THREADING