]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-initialize.m
objc4-371.2.tar.gz
[apple/objc4.git] / runtime / objc-initialize.m
1 /*
2 * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /***********************************************************************
25 * objc-initialize.m
26 * +initialize support
27 **********************************************************************/
28
29 /***********************************************************************
30 * Thread-safety during class initialization (GrP 2001-9-24)
31 *
32 * Initial state: CLS_INITIALIZING and CLS_INITIALIZED both clear.
33 * During initialization: CLS_INITIALIZING is set
34 * After initialization: CLS_INITIALIZING clear and CLS_INITIALIZED set.
35 * CLS_INITIALIZING and CLS_INITIALIZED are never set at the same time.
36 * CLS_INITIALIZED is never cleared once set.
37 *
38 * Only one thread is allowed to actually initialize a class and send
39 * +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING.
40 *
41 * Additionally, threads trying to send messages to a class must wait for
42 * +initialize to finish. During initialization of a class, that class's
43 * method cache is kept empty. objc_msgSend will revert to
44 * class_lookupMethodAndLoadCache, which checks CLS_INITIALIZED before
45 * messaging. If CLS_INITIALIZED is clear but CLS_INITIALIZING is set,
46 * the thread must block, unless it is the thread that started
47 * initializing the class in the first place.
48 *
49 * Each thread keeps a list of classes it's initializing.
50 * The global classInitLock is used to synchronize changes to CLS_INITIALIZED
51 * and CLS_INITIALIZING: the transition to CLS_INITIALIZING must be
52 * an atomic test-and-set with respect to itself and the transition
53 * to CLS_INITIALIZED.
54 * The global classInitWaitCond is used to block threads waiting for an
55 * initialization to complete. The classInitLock synchronizes
56 * condition checking and the condition variable.
57 **********************************************************************/
58
59 /***********************************************************************
60 * +initialize deadlock case when a class is marked initializing while
61 * its superclass is initialized. Solved by completely initializing
62 * superclasses before beginning to initialize a class.
63 *
64 * OmniWeb class hierarchy:
65 * OBObject
66 * | ` OBPostLoader
67 * OFObject
68 * / \
69 * OWAddressEntry OWController
70 * |
71 * OWConsoleController
72 *
73 * Thread 1 (evil testing thread):
74 * initialize OWAddressEntry
75 * super init OFObject
76 * super init OBObject
77 * [OBObject initialize] runs OBPostLoader, which inits lots of classes...
78 * initialize OWConsoleController
79 * super init OWController - wait for Thread 2 to finish OWController init
80 *
81 * Thread 2 (normal OmniWeb thread):
82 * initialize OWController
83 * super init OFObject - wait for Thread 1 to finish OFObject init
84 *
85 * deadlock!
86 *
87 * Solution: fully initialize super classes before beginning to initialize
88 * a subclass. Then the initializing+initialized part of the class hierarchy
89 * will be a contiguous subtree starting at the root, so other threads
90 * can't jump into the middle between two initializing classes, and we won't
91 * get stuck while a superclass waits for its subclass which waits for the
92 * superclass.
93 **********************************************************************/
94
95 #include <pthread.h>
96 #include <assert.h>
97
98 #import "objc-private.h"
99 #import "objc-initialize.h"
100
101 /* classInitLock protects classInitWaitCond and examination and modification
102 * of CLS_INITIALIZED and CLS_INITIALIZING. */
103 static OBJC_DECLARE_LOCK(classInitLock);
104
105 /* classInitWaitCond is signalled when any class is done initializing.
106 * Threads that are waiting for a class to finish initializing wait on this. */
107 static pthread_cond_t classInitWaitCond = PTHREAD_COND_INITIALIZER;
108
109
110 /***********************************************************************
111 * struct _objc_initializing_classes
112 * Per-thread list of classes currently being initialized by that thread.
113 * During initialization, that thread is allowed to send messages to that
114 * class, but other threads have to wait.
115 * The list is a simple array of metaclasses (the metaclass stores
116 * the initialization state).
117 **********************************************************************/
118 typedef struct _objc_initializing_classes {
119 int classesAllocated;
120 Class *metaclasses;
121 } _objc_initializing_classes;
122
123
124 /***********************************************************************
125 * _fetchInitializingClassList
126 * Return the list of classes being initialized by this thread.
127 * If create == YES, create the list when no classes are being initialized by this thread.
128 * If create == NO, return NULL when no classes are being initialized by this thread.
129 **********************************************************************/
130 static _objc_initializing_classes *_fetchInitializingClassList(BOOL create)
131 {
132 _objc_pthread_data *data;
133 _objc_initializing_classes *list;
134 Class *classes;
135
136 data = _objc_fetch_pthread_data(create);
137 if (data == NULL && !create) return NULL;
138
139 list = data->initializingClasses;
140 if (list == NULL) {
141 if (!create) {
142 return NULL;
143 } else {
144 list = _calloc_internal(1, sizeof(_objc_initializing_classes));
145 data->initializingClasses = list;
146 }
147 }
148
149 classes = list->metaclasses;
150 if (classes == NULL) {
151 // If _objc_initializing_classes exists, allocate metaclass array,
152 // even if create == NO.
153 // Allow 4 simultaneous class inits on this thread before realloc.
154 list->classesAllocated = 4;
155 classes = _calloc_internal(list->classesAllocated, sizeof(Class));
156 list->metaclasses = classes;
157 }
158 return list;
159 }
160
161
162 /***********************************************************************
163 * _destroyInitializingClassList
164 * Deallocate memory used by the given initialization list.
165 * Any part of the list may be NULL.
166 * Called from _objc_pthread_destroyspecific().
167 **********************************************************************/
168 __private_extern__
169 void _destroyInitializingClassList(struct _objc_initializing_classes *list)
170 {
171 if (list != NULL) {
172 if (list->metaclasses != NULL) {
173 _free_internal(list->metaclasses);
174 }
175 _free_internal(list);
176 }
177 }
178
179
180 /***********************************************************************
181 * _thisThreadIsInitializingClass
182 * Return TRUE if this thread is currently initializing the given class.
183 **********************************************************************/
184 static BOOL _thisThreadIsInitializingClass(Class cls)
185 {
186 int i;
187
188 _objc_initializing_classes *list = _fetchInitializingClassList(NO);
189 if (list) {
190 cls = _class_getMeta(cls);
191 for (i = 0; i < list->classesAllocated; i++) {
192 if (cls == list->metaclasses[i]) return YES;
193 }
194 }
195
196 // no list or not found in list
197 return NO;
198 }
199
200
201 /***********************************************************************
202 * _setThisThreadIsInitializingClass
203 * Record that this thread is currently initializing the given class.
204 * This thread will be allowed to send messages to the class, but
205 * other threads will have to wait.
206 **********************************************************************/
207 static void _setThisThreadIsInitializingClass(Class cls)
208 {
209 int i;
210 _objc_initializing_classes *list = _fetchInitializingClassList(YES);
211 cls = _class_getMeta(cls);
212
213 // paranoia: explicitly disallow duplicates
214 for (i = 0; i < list->classesAllocated; i++) {
215 if (cls == list->metaclasses[i]) {
216 _objc_fatal("thread is already initializing this class!");
217 return; // already the initializer
218 }
219 }
220
221 for (i = 0; i < list->classesAllocated; i++) {
222 if (0 == list->metaclasses[i]) {
223 list->metaclasses[i] = cls;
224 return;
225 }
226 }
227
228 // class list is full - reallocate
229 list->classesAllocated = list->classesAllocated * 2 + 1;
230 list->metaclasses = _realloc_internal(list->metaclasses, list->classesAllocated * sizeof(Class));
231 // zero out the new entries
232 list->metaclasses[i++] = cls;
233 for ( ; i < list->classesAllocated; i++) {
234 list->metaclasses[i] = NULL;
235 }
236 }
237
238
239 /***********************************************************************
240 * _setThisThreadIsNotInitializingClass
241 * Record that this thread is no longer initializing the given class.
242 **********************************************************************/
243 static void _setThisThreadIsNotInitializingClass(Class cls)
244 {
245 int i;
246
247 _objc_initializing_classes *list = _fetchInitializingClassList(NO);
248 if (list) {
249 cls = _class_getMeta(cls);
250 for (i = 0; i < list->classesAllocated; i++) {
251 if (cls == list->metaclasses[i]) {
252 list->metaclasses[i] = NULL;
253 return;
254 }
255 }
256 }
257
258 // no list or not found in list
259 _objc_fatal("thread is not initializing this class!");
260 }
261
262
263 /***********************************************************************
264 * class_initialize. Send the '+initialize' message on demand to any
265 * uninitialized class. Force initialization of superclasses first.
266 *
267 * Called only from _class_lookupMethodAndLoadCache (or itself).
268 **********************************************************************/
269 __private_extern__ void _class_initialize(Class cls)
270 {
271 Class supercls;
272 BOOL reallyInitialize = NO;
273
274 // Get the real class from the metaclass. The superclass chain
275 // hangs off the real class only.
276 cls = _class_getNonMetaClass(cls);
277
278 // Make sure super is done initializing BEFORE beginning to initialize cls.
279 // See note about deadlock above.
280 supercls = _class_getSuperclass(cls);
281 if (supercls && !_class_isInitialized(supercls)) {
282 _class_initialize(supercls);
283 }
284
285 // Try to atomically set CLS_INITIALIZING.
286 OBJC_LOCK(&classInitLock);
287 if (!_class_isInitialized(cls) && !_class_isInitializing(cls)) {
288 _class_setInitializing(cls);
289 reallyInitialize = YES;
290 }
291 OBJC_UNLOCK(&classInitLock);
292
293 if (reallyInitialize) {
294 // We successfully set the CLS_INITIALIZING bit. Initialize the class.
295
296 // Record that we're initializing this class so we can message it.
297 _setThisThreadIsInitializingClass(cls);
298
299 // Send the +initialize message.
300 // Note that +initialize is sent to the superclass (again) if
301 // this class doesn't implement +initialize. 2157218
302 if (PrintInitializing) {
303 _objc_inform("INITIALIZE: calling +[%s initialize]",
304 _class_getName(cls));
305 }
306 [(id)cls initialize];
307
308 // propagate finalization affinity.
309 if (UseGC && supercls && _class_shouldFinalizeOnMainThread(supercls)) {
310 _class_setFinalizeOnMainThread(cls);
311 }
312
313 // Done initializing. Update the info bits and notify waiting threads.
314 OBJC_LOCK(&classInitLock);
315 _class_setInitialized(cls);
316 pthread_cond_broadcast(&classInitWaitCond);
317 OBJC_UNLOCK(&classInitLock);
318 _setThisThreadIsNotInitializingClass(cls);
319 return;
320 }
321
322 else if (_class_isInitializing(cls)) {
323 // We couldn't set INITIALIZING because INITIALIZING was already set.
324 // If this thread set it earlier, continue normally.
325 // If some other thread set it, block until initialize is done.
326 // It's ok if INITIALIZING changes to INITIALIZED while we're here,
327 // because we safely check for INITIALIZED inside the lock
328 // before blocking.
329 if (_thisThreadIsInitializingClass(cls)) {
330 return;
331 } else {
332 OBJC_LOCK(&classInitLock);
333 while (!_class_isInitialized(cls)) {
334 pthread_cond_wait(&classInitWaitCond, &classInitLock);
335 }
336 OBJC_UNLOCK(&classInitLock);
337 return;
338 }
339 }
340
341 else if (_class_isInitialized(cls)) {
342 // Set CLS_INITIALIZING failed because someone else already
343 // initialized the class. Continue normally.
344 // NOTE this check must come AFTER the ISINITIALIZING case.
345 // Otherwise: Another thread is initializing this class. ISINITIALIZED
346 // is false. Skip this clause. Then the other thread finishes
347 // initialization and sets INITIALIZING=no and INITIALIZED=yes.
348 // Skip the ISINITIALIZING clause. Die horribly.
349 return;
350 }
351
352 else {
353 // We shouldn't be here.
354 _objc_fatal("thread-safe class init in objc runtime is buggy!");
355 }
356 }