]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/threading.cpp
Security-163.tar.gz
[apple/security.git] / cdsa / cdsa_utilities / threading.cpp
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 #include <Security/threading.h>
29 #include <Security/memutils.h>
30
31
32 //
33 // Thread-local storage primitive
34 //
35 #if _USE_THREADS == _USE_PTHREADS
36
37 ThreadStoreSlot::ThreadStoreSlot(Destructor *destructor)
38 {
39 if (int err = pthread_key_create(&mKey, destructor))
40 UnixError::throwMe(err);
41 }
42
43 ThreadStoreSlot::~ThreadStoreSlot()
44 {
45 //@@@ if we wanted to dispose of pending task objects, we'd have
46 //@@@ to keep a set of them and delete them explicitly here
47 pthread_key_delete(mKey);
48 }
49
50 #endif
51
52
53 //
54 // Mutex implementation
55 //
56 #if _USE_THREADS == _USE_PTHREADS
57
58 bool Mutex::debugHasInitialized;
59 bool Mutex::loggingMutexi;
60
61 Mutex::Mutex(bool log)
62 {
63 #if !defined(THREAD_NDEBUG)
64 // this debug-setup code isn't interlocked, but it's idempotent
65 // (don't worry, be happy)
66 if (!debugHasInitialized) {
67 loggingMutexi = Debug::debugging("mutex") || Debug::debugging("mutex-c");
68 debugHasInitialized = true;
69 }
70 debugLog = log && loggingMutexi;
71 useCount = contentionCount = 0;
72 #else
73 debugLog = false;
74 #endif //THREAD_NDEBUG
75 check(pthread_mutex_init(&me, NULL));
76 }
77
78 Mutex::~Mutex()
79 {
80 #if !defined(THREAD_NDEBUG)
81 if (debugLog) {
82 if (contentionCount > 0)
83 secdebug("mutex-c", "%p destroyed after %ld/%ld locks/contentions",
84 this, useCount, contentionCount);
85 else if (useCount > 100)
86 secdebug("mutex", "%p destroyed after %ld locks", this, useCount);
87 }
88 #endif //THREAD_NDEBUG
89 check(pthread_mutex_destroy(&me));
90 }
91
92 void Mutex::lock()
93 {
94 #if !defined(THREAD_NDEBUG)
95 useCount++;
96 if (debugLog) {
97 switch (int err = pthread_mutex_trylock(&me)) {
98 case 0:
99 break;
100 case EBUSY:
101 if (debugLog)
102 secdebug("mutex-c", "%p contended (%ld of %ld)", this, ++contentionCount, useCount);
103 check(pthread_mutex_lock(&me));
104 break;
105 default:
106 UnixError::throwMe(err);
107 }
108 if (useCount % 100 == 0)
109 secdebug("mutex", "%p locked %ld", this, useCount);
110 else
111 secdebug("mutex", "%p locked", this);
112 return;
113 }
114 #endif //THREAD_NDEBUG
115 check(pthread_mutex_lock(&me));
116 }
117
118 bool Mutex::tryLock()
119 {
120 useCount++;
121 if (int err = pthread_mutex_trylock(&me)) {
122 if (err != EBUSY)
123 UnixError::throwMe(err);
124 #if !defined(THREAD_NDEBUG)
125 if (debugLog)
126 secdebug("mutex-c", "%p trylock contended (%ld of %ld)",
127 this, ++contentionCount, useCount);
128 #endif //THREAD_NDEBUG
129 return false;
130 }
131 #if !defined(THREAD_NDEBUG)
132 if (debugLog)
133 if (useCount % 100 == 0)
134 secdebug("mutex", "%p locked %ld", this, useCount);
135 else
136 secdebug("mutex", "%p locked", this);
137 #endif //THREAD_NDEBUG
138 return true;
139 }
140
141 void Mutex::unlock()
142 {
143 #if !defined(MUTEX_NDEBUG)
144 if (debugLog)
145 secdebug("mutex", "%p unlocked", this);
146 #endif //MUTEX_NDEBUG
147 check(pthread_mutex_unlock(&me));
148 }
149
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 //
160 void CountingMutex::enter()
161 {
162 lock();
163 mCount++;
164 secdebug("mutex", "%p up to %d", this, mCount);
165 unlock();
166 }
167
168 bool CountingMutex::tryEnter()
169 {
170 if (!tryLock())
171 return false;
172 mCount++;
173 secdebug("mutex", "%p up to %d (was try)", this, mCount);
174 unlock();
175 return true;
176 }
177
178 void CountingMutex::exit()
179 {
180 lock();
181 assert(mCount > 0);
182 mCount--;
183 secdebug("mutex", "%p down to %d", this, mCount);
184 unlock();
185 }
186
187 void CountingMutex::finishEnter()
188 {
189 mCount++;
190 secdebug("mutex", "%p finish up to %d", this, mCount);
191 unlock();
192 }
193
194 void CountingMutex::finishExit()
195 {
196 assert(mCount > 0);
197 mCount--;
198 secdebug("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
209 Thread::~Thread()
210 {
211 }
212
213 void Thread::run()
214 {
215 if (int err = pthread_create(&self.mIdent, NULL, runner, this))
216 UnixError::throwMe(err);
217 secdebug("thread", "%p created", self.mIdent);
218 }
219
220 void *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 secdebug("thread", "%p starting", me->self.mIdent);
226 me->action();
227 secdebug("thread", "%p terminating", me->self.mIdent);
228 delete me;
229 return NULL;
230 }
231
232 void Thread::yield()
233 {
234 sched_yield();
235 }
236
237
238 //
239 // Make a more-or-less unique string representation of a thread id.
240 // This is meant FOR DEBUGGING ONLY. Don't use this in production code.
241 //
242 void Thread::Identity::getIdString(char id[idLength])
243 {
244 pthread_t current = pthread_self();
245 // We're not supposed to know what a pthread_t is. Just print the first few bytes...
246 // (On MacOS X, it's a pointer to a pthread_t internal structure, so this works fine.)
247 long ids;
248 memcpy(&ids, &current, sizeof(ids));
249 snprintf(id, idLength, "%lx", ids);
250 }
251
252
253 #endif // PTHREADS
254
255
256 //
257 // ThreadRunner implementation
258 //
259 ThreadRunner::ThreadRunner(Action *todo)
260 {
261 mAction = todo;
262 run();
263 }
264
265 void 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 //
277 NestingMutex::NestingMutex() : mCount(0)
278 { }
279
280 void NestingMutex::lock()
281 {
282 while (!tryLock()) {
283 mWait.lock();
284 mWait.unlock();
285 }
286 }
287
288 bool 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
304 void 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 }