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