Fix a lock-order reversal where one thread calls some dtable-manipulating

function while another is initializing.
main
theraven 13 years ago
parent 65e3dc736c
commit ca25388b37

@ -680,6 +680,12 @@ PRIVATE void objc_send_initialize(id object)
return; return;
} }
// Lock the runtime while we're creating dtables and before we acquire any
// other locks. This prevents a lock-order reversal when
// dtable_for_class is called from something holding the runtime lock while
// we're still holding the initialize lock. We should ensure that we never
// acquire the runtime lock after acquiring the initialize lock.
LOCK_RUNTIME();
LOCK_OBJECT_FOR_SCOPE((id)meta); LOCK_OBJECT_FOR_SCOPE((id)meta);
LOCK(&initialize_lock); LOCK(&initialize_lock);
if (objc_test_class_flag(class, objc_class_flag_initialized)) if (objc_test_class_flag(class, objc_class_flag_initialized))
@ -696,6 +702,14 @@ PRIVATE void objc_send_initialize(id object)
dtable_t class_dtable = create_dtable_for_class(class, uninstalled_dtable); dtable_t class_dtable = create_dtable_for_class(class, uninstalled_dtable);
dtable_t dtable = skipMeta ? 0 : create_dtable_for_class(meta, class_dtable); dtable_t dtable = skipMeta ? 0 : create_dtable_for_class(meta, class_dtable);
// Now we've finished doing things that may acquire the runtime lock, so we
// can hold onto the initialise lock to make anything doing
// dtable_for_class block until we've finished updating temporary dtable
// lists.
// If another thread holds the runtime lock, it can now proceed until it
// gets into a dtable_for_class call, and then block there waiting for us
// to finish setting up the temporary dtable.
UNLOCK_RUNTIME();
static SEL initializeSel = 0; static SEL initializeSel = 0;
if (0 == initializeSel) if (0 == initializeSel)
@ -730,7 +744,12 @@ PRIVATE void objc_send_initialize(id object)
__attribute__((cleanup(remove_dtable))) __attribute__((cleanup(remove_dtable)))
InitializingDtable meta_buffer = { meta, dtable, &buffer }; InitializingDtable meta_buffer = { meta, dtable, &buffer };
temporary_dtables = &meta_buffer; temporary_dtables = &meta_buffer;
// We now release the initialize lock. We'll reacquire it later when we do
// the cleanup, but at this point we allow other threads to get the
// temporary dtable and call +initialize in other threads.
UNLOCK(&initialize_lock); UNLOCK(&initialize_lock);
// We still hold the class lock at this point. dtable_for_class will block
// there after acquiring the temporary dtable.
checkARCAccessors(class); checkARCAccessors(class);

Loading…
Cancel
Save