dyld-45.1.tar.gz
[apple/dyld.git] / src / dyldLock.cpp
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