]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-sync.m
objc4-274.tar.gz
[apple/objc4.git] / runtime / objc-sync.m
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 //
26 // objc_sync.m
27 //
28 // Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
29 //
30
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <sys/time.h>
34 #include <pthread.h>
35 #include <errno.h>
36 #include <AssertMacros.h>
37
38 #include "objc-sync.h"
39
40 //
41 // Code by Nick Kledzik
42 //
43
44 // revised comments by Blaine
45
46 //
47 // Allocate a lock only when needed. Since few locks are needed at any point
48 // in time, keep them on a single list.
49 //
50
51 static pthread_mutexattr_t sRecursiveLockAttr;
52 static bool sRecursiveLockAttrIntialized = false;
53
54 static pthread_mutexattr_t* recursiveAttributes()
55 {
56 if ( !sRecursiveLockAttrIntialized ) {
57 int err = pthread_mutexattr_init(&sRecursiveLockAttr);
58 require_noerr_string(err, done, "pthread_mutexattr_init failed");
59
60 err = pthread_mutexattr_settype(&sRecursiveLockAttr, PTHREAD_MUTEX_RECURSIVE);
61 require_noerr_string(err, done, "pthread_mutexattr_settype failed");
62
63 sRecursiveLockAttrIntialized = true;
64 }
65
66 done:
67 return &sRecursiveLockAttr;
68 }
69
70
71 struct SyncData
72 {
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;
78 };
79 typedef struct SyncData SyncData;
80
81
82 static pthread_mutex_t sTableLock = PTHREAD_MUTEX_INITIALIZER;
83 static SyncData* sDataList = NULL;
84
85 enum usage { ACQUIRE, RELEASE, CHECK };
86
87 static SyncData* id2data(id object, enum usage why)
88 {
89 SyncData* result = NULL;
90 int err;
91
92 pthread_mutex_lock(&sTableLock);
93
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.
99
100 SyncData* firstUnused = NULL;
101 SyncData* p;
102 for (p = sDataList; p != NULL; p = p->nextData) {
103 if ( p->object == object ) {
104 result = p;
105 goto done;
106 }
107 if ( (firstUnused == NULL) && (p->object == NULL) )
108 firstUnused = p;
109 }
110
111 // no SyncData currently associated with object
112 if ( (why == RELEASE) || (why == CHECK) )
113 goto done;
114
115 // an unused one was found, use it
116 if ( firstUnused != NULL ) {
117 result = firstUnused;
118 result->object = object;
119 result->lockCount = 0; // sanity
120 goto done;
121 }
122
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;
135 sDataList = result;
136
137 done:
138 if ( result != NULL ) {
139 switch ( why ) {
140 case ACQUIRE:
141 result->lockCount++;
142 break;
143 case RELEASE:
144 result->lockCount--;
145 if ( result->lockCount == 0 )
146 result->object = NULL; // now recycled
147 break;
148 case CHECK:
149 // do nothing
150 break;
151 }
152 }
153 pthread_mutex_unlock(&sTableLock);
154 return result;
155 }
156
157
158
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)
163 {
164 int result = OBJC_SYNC_SUCCESS;
165
166 if (obj) {
167 SyncData* data = id2data(obj, ACQUIRE);
168 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_INITIALIZED, "id2data failed");
169
170 result = pthread_mutex_lock(&data->mutex);
171 require_noerr_string(result, done, "pthread_mutex_lock failed");
172 } else {
173 // @synchronized(nil) does nothing
174 }
175
176 done:
177 return result;
178 }
179
180
181 // End synchronizing on 'obj'.
182 // Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
183 int objc_sync_exit(id obj)
184 {
185 int result = OBJC_SYNC_SUCCESS;
186
187 if (obj) {
188 SyncData* data = id2data(obj, RELEASE);
189 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
190
191 result = pthread_mutex_unlock(&data->mutex);
192 require_noerr_string(result, done, "pthread_mutex_unlock failed");
193 } else {
194 // @synchronized(nil) does nothing
195 }
196
197 done:
198 if ( result == EPERM )
199 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
200
201 return result;
202 }
203
204
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)
208 {
209 int result = OBJC_SYNC_SUCCESS;
210
211 SyncData* data = id2data(obj, CHECK);
212 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
213
214
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");
219 }
220 else {
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");
226 }
227 // no-op to keep compiler from complaining about branch to next instruction
228 data = NULL;
229
230 done:
231 if ( result == EPERM )
232 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
233 else if ( result == ETIMEDOUT )
234 result = OBJC_SYNC_TIMED_OUT;
235
236 return result;
237 }
238
239
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)
243 {
244 int result = OBJC_SYNC_SUCCESS;
245
246 SyncData* data = id2data(obj, CHECK);
247 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
248
249 result = pthread_cond_signal(&data->conditionVariable);
250 require_noerr_string(result, done, "pthread_cond_signal failed");
251
252 done:
253 if ( result == EPERM )
254 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
255
256 return result;
257 }
258
259
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)
263 {
264 int result = OBJC_SYNC_SUCCESS;
265
266 SyncData* data = id2data(obj, CHECK);
267 require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
268
269 result = pthread_cond_broadcast(&data->conditionVariable);
270 require_noerr_string(result, done, "pthread_cond_broadcast failed");
271
272 done:
273 if ( result == EPERM )
274 result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
275
276 return result;
277 }
278
279
280
281
282
283