2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
28 // Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
35 #include <AssertMacros.h>
37 #include "objc-sync.h"
40 // Code by Nick Kledzik
43 // revised comments by Blaine
46 // Allocate a lock only when needed. Since few locks are needed at any point
47 // in time, keep them on a single list.
50 static pthread_mutexattr_t sRecursiveLockAttr;
51 static bool sRecursiveLockAttrIntialized = false;
53 static pthread_mutexattr_t* recursiveAttributes()
55 if ( !sRecursiveLockAttrIntialized ) {
56 int err = pthread_mutexattr_init(&sRecursiveLockAttr);
57 require_noerr_string(err, done, "pthread_mutexattr_init failed");
59 err = pthread_mutexattr_settype(&sRecursiveLockAttr, PTHREAD_MUTEX_RECURSIVE);
60 require_noerr_string(err, done, "pthread_mutexattr_settype failed");
62 sRecursiveLockAttrIntialized = true;
66 return &sRecursiveLockAttr;
72 struct SyncData* nextData; // only accessed while holding sTableLock
73 id object; // only accessed while holding sTableLock
74 unsigned int lockCount; // only accessed while holding sTableLock
75 pthread_mutex_t mutex;
76 pthread_cond_t conditionVariable;
78 typedef struct SyncData SyncData;
81 static pthread_mutex_t sTableLock = PTHREAD_MUTEX_INITIALIZER;
82 static SyncData* sDataList = NULL;
84 enum usage { ACQUIRE, RELEASE, CHECK };
86 static SyncData* id2data(id object, enum usage why)
88 SyncData* result = NULL;
91 pthread_mutex_lock(&sTableLock);
93 // Walk in-use list looking for matching object
94 // sTableLock keeps other threads from winning an allocation race
95 // for the same new object.
96 // We could keep the nodes in some hash table if we find that there are
97 // more than 20 or so distinct locks active, but we don't do that now.
99 SyncData* firstUnused = NULL;
101 for (p = sDataList; p != NULL; p = p->nextData) {
102 if ( p->object == object ) {
106 if ( (firstUnused == NULL) && (p->object == NULL) )
110 // no SyncData currently associated with object
111 if ( (why == RELEASE) || (why == CHECK) )
114 // an unused one was found, use it
115 if ( firstUnused != NULL ) {
116 result = firstUnused;
117 result->object = object;
118 result->lockCount = 0; // sanity
122 // malloc a new SyncData and add to list.
123 // XXX calling malloc with a global lock held is bad practice,
124 // might be worth releasing the lock, mallocing, and searching again.
125 // But since we never free these guys we won't be stuck in malloc very often.
126 result = (SyncData*)malloc(sizeof(SyncData));
127 result->object = object;
128 result->lockCount = 0;
129 err = pthread_mutex_init(&result->mutex, recursiveAttributes());
130 require_noerr_string(err, done, "pthread_mutex_init failed");
131 err = pthread_cond_init(&result->conditionVariable, NULL);
132 require_noerr_string(err, done, "pthread_cond_init failed");
133 result->nextData = sDataList;
137 if ( result != NULL ) {
144 if ( result->lockCount == 0 )
145 result->object = NULL; // now recycled
152 pthread_mutex_unlock(&sTableLock);
158 // Begin synchronizing on 'obj'.
159 // Allocates recursive pthread_mutex associated with 'obj' if needed.
160 // Returns OBJC_SYNC_SUCCESS once lock is acquired.
161 int objc_sync_enter(id obj)
163 int result = OBJC_SYNC_SUCCESS;
165 SyncData* data = id2data(obj, ACQUIRE);
166 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_INITIALIZED, "id2data failed");
168 result = pthread_mutex_lock(&data->mutex);
169 require_noerr_string(result, done, "pthread_mutex_lock failed");
176 // End synchronizing on 'obj'.
177 // Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
178 int objc_sync_exit(id obj)
180 int result = OBJC_SYNC_SUCCESS;
182 SyncData* data = id2data(obj, RELEASE);
183 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
185 result = pthread_mutex_unlock(&data->mutex);
186 require_noerr_string(result, done, "pthread_mutex_unlock failed");
189 if ( result == EPERM )
190 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
196 // Temporarily release lock on 'obj' and wait for another thread to notify on 'obj'
197 // Return OBJC_SYNC_SUCCESS, OBJC_SYNC_NOT_OWNING_THREAD_ERROR, OBJC_SYNC_TIMED_OUT, OBJC_SYNC_INTERRUPTED
198 int objc_sync_wait(id obj, long long milliSecondsMaxWait)
200 int result = OBJC_SYNC_SUCCESS;
202 SyncData* data = id2data(obj, CHECK);
203 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
206 // XXX need to retry cond_wait under out-of-our-control failures
207 if ( milliSecondsMaxWait == 0 ) {
208 result = pthread_cond_wait(&data->conditionVariable, &data->mutex);
209 require_noerr_string(result, done, "pthread_cond_wait failed");
212 struct timespec maxWait;
213 maxWait.tv_sec = milliSecondsMaxWait / 1000;
214 maxWait.tv_nsec = (milliSecondsMaxWait - (maxWait.tv_sec * 1000)) * 1000000;
215 result = pthread_cond_timedwait_relative_np(&data->conditionVariable, &data->mutex, &maxWait);
216 require_noerr_string(result, done, "pthread_cond_timedwait_relative_np failed");
218 // no-op to keep compiler from complaining about branch to next instruction
222 if ( result == EPERM )
223 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
224 else if ( result == ETIMEDOUT )
225 result = OBJC_SYNC_TIMED_OUT;
231 // Wake up another thread waiting on 'obj'
232 // Return OBJC_SYNC_SUCCESS, OBJC_SYNC_NOT_OWNING_THREAD_ERROR
233 int objc_sync_notify(id obj)
235 int result = OBJC_SYNC_SUCCESS;
237 SyncData* data = id2data(obj, CHECK);
238 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
240 result = pthread_cond_signal(&data->conditionVariable);
241 require_noerr_string(result, done, "pthread_cond_signal failed");
244 if ( result == EPERM )
245 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
251 // Wake up all threads waiting on 'obj'
252 // Return OBJC_SYNC_SUCCESS, OBJC_SYNC_NOT_OWNING_THREAD_ERROR
253 int objc_sync_notifyAll(id obj)
255 int result = OBJC_SYNC_SUCCESS;
257 SyncData* data = id2data(obj, CHECK);
258 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
260 result = pthread_cond_broadcast(&data->conditionVariable);
261 require_noerr_string(result, done, "pthread_cond_broadcast failed");
264 if ( result == EPERM )
265 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;