]>
Commit | Line | Data |
---|---|---|
0959b6d4 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #include <pthread.h> | |
26 | ||
27 | #include "dyldLock.h" | |
28 | ||
29 | // until the reader/writer locks are fully tested, we just use a simple recursive mutex | |
30 | #define REAL_READER_WRITER_LOCK 0 | |
31 | ||
32 | ||
33 | ||
34 | // | |
35 | // This class implements a recursive reader/writer lock. | |
36 | // Recursive means a thread that has already aquired the lock can re-acquire it. | |
37 | // The lock allows either: | |
38 | // a) one "writer" thread to hold lock | |
39 | // b) multiple "reader" threads to hold | |
40 | // | |
41 | // A thread with the writer can acquire a reader lock. | |
42 | // A thread with a reader lock can acquire a writer lock iff it is the only thread with a reader lock | |
43 | // | |
44 | class RecursiveReaderWriterLock | |
45 | { | |
46 | public: | |
47 | RecursiveReaderWriterLock() __attribute__((visibility("hidden"))); | |
48 | void initIfNeeded(); | |
49 | ||
50 | ||
51 | void lockForSingleWritingThread() __attribute__((visibility("hidden"))); | |
52 | void unlockForSingleWritingThread() __attribute__((visibility("hidden"))); | |
53 | ||
54 | void lockForMultipleReadingThreads() __attribute__((visibility("hidden"))); | |
55 | void unlockForMultipleReadingThreads() __attribute__((visibility("hidden"))); | |
56 | ||
57 | private: | |
58 | #if REAL_READER_WRITER_LOCK | |
59 | struct ThreadRecursionCount { | |
60 | pthread_t fThread; | |
61 | uint32_t fCount; | |
62 | }; | |
63 | bool writerThreadIsAnyThreadBut(pthread_t thread); | |
64 | void writerThreadRetain(pthread_t thread); | |
65 | bool writerThreadRelease(pthread_t thread); | |
66 | ||
67 | enum { kMaxReaderThreads = 4 }; | |
68 | bool readerThreadSetRetain(pthread_t thread); | |
69 | bool readerThreadSetRelease(pthread_t thread); | |
70 | bool readerThreadSetContainsAnotherThread(pthread_t thread); | |
71 | ||
72 | ThreadRecursionCount fWriterThread; | |
73 | ThreadRecursionCount fReaderThreads[kMaxReaderThreads]; | |
74 | pthread_cond_t fLockFree; | |
75 | pthread_mutex_t fMutex; | |
76 | #else | |
77 | pthread_mutex_t fMutex; | |
78 | #endif | |
79 | bool fInitialized; // assumes this is statically initialized to false because sLock is static | |
80 | }; | |
81 | ||
82 | ||
83 | // | |
84 | // initIfNeeded() is a hack so that when libSystem_debug.dylb is useable. | |
85 | // The problem is that Objective-C +load methods are called before C++ initialziers are run | |
86 | // If +load method makes a call into dyld, sLock is not initialized. | |
87 | // | |
88 | // The long term solution is for objc and dyld to work more closely together so that instead | |
89 | // of running all +load methods before all initializers, we run each image's +load then its | |
90 | // initializers all in bottom up order. | |
91 | // | |
92 | // This lazy initialization is not thread safe, but as long as someone does not create a | |
93 | // new thread in a +load method, the C++ constructor for sLock will be called before main() | |
94 | // so there will only be one thead. | |
95 | // | |
96 | void RecursiveReaderWriterLock::initIfNeeded() | |
97 | { | |
98 | if ( ! fInitialized ) { | |
99 | pthread_mutexattr_t recursiveMutexAttr; | |
100 | pthread_mutexattr_init(&recursiveMutexAttr); | |
101 | pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE); | |
102 | pthread_mutex_init(&fMutex, &recursiveMutexAttr); | |
103 | #if REAL_READER_WRITER_LOCK | |
104 | pthread_cond_init(&fLockFree, NULL); | |
105 | fWriterThread.fThread = NULL; | |
106 | fWriterThread.fCount = 0; | |
107 | for (int i=0; i < kMaxReaderThreads; ++i) { | |
108 | fReaderThreads[i].fThread = NULL; | |
109 | fReaderThreads[i].fCount = 0; | |
110 | } | |
111 | #endif | |
112 | fInitialized = true; | |
113 | } | |
114 | } | |
115 | ||
116 | RecursiveReaderWriterLock::RecursiveReaderWriterLock() | |
117 | { | |
118 | initIfNeeded(); | |
119 | } | |
120 | ||
121 | void RecursiveReaderWriterLock::lockForSingleWritingThread() | |
122 | { | |
123 | this->initIfNeeded(); | |
124 | #if REAL_READER_WRITER_LOCK | |
125 | pthread_mutex_lock(&fMutex); | |
126 | pthread_t thisThread = pthread_self(); | |
127 | // wait as long as there is another writer or any readers on a different thread | |
128 | while ( writerThreadIsAnyThreadBut(thisThread) || readerThreadSetContainsAnotherThread(thisThread) ) { | |
129 | pthread_cond_wait(&fLockFree, &fMutex); | |
130 | } | |
131 | writerThreadRetain(thisThread); | |
132 | pthread_mutex_unlock(&fMutex); | |
133 | #else | |
134 | pthread_mutex_lock(&fMutex); | |
135 | #endif | |
136 | } | |
137 | ||
138 | void RecursiveReaderWriterLock::unlockForSingleWritingThread() | |
139 | { | |
140 | this->initIfNeeded(); | |
141 | #if REAL_READER_WRITER_LOCK | |
142 | pthread_mutex_lock(&fMutex); | |
143 | if ( writerThreadRelease(pthread_self()) ) { | |
144 | pthread_cond_broadcast(&fLockFree); | |
145 | } | |
146 | pthread_mutex_unlock(&fMutex); | |
147 | #else | |
148 | pthread_mutex_unlock(&fMutex); | |
149 | #endif | |
150 | } | |
151 | ||
152 | ||
153 | void RecursiveReaderWriterLock::lockForMultipleReadingThreads() | |
154 | { | |
155 | this->initIfNeeded(); | |
156 | #if REAL_READER_WRITER_LOCK | |
157 | pthread_mutex_lock(&fMutex); | |
158 | pthread_t thisThread = pthread_self(); | |
159 | // wait as long as there is a writer on another thread or too many readers already | |
160 | while ( writerThreadIsAnyThreadBut(thisThread) || !readerThreadSetRetain(thisThread) ) { | |
161 | pthread_cond_wait(&fLockFree, &fMutex); | |
162 | } | |
163 | pthread_mutex_unlock(&fMutex); | |
164 | #else | |
165 | pthread_mutex_lock(&fMutex); | |
166 | #endif | |
167 | } | |
168 | ||
169 | ||
170 | void RecursiveReaderWriterLock::unlockForMultipleReadingThreads() | |
171 | { | |
172 | this->initIfNeeded(); | |
173 | #if REAL_READER_WRITER_LOCK | |
174 | pthread_mutex_lock(&fMutex); | |
175 | if ( readerThreadSetRelease(pthread_self()) ) { | |
176 | pthread_cond_broadcast(&fLockFree); | |
177 | } | |
178 | pthread_mutex_unlock(&fMutex); | |
179 | #else | |
180 | pthread_mutex_unlock(&fMutex); | |
181 | #endif | |
182 | } | |
183 | ||
184 | #if REAL_READER_WRITER_LOCK | |
185 | bool RecursiveReaderWriterLock::writerThreadIsAnyThreadBut(pthread_t thread) | |
186 | { | |
187 | return ( (fWriterThread.fThread != NULL) && (fWriterThread.fThread != thread) ); | |
188 | } | |
189 | ||
190 | void RecursiveReaderWriterLock::writerThreadRetain(pthread_t thread) | |
191 | { | |
192 | ++fWriterThread.fCount; | |
193 | } | |
194 | ||
195 | bool RecursiveReaderWriterLock::writerThreadRelease(pthread_t thread) | |
196 | { | |
197 | return ( --fWriterThread.fCount == 0 ); | |
198 | } | |
199 | ||
200 | ||
201 | bool RecursiveReaderWriterLock::readerThreadSetRetain(pthread_t thread) | |
202 | { | |
203 | // if thread is already in set, bump its count | |
204 | for (int i=0; i < kMaxReaderThreads; ++i) { | |
205 | if ( fReaderThreads[i].fThread == thread ) { | |
206 | ++fReaderThreads[i].fCount; | |
207 | return true; | |
208 | } | |
209 | } | |
210 | // find empty slot in set | |
211 | for (int i=0; i < kMaxReaderThreads; ++i) { | |
212 | if ( fReaderThreads[i].fThread == NULL ) { | |
213 | fReaderThreads[i].fThread = thread; | |
214 | fReaderThreads[i].fCount = 1; | |
215 | return true; | |
216 | } | |
217 | } | |
218 | ||
219 | // all reader slots full | |
220 | return false; | |
221 | } | |
222 | ||
223 | bool RecursiveReaderWriterLock::readerThreadSetRelease(pthread_t thread) | |
224 | { | |
225 | for (int i=0; i < kMaxReaderThreads; ++i) { | |
226 | if ( fReaderThreads[i].fThread == thread ) { | |
227 | if ( --fReaderThreads[i].fCount == 0 ) { | |
228 | fReaderThreads[i].fThread = NULL; | |
229 | return true; | |
230 | } | |
231 | return false; | |
232 | } | |
233 | } | |
234 | // should never get here | |
235 | return false; | |
236 | } | |
237 | ||
238 | bool RecursiveReaderWriterLock::readerThreadSetContainsAnotherThread(pthread_t thread) | |
239 | { | |
240 | for (int i=0; i < kMaxReaderThreads; ++i) { | |
241 | if ( (fReaderThreads[i].fThread != NULL) && (fReaderThreads[i].fThread != thread) ) | |
242 | return true; | |
243 | } | |
244 | return false; | |
245 | } | |
246 | #endif | |
247 | ||
248 | ||
249 | // dyld's global reader/writer lock | |
250 | static RecursiveReaderWriterLock sLock; | |
251 | ||
252 | ||
253 | LockReaderHelper::LockReaderHelper() | |
254 | { | |
255 | sLock.lockForMultipleReadingThreads(); | |
256 | } | |
257 | ||
258 | LockReaderHelper::~LockReaderHelper() | |
259 | { | |
260 | sLock.unlockForMultipleReadingThreads(); | |
261 | } | |
262 | ||
263 | ||
264 | LockWriterHelper::LockWriterHelper() | |
265 | { | |
266 | sLock.lockForSingleWritingThread(); | |
267 | } | |
268 | ||
269 | LockWriterHelper::~LockWriterHelper() | |
270 | { | |
271 | sLock.unlockForSingleWritingThread(); | |
272 | } | |
273 | ||
274 | ||
275 | // needed by lazy binding | |
276 | void lockForLazyBinding() | |
277 | { | |
278 | sLock.lockForMultipleReadingThreads(); | |
279 | } | |
280 | ||
281 | void unlockForLazyBinding() | |
282 | { | |
283 | sLock.unlockForMultipleReadingThreads(); | |
284 | } | |
285 | ||
286 | ||
287 |