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