// Detach current loadable list. structloadable_class *classes = loadable_classes; int used = loadable_classes_used; loadable_classes = nil; loadable_classes_allocated = 0; loadable_classes_used = 0;
// Call all +loads for the detached list. for (i = 0; i < used; i++) { Class cls = classes[i].cls; load_method_t load_method = (load_method_t)classes[i].method; if (!cls) continue;
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver) { ...
if (initialize && !cls->isInitialized()) { runtimeLock.unlock(); _class_initialize (_class_getNonMetaClass(cls, inst)); runtimeLock.lock(); // If sel == initialize, _class_initialize will send +initialize and // then the messenger will send +initialize again after this // procedure finishes. Of course, if this is not being called // from the messenger then it won't happen. 2778172 }
// Make sure super is done initializing BEFORE beginning to initialize cls. // See note about deadlock above. supercls = cls->superclass; if (supercls && !supercls->isInitialized()) { _class_initialize(supercls); }
// Try to atomically set CLS_INITIALIZING. { monitor_locker_tlock(classInitLock); if (!cls->isInitialized() && !cls->isInitializing()) { cls->setInitializing(); reallyInitialize = YES; } }
if (reallyInitialize) { // We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it. _setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) { // LOL JK we don't really call +initialize methods after fork(). performForkChildInitialize(cls, supercls); return; }
// Send the +initialize message. // Note that +initialize is sent to the superclass (again) if // this class doesn't implement +initialize. 2157218 if (PrintInitializing) { _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]", pthread_self(), cls->nameForLogging()); }
// Exceptions: A +initialize call that throws an exception // is deemed to be a complete and successful +initialize. // // Only __OBJC2__ adds these handlers. !__OBJC2__ has a // bootstrapping problem of this versus CF's call to // objc_exception_set_functions(). #if __OBJC2__ @try #endif { callInitialize(cls);
elseif (cls->isInitializing()) { // We couldn't set INITIALIZING because INITIALIZING was already set. // If this thread set it earlier, continue normally. // If some other thread set it, block until initialize is done. // It's ok if INITIALIZING changes to INITIALIZED while we're here, // because we safely check for INITIALIZED inside the lock // before blocking. if (_thisThreadIsInitializingClass(cls)) { return; } elseif (!MultithreadedForkChild) { waitForInitializeToComplete(cls); return; } else { // We're on the child side of fork(), facing a class that // was initializing by some other thread when fork() was called. _setThisThreadIsInitializingClass(cls); performForkChildInitialize(cls, supercls); } }
elseif (cls->isInitialized()) { // Set CLS_INITIALIZING failed because someone else already // initialized the class. Continue normally. // NOTE this check must come AFTER the ISINITIALIZING case. // Otherwise: Another thread is initializing this class. ISINITIALIZED // is false. Skip this clause. Then the other thread finishes // initialization and sets INITIALIZING=no and INITIALIZED=yes. // Skip the ISINITIALIZING clause. Die horribly. return; }
else { // We shouldn't be here. _objc_fatal("thread-safe class init in objc runtime is buggy!"); } }
staticvoid _finishInitializingAfter(Class cls, Class supercls) { PendingInitialize *pending;
classInitLock.assertLocked();
if (PrintInitializing) { _objc_inform("INITIALIZE: thread %p: class %s will be marked as fully " "+initialized after superclass +[%s initialize] completes", pthread_self(), cls->nameForLogging(), supercls->nameForLogging()); }
if (!pendingInitializeMap) { pendingInitializeMap = NXCreateMapTable(NXPtrValueMapPrototype, 10); // fixme pre-size this table for CF/NSObject +initialize }
if (PrintInitializing) { _objc_inform("INITIALIZE: thread %p: %s is fully +initialized", pthread_self(), cls->nameForLogging()); }
// mark this class as fully +initialized cls->setInitialized(); classInitLock.notifyAll(); _setThisThreadIsNotInitializingClass(cls);
// mark any subclasses that were merely waiting for this class if (!pendingInitializeMap) return; pending = (PendingInitialize *)NXMapGet(pendingInitializeMap, cls); if (!pending) return;
NXMapRemove(pendingInitializeMap, cls);
// Destroy the pending table if it's now empty, to save memory. if (NXCountMapTable(pendingInitializeMap) == 0) { NXFreeMapTable(pendingInitializeMap); pendingInitializeMap = nil; }
while (pending) { PendingInitialize *next = pending->next; if (pending->subclass) _finishInitializing(pending->subclass, cls); free(pending); pending = next; } }