]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | // | |
26 | // threading - generic thread support | |
27 | // | |
28 | #include <security_utilities/threading.h> | |
29 | #include <security_utilities/globalizer.h> | |
30 | #include <security_utilities/memutils.h> | |
31 | ||
32 | #include <unistd.h> // WWDC 2007 thread-crash workaround | |
33 | #include <syslog.h> // WWDC 2007 thread-crash workaround | |
34 | ||
35 | // | |
36 | // Thread-local storage primitive | |
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 | ||
52 | // | |
53 | // Mutex implementation | |
54 | // | |
55 | struct MutexAttributes { | |
56 | pthread_mutexattr_t recursive; | |
57 | pthread_mutexattr_t checking; | |
58 | ||
59 | MutexAttributes() | |
60 | { | |
61 | pthread_mutexattr_init(&recursive); | |
62 | pthread_mutexattr_settype(&recursive, PTHREAD_MUTEX_RECURSIVE); | |
63 | #if !defined(NDEBUG) | |
64 | pthread_mutexattr_init(&checking); | |
65 | pthread_mutexattr_settype(&checking, PTHREAD_MUTEX_ERRORCHECK); | |
66 | #endif //NDEBUG | |
67 | } | |
68 | }; | |
69 | ||
70 | static ModuleNexus<MutexAttributes> mutexAttrs; | |
71 | ||
72 | ||
73 | Mutex::Mutex() | |
74 | { | |
75 | check(pthread_mutex_init(&me, NULL)); | |
76 | } | |
77 | ||
78 | Mutex::Mutex(Type type) | |
79 | { | |
80 | switch (type) { | |
81 | case normal: | |
82 | check(pthread_mutex_init(&me, IFELSEDEBUG(&mutexAttrs().checking, NULL))); | |
83 | break; | |
84 | case recursive: // requested recursive (is also checking, always) | |
85 | check(pthread_mutex_init(&me, &mutexAttrs().recursive)); | |
86 | break; | |
87 | }; | |
88 | } | |
89 | ||
90 | ||
91 | Mutex::~Mutex() | |
92 | { | |
93 | int result = pthread_mutex_destroy(&me); | |
94 | check(result); | |
95 | } | |
96 | ||
97 | ||
98 | void Mutex::lock() | |
99 | { | |
100 | check(pthread_mutex_lock(&me)); | |
101 | } | |
102 | ||
103 | ||
104 | bool Mutex::tryLock() | |
105 | { | |
106 | if (int err = pthread_mutex_trylock(&me)) { | |
107 | if (err != EBUSY) | |
108 | UnixError::throwMe(err); | |
109 | return false; | |
110 | } | |
111 | ||
112 | return true; | |
113 | } | |
114 | ||
115 | ||
116 | void Mutex::unlock() | |
117 | { | |
118 | int result = pthread_mutex_unlock(&me); | |
119 | check(result); | |
120 | } | |
121 | ||
122 | ||
123 | // | |
124 | // Condition variables | |
125 | // | |
126 | Condition::Condition(Mutex &lock) : mutex(lock) | |
127 | { | |
128 | check(pthread_cond_init(&me, NULL)); | |
129 | } | |
130 | ||
131 | Condition::~Condition() | |
132 | { | |
133 | check(pthread_cond_destroy(&me)); | |
134 | } | |
135 | ||
136 | void Condition::wait() | |
137 | { | |
138 | check(pthread_cond_wait(&me, &mutex.me)); | |
139 | } | |
140 | ||
141 | void Condition::signal() | |
142 | { | |
143 | check(pthread_cond_signal(&me)); | |
144 | } | |
145 | ||
146 | void Condition::broadcast() | |
147 | { | |
148 | check(pthread_cond_broadcast(&me)); | |
149 | } | |
150 | ||
151 | ||
152 | // | |
153 | // CountingMutex implementation. | |
154 | // | |
155 | void CountingMutex::enter() | |
156 | { | |
157 | lock(); | |
158 | mCount++; | |
159 | secdebug("cmutex", "%p up to %d", this, mCount); | |
160 | unlock(); | |
161 | } | |
162 | ||
163 | bool CountingMutex::tryEnter() | |
164 | { | |
165 | if (!tryLock()) | |
166 | return false; | |
167 | mCount++; | |
168 | secdebug("cmutex", "%p up to %d (was try)", this, mCount); | |
169 | unlock(); | |
170 | return true; | |
171 | } | |
172 | ||
173 | void CountingMutex::exit() | |
174 | { | |
175 | lock(); | |
176 | assert(mCount > 0); | |
177 | mCount--; | |
178 | secdebug("cmutex", "%p down to %d", this, mCount); | |
179 | unlock(); | |
180 | } | |
181 | ||
182 | void CountingMutex::finishEnter() | |
183 | { | |
184 | mCount++; | |
185 | secdebug("cmutex", "%p finish up to %d", this, mCount); | |
186 | unlock(); | |
187 | } | |
188 | ||
189 | void CountingMutex::finishExit() | |
190 | { | |
191 | assert(mCount > 0); | |
192 | mCount--; | |
193 | secdebug("cmutex", "%p finish down to %d", this, mCount); | |
194 | unlock(); | |
195 | } | |
196 | ||
197 | ||
198 | ||
199 | // | |
200 | // Threads implementation | |
201 | // | |
202 | Thread::~Thread() | |
203 | { | |
204 | } | |
205 | ||
206 | void Thread::run() | |
207 | { | |
208 | pthread_attr_t ptattrs; | |
209 | int err, ntries = 10; // 10 is arbitrary | |
210 | ||
211 | if ((err = pthread_attr_init(&ptattrs)) || | |
212 | (err = pthread_attr_setdetachstate(&ptattrs, PTHREAD_CREATE_DETACHED))) | |
213 | { | |
214 | syslog(LOG_ERR, "error %d setting thread detach state", err); | |
215 | } | |
427c49bc A |
216 | while ((err = pthread_create(&self.mIdent, &ptattrs, runner, this) && |
217 | --ntries)) | |
b1ab9ed8 A |
218 | { |
219 | syslog(LOG_ERR, "pthread_create() error %d", err); | |
220 | usleep(50000); // 50 ms is arbitrary | |
221 | } | |
222 | if (err) | |
223 | { | |
224 | syslog(LOG_ERR, "too many failed pthread_create() attempts"); | |
225 | } | |
226 | else | |
227 | secdebug("thread", "%p created", self.mIdent); | |
228 | } | |
229 | ||
230 | void *Thread::runner(void *arg) | |
231 | { | |
232 | try // the top level of any running thread of execution must have a try/catch around it, | |
233 | // otherwise it will crash if something underneath throws. | |
234 | { | |
235 | Thread *me = static_cast<Thread *>(arg); | |
236 | secdebug("thread", "%p starting", me->self.mIdent); | |
237 | me->action(); | |
238 | secdebug("thread", "%p terminating", me->self.mIdent); | |
239 | delete me; | |
240 | return NULL; | |
241 | } | |
242 | catch (...) | |
243 | { | |
427c49bc | 244 | return NULL; |
b1ab9ed8 A |
245 | } |
246 | } | |
247 | ||
248 | void Thread::yield() | |
249 | { | |
250 | ::sched_yield(); | |
251 | } | |
252 | ||
253 | ||
254 | // | |
255 | // ThreadRunner implementation | |
256 | // | |
257 | ThreadRunner::ThreadRunner(Action *todo) | |
258 | { | |
259 | mAction = todo; | |
260 | run(); | |
261 | } | |
262 | ||
263 | void ThreadRunner::action() | |
264 | { | |
265 | mAction(); | |
266 | } |