1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
29 // until the reader/writer locks are fully tested, we just use a simple recursive mutex
30 #define REAL_READER_WRITER_LOCK 0
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
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
44 class RecursiveReaderWriterLock
47 RecursiveReaderWriterLock() __attribute__((visibility("hidden")));
51 void lockForSingleWritingThread() __attribute__((visibility("hidden")));
52 void unlockForSingleWritingThread() __attribute__((visibility("hidden")));
54 void lockForMultipleReadingThreads() __attribute__((visibility("hidden")));
55 void unlockForMultipleReadingThreads() __attribute__((visibility("hidden")));
58 #if REAL_READER_WRITER_LOCK
59 struct ThreadRecursionCount
{
63 bool writerThreadIsAnyThreadBut(pthread_t thread
);
64 void writerThreadRetain(pthread_t thread
);
65 bool writerThreadRelease(pthread_t thread
);
67 enum { kMaxReaderThreads
= 4 };
68 bool readerThreadSetRetain(pthread_t thread
);
69 bool readerThreadSetRelease(pthread_t thread
);
70 bool readerThreadSetContainsAnotherThread(pthread_t thread
);
72 ThreadRecursionCount fWriterThread
;
73 ThreadRecursionCount fReaderThreads
[kMaxReaderThreads
];
74 pthread_cond_t fLockFree
;
75 pthread_mutex_t fMutex
;
77 pthread_mutex_t fMutex
;
79 bool fInitialized
; // assumes this is statically initialized to false because sLock is static
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.
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.
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.
96 void RecursiveReaderWriterLock::initIfNeeded()
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;
116 RecursiveReaderWriterLock::RecursiveReaderWriterLock()
121 void RecursiveReaderWriterLock::lockForSingleWritingThread()
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
);
131 writerThreadRetain(thisThread
);
132 pthread_mutex_unlock(&fMutex
);
134 pthread_mutex_lock(&fMutex
);
138 void RecursiveReaderWriterLock::unlockForSingleWritingThread()
140 this->initIfNeeded();
141 #if REAL_READER_WRITER_LOCK
142 pthread_mutex_lock(&fMutex
);
143 if ( writerThreadRelease(pthread_self()) ) {
144 pthread_cond_broadcast(&fLockFree
);
146 pthread_mutex_unlock(&fMutex
);
148 pthread_mutex_unlock(&fMutex
);
153 void RecursiveReaderWriterLock::lockForMultipleReadingThreads()
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
);
163 pthread_mutex_unlock(&fMutex
);
165 pthread_mutex_lock(&fMutex
);
170 void RecursiveReaderWriterLock::unlockForMultipleReadingThreads()
172 this->initIfNeeded();
173 #if REAL_READER_WRITER_LOCK
174 pthread_mutex_lock(&fMutex
);
175 if ( readerThreadSetRelease(pthread_self()) ) {
176 pthread_cond_broadcast(&fLockFree
);
178 pthread_mutex_unlock(&fMutex
);
180 pthread_mutex_unlock(&fMutex
);
184 #if REAL_READER_WRITER_LOCK
185 bool RecursiveReaderWriterLock::writerThreadIsAnyThreadBut(pthread_t thread
)
187 return ( (fWriterThread
.fThread
!= NULL
) && (fWriterThread
.fThread
!= thread
) );
190 void RecursiveReaderWriterLock::writerThreadRetain(pthread_t thread
)
192 ++fWriterThread
.fCount
;
195 bool RecursiveReaderWriterLock::writerThreadRelease(pthread_t thread
)
197 return ( --fWriterThread
.fCount
== 0 );
201 bool RecursiveReaderWriterLock::readerThreadSetRetain(pthread_t thread
)
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
;
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;
219 // all reader slots full
223 bool RecursiveReaderWriterLock::readerThreadSetRelease(pthread_t thread
)
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
;
234 // should never get here
238 bool RecursiveReaderWriterLock::readerThreadSetContainsAnotherThread(pthread_t thread
)
240 for (int i
=0; i
< kMaxReaderThreads
; ++i
) {
241 if ( (fReaderThreads
[i
].fThread
!= NULL
) && (fReaderThreads
[i
].fThread
!= thread
) )
249 // dyld's global reader/writer lock
250 static RecursiveReaderWriterLock sLock
;
253 LockReaderHelper::LockReaderHelper()
255 sLock
.lockForMultipleReadingThreads();
258 LockReaderHelper::~LockReaderHelper()
260 sLock
.unlockForMultipleReadingThreads();
264 LockWriterHelper::LockWriterHelper()
266 sLock
.lockForSingleWritingThread();
269 LockWriterHelper::~LockWriterHelper()
271 sLock
.unlockForSingleWritingThread();
275 // needed by lazy binding
276 void lockForLazyBinding()
278 sLock
.lockForMultipleReadingThreads();
281 void unlockForLazyBinding()
283 sLock
.unlockForMultipleReadingThreads();