]> git.saurik.com Git - apple/security.git/blame - cdsa/cdsa_utilities/threading.cpp
Security-30.1.tar.gz
[apple/security.git] / cdsa / cdsa_utilities / threading.cpp
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
23
24//
25// Since we are planning to generate "stub" out of line code for threading methods,
26// we must force THREAD_NDEBUG to off while compiling our header. Trust me.
27//
28#if !defined(THREAD_CLEAN_NDEBUG)
29# define THREAD_MAKE_STUBS
30#endif
31#include <Security/threading.h>
32
33
34//
35// Thread-local storage primitive
36//
37#if _USE_THREADS == _USE_PTHREADS
38
39ThreadStoreSlot::ThreadStoreSlot(Destructor *destructor)
40{
41 if (int err = pthread_key_create(&mKey, destructor))
42 UnixError::throwMe(err);
43}
44
45ThreadStoreSlot::~ThreadStoreSlot()
46{
47 //@@@ if we wanted to dispose of pending task objects, we'd have
48 //@@@ to keep a set of them and delete them explicitly here
49 pthread_key_delete(mKey);
50}
51
52#endif
53
54
55//
56// Mutex implementation
57//
58#if _USE_THREADS == _USE_PTHREADS
59
60#if !defined(THREAD_CLEAN_NDEBUG)
61
62bool Mutex::debugHasInitialized;
63bool Mutex::loggingMutexi;
64
65Mutex::Mutex(bool log)
66{
67#if !defined(THREAD_NDEBUG)
68 // this debug-setup code isn't interlocked, but it's idempotent
69 // (don't worry, be happy)
70 if (!debugHasInitialized) {
71 loggingMutexi = Debug::debugging("mutex");
72 debugHasInitialized = true;
73 }
74 debugLog = log && loggingMutexi;
75 useCount = contentionCount = 0;
76#else
77 debugLog = false;
78#endif //THREAD_NDEBUG
79 check(pthread_mutex_init(&me, NULL));
80}
81
82Mutex::~Mutex()
83{
84#if !defined(THREAD_NDEBUG)
85 if (debugLog && (useCount > 100 || contentionCount > 0))
86 debug("mutex", "%p destroyed after %ld/%ld locks/contentions", this, useCount, contentionCount);
87#endif //THREAD_NDEBUG
88 check(pthread_mutex_destroy(&me));
89}
90
91void Mutex::lock()
92{
93#if !defined(THREAD_NDEBUG)
94 useCount++;
95 if (debugLog) {
96 switch (int err = pthread_mutex_trylock(&me)) {
97 case 0:
98 break;
99 case EBUSY:
100 if (debugLog)
101 debug("mutex", "%p contended (%ld of %ld)", this, ++contentionCount, useCount);
102 check(pthread_mutex_lock(&me));
103 break;
104 default:
105 UnixError::throwMe(err);
106 }
107 if (useCount % 100 == 0)
108 debug("mutex", "%p locked %ld", this, useCount);
109 else
110 debug("mutex", "%p locked", this);
111 return;
112 }
113#endif //THREAD_NDEBUG
114 check(pthread_mutex_lock(&me));
115}
116
117bool Mutex::tryLock()
118{
119 useCount++;
120 if (int err = pthread_mutex_trylock(&me)) {
121 if (err != EBUSY)
122 UnixError::throwMe(err);
123#if !defined(THREAD_NDEBUG)
124 if (debugLog)
125 debug("mutex", "%p trylock contended (%ld of %ld)",
126 this, ++contentionCount, useCount);
127#endif //THREAD_NDEBUG
128 return false;
129 }
130#if !defined(THREAD_NDEBUG)
131 if (debugLog)
132 if (useCount % 100 == 0)
133 debug("mutex", "%p locked %ld", this, useCount);
134 else
135 debug("mutex", "%p locked", this);
136#endif //THREAD_NDEBUG
137 return true;
138}
139
140void Mutex::unlock()
141{
142#if !defined(MUTEX_NDEBUG)
143 if (debugLog)
144 debug("mutex", "%p unlocked", this);
145#endif //MUTEX_NDEBUG
146 check(pthread_mutex_unlock(&me));
147}
148
149#endif //!THREAD_CLEAN_NDEBUG
150#endif //PTHREADS
151
152
153//
154// CountingMutex implementation.
155// Note that this is a generic implementation based on a specific Mutex type.
156// In other words, it should work no matter how Mutex is implemented.
157// Also note that CountingMutex is expected to interlock properly with Mutex,
158// so you canNOT just use an AtomicCounter here.
159//
160void CountingMutex::enter()
161{
162 lock();
163 mCount++;
164 debug("mutex", "%p up to %d", this, mCount);
165 unlock();
166}
167
168bool CountingMutex::tryEnter()
169{
170 if (!tryLock())
171 return false;
172 mCount++;
173 debug("mutex", "%p up to %d (was try)", this, mCount);
174 unlock();
175 return true;
176}
177
178void CountingMutex::exit()
179{
180 lock();
181 assert(mCount > 0);
182 mCount--;
183 debug("mutex", "%p down to %d", this, mCount);
184 unlock();
185}
186
187void CountingMutex::finishEnter()
188{
189 mCount++;
190 debug("mutex", "%p finish up to %d", this, mCount);
191 unlock();
192}
193
194void CountingMutex::finishExit()
195{
196 assert(mCount > 0);
197 mCount--;
198 debug("mutex", "%p finish down to %d", this, mCount);
199 unlock();
200}
201
202
203
204//
205// Threads implementation
206//
207#if _USE_THREADS == _USE_PTHREADS
208
209Thread::~Thread()
210{
211}
212
213void Thread::run()
214{
215 if (int err = pthread_create(&self.mIdent, NULL, runner, this))
216 UnixError::throwMe(err);
217 debug("thread", "%p created", self.mIdent);
218}
219
220void *Thread::runner(void *arg)
221{
222 Thread *me = static_cast<Thread *>(arg);
223 if (int err = pthread_detach(me->self.mIdent))
224 UnixError::throwMe(err);
225 debug("thread", "%p starting", me->self.mIdent);
226 me->action();
227 debug("thread", "%p terminating", me->self.mIdent);
228 delete me;
229 return NULL;
230}
231
232void Thread::yield()
233{
234 sched_yield();
235}
236
237#if !defined(NDEBUG)
238
239#include <Security/memutils.h>
240
241void Thread::Identity::getIdString(char id[idLength])
242{
243 pthread_t current = pthread_self();
244 // We're not supposed to know what a pthread_t is. Just print the first few bytes...
245 // (On MacOS X, it's a pointer to a pthread_t internal structure, so this works fine.)
246 void *p;
247 memcpy(&p, &current, sizeof(p));
248 snprintf(id, idLength, "%lx", long(p));
249}
250
251#endif // NDEBUG
252
253#endif // PTHREADS
254
255
256//
257// ThreadRunner implementation
258//
259ThreadRunner::ThreadRunner(Action *todo)
260{
261 mAction = todo;
262 run();
263}
264
265void ThreadRunner::action()
266{
267 mAction();
268}
269
270
271//
272// Nesting Mutexi.
273// This implementation uses mWait as a "sloppy" wait blocker (only).
274// It should be a semaphore of course, but we don't have a semaphore
275// abstraction right now. The authoritative locking protocol is based on mLock.
276//
277NestingMutex::NestingMutex() : mCount(0)
278{ }
279
280void NestingMutex::lock()
281{
282 while (!tryLock()) {
283 mWait.lock();
284 mWait.unlock();
285 }
286}
287
288bool NestingMutex::tryLock()
289{
290 StLock<Mutex> _(mLock);
291 if (mCount == 0) { // initial lock
292 mCount = 1;
293 mIdent = Thread::Identity::current();
294 mWait.lock();
295 return true;
296 } else if (mIdent == Thread::Identity::current()) { // recursive lock
297 mCount++;
298 return true;
299 } else { // locked by another thread
300 return false;
301 }
302}
303
304void NestingMutex::unlock()
305{
306 StLock<Mutex> _(mLock);
307 assert(mCount > 0 && mIdent == Thread::Identity::current());
308 if (--mCount == 0) // last recursive unlock
309 mWait.unlock();
310}