From 6b285936bed090d06f3bfc66d0886c12dc088753 Mon Sep 17 00:00:00 2001 From: Niels Grewe Date: Mon, 24 Oct 2016 10:49:02 +0100 Subject: [PATCH] Avoid a deadlock As described in gnustep/libobjc2#20 --- dtable.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/dtable.c b/dtable.c index b537e9c..4e39562 100644 --- a/dtable.c +++ b/dtable.c @@ -824,6 +824,13 @@ PRIVATE void objc_send_initialize(id object) objc_send_initialize((id)class->super_class); } + // 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(); + // Superclass +initialize might possibly send a message to this class, in // which case this method would be called again. See NSObject and // NSAutoreleasePool +initialize interaction in GNUstep. @@ -831,19 +838,17 @@ PRIVATE void objc_send_initialize(id object) { // We know that initialization has started because the flag is set. // Check that it's finished by grabbing the class lock. This will be - // released once the class has been fully initialized + // released once the class has been fully initialized. The runtime + // lock needs to be released first to prevent a deadlock between the + // runtime lock and the class-specific lock. + UNLOCK_RUNTIME(); + objc_sync_enter((id)meta); objc_sync_exit((id)meta); assert(dtable_for_class(class) != uninstalled_dtable); 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(&initialize_lock); if (objc_test_class_flag(class, objc_class_flag_initialized))