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.
36 #include <AssertMacros.h>
38 #include "objc-sync.h"
41 // Code by Nick Kledzik
44 // revised comments by Blaine
47 // Allocate a lock only when needed. Since few locks are needed at any point
48 // in time, keep them on a single list.
51 static pthread_mutexattr_t sRecursiveLockAttr;
52 static bool sRecursiveLockAttrIntialized = false;
54 static pthread_mutexattr_t* recursiveAttributes()
56 if ( !sRecursiveLockAttrIntialized ) {
57 int err = pthread_mutexattr_init(&sRecursiveLockAttr);
58 require_noerr_string(err, done, "pthread_mutexattr_init failed");
60 err = pthread_mutexattr_settype(&sRecursiveLockAttr, PTHREAD_MUTEX_RECURSIVE);
61 require_noerr_string(err, done, "pthread_mutexattr_settype failed");
63 sRecursiveLockAttrIntialized = true;
67 return &sRecursiveLockAttr;
73 struct SyncData* nextData; // only accessed while holding sTableLock
74 id object; // only accessed while holding sTableLock
75 unsigned int lockCount; // only accessed while holding sTableLock
76 pthread_mutex_t mutex;
77 pthread_cond_t conditionVariable;
79 typedef struct SyncData SyncData;
82 static pthread_mutex_t sTableLock = PTHREAD_MUTEX_INITIALIZER;
83 static SyncData* sDataList = NULL;
85 enum usage { ACQUIRE, RELEASE, CHECK };
87 static SyncData* id2data(id object, enum usage why)
89 SyncData* result = NULL;
92 pthread_mutex_lock(&sTableLock);
94 // Walk in-use list looking for matching object
95 // sTableLock keeps other threads from winning an allocation race
96 // for the same new object.
97 // We could keep the nodes in some hash table if we find that there are
98 // more than 20 or so distinct locks active, but we don't do that now.
100 SyncData* firstUnused = NULL;
102 for (p = sDataList; p != NULL; p = p->nextData) {
103 if ( p->object == object ) {
107 if ( (firstUnused == NULL) && (p->object == NULL) )
111 // no SyncData currently associated with object
112 if ( (why == RELEASE) || (why == CHECK) )
115 // an unused one was found, use it
116 if ( firstUnused != NULL ) {
117 result = firstUnused;
118 result->object = object;
119 result->lockCount = 0; // sanity
123 // malloc a new SyncData and add to list.
124 // XXX calling malloc with a global lock held is bad practice,
125 // might be worth releasing the lock, mallocing, and searching again.
126 // But since we never free these guys we won't be stuck in malloc very often.
127 result = (SyncData*)malloc(sizeof(SyncData));
128 result->object = object;
129 result->lockCount = 0;
130 err = pthread_mutex_init(&result->mutex, recursiveAttributes());
131 require_noerr_string(err, done, "pthread_mutex_init failed");
132 err = pthread_cond_init(&result->conditionVariable, NULL);
133 require_noerr_string(err, done, "pthread_cond_init failed");
134 result->nextData = sDataList;
138 if ( result != NULL ) {
145 if ( result->lockCount == 0 )
146 result->object = NULL; // now recycled
153 pthread_mutex_unlock(&sTableLock);
159 // Begin synchronizing on 'obj'.
160 // Allocates recursive pthread_mutex associated with 'obj' if needed.
161 // Returns OBJC_SYNC_SUCCESS once lock is acquired.
162 int objc_sync_enter(id obj)
164 int result = OBJC_SYNC_SUCCESS;
167 SyncData* data = id2data(obj, ACQUIRE);
168 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_INITIALIZED, "id2data failed");
170 result = pthread_mutex_lock(&data->mutex);
171 require_noerr_string(result, done, "pthread_mutex_lock failed");
173 // @synchronized(nil) does nothing
181 // End synchronizing on 'obj'.
182 // Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
183 int objc_sync_exit(id obj)
185 int result = OBJC_SYNC_SUCCESS;
188 SyncData* data = id2data(obj, RELEASE);
189 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
191 result = pthread_mutex_unlock(&data->mutex);
192 require_noerr_string(result, done, "pthread_mutex_unlock failed");
194 // @synchronized(nil) does nothing
198 if ( result == EPERM )
199 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
205 // Temporarily release lock on 'obj' and wait for another thread to notify on 'obj'
206 // Return OBJC_SYNC_SUCCESS, OBJC_SYNC_NOT_OWNING_THREAD_ERROR, OBJC_SYNC_TIMED_OUT, OBJC_SYNC_INTERRUPTED
207 int objc_sync_wait(id obj, long long milliSecondsMaxWait)
209 int result = OBJC_SYNC_SUCCESS;
211 SyncData* data = id2data(obj, CHECK);
212 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
215 // XXX need to retry cond_wait under out-of-our-control failures
216 if ( milliSecondsMaxWait == 0 ) {
217 result = pthread_cond_wait(&data->conditionVariable, &data->mutex);
218 require_noerr_string(result, done, "pthread_cond_wait failed");
221 struct timespec maxWait;
222 maxWait.tv_sec = milliSecondsMaxWait / 1000;
223 maxWait.tv_nsec = (milliSecondsMaxWait - (maxWait.tv_sec * 1000)) * 1000000;
224 result = pthread_cond_timedwait_relative_np(&data->conditionVariable, &data->mutex, &maxWait);
225 require_noerr_string(result, done, "pthread_cond_timedwait_relative_np failed");
227 // no-op to keep compiler from complaining about branch to next instruction
231 if ( result == EPERM )
232 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
233 else if ( result == ETIMEDOUT )
234 result = OBJC_SYNC_TIMED_OUT;
240 // Wake up another thread waiting on 'obj'
241 // Return OBJC_SYNC_SUCCESS, OBJC_SYNC_NOT_OWNING_THREAD_ERROR
242 int objc_sync_notify(id obj)
244 int result = OBJC_SYNC_SUCCESS;
246 SyncData* data = id2data(obj, CHECK);
247 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
249 result = pthread_cond_signal(&data->conditionVariable);
250 require_noerr_string(result, done, "pthread_cond_signal failed");
253 if ( result == EPERM )
254 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
260 // Wake up all threads waiting on 'obj'
261 // Return OBJC_SYNC_SUCCESS, OBJC_SYNC_NOT_OWNING_THREAD_ERROR
262 int objc_sync_notifyAll(id obj)
264 int result = OBJC_SYNC_SUCCESS;
266 SyncData* data = id2data(obj, CHECK);
267 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
269 result = pthread_cond_broadcast(&data->conditionVariable);
270 require_noerr_string(result, done, "pthread_cond_broadcast failed");
273 if ( result == EPERM )
274 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;