merge dtables inplace, don't hold rt lock while initializing

main
Dustin Howett 8 years ago
parent 9459f53ffa
commit 82d4d3fbfd

@ -360,6 +360,11 @@ PRIVATE void objc_update_dtable_for_class(Class cls)
update_dtable(dtable);
}
PRIVATE void objc_update_dtable_for_new_superclass(Class cls, Class newSuper)
{
// This is not required. objc_dtable_lookup looks directly at the class's
// superclass for each lookup, and the cache only stores local slots.
}
PRIVATE void add_method_list_to_class(Class cls,
struct objc_method_list *list)
{
@ -622,6 +627,69 @@ PRIVATE void objc_update_dtable_for_class(Class cls)
checkARCAccessors(cls);
}
static void rebaseDtableRecursive(Class cls, Class owner, Class newSuper)
{
dtable_t parentDtable = dtable_for_class(newSuper);
dtable_t dtable = dtable_for_class(cls);
uint32_t idx = 0;
struct objc_slot *slot;
while ((slot = SparseArrayNext(parentDtable, &idx)))
{
struct objc_slot *existingSlot = SparseArrayLookup(dtable, idx);
if (NULL != existingSlot)
{
if (slot->method == existingSlot->method
|| slot->owner == existingSlot->owner
|| classIsOrInherits(existingSlot->owner, owner))
{
// IMPs should not be changing right now; disregard anything
// that's already owned by the new parent.
// Slots from base or lower are safe from replacement.
continue;
}
// version doesn't need to be updated since we're replacing slots inherited
// directly from the old parent; this does not invalidate them.
}
// propagate changes downward
SparseArrayInsert(dtable, idx, slot);
}
idx = 0;
while ((slot = SparseArrayNext(dtable, &idx)))
{
void *exist = SparseArrayLookup(parentDtable, idx);
// Everything that exists in the parent dtable was merged above,
// and everything implemented at base or below is safe.
if (exist || classIsOrInherits(slot->owner, owner)) { continue; }
SparseArrayInsert(dtable, idx, 0);
}
// merge can make a class ARC-compatible.
checkARCAccessors(cls);
for (struct objc_class *subclass=cls->subclass_list ;
Nil != subclass ; subclass = subclass->sibling_class)
{
// Don't bother updating dtables for subclasses that haven't been
// initialized yet
if (!classHasDtable(subclass)) { continue; }
rebaseDtableRecursive(subclass, owner, newSuper);
}
}
PRIVATE void objc_update_dtable_for_new_superclass(Class cls, Class newSuper)
{
// Only update real dtables
if (!classHasDtable(cls)) { return; }
LOCK_RUNTIME_FOR_SCOPE();
rebaseDtableRecursive(cls, cls, newSuper);
return;
}
PRIVATE void add_method_list_to_class(Class cls,
struct objc_method_list *list)
{

@ -115,6 +115,11 @@ static inline int classHasDtable(struct objc_class *cls)
* modifying a class's method list.
*/
void objc_update_dtable_for_class(Class);
/**
* Updates the dtable for a class and its subclasses. Must be called after
* changing and initializing a class's superclass.
*/
void objc_update_dtable_for_new_superclass(Class, Class);
/**
* Adds a single method list to a class. This is used when loading categories,
* and is faster than completely rebuilding the dtable.

@ -23,6 +23,8 @@ struct objc_slot *objc_get_slot(Class cls, SEL selector);
#define CHECK_ARG(arg) if (0 == arg) { return 0; }
static inline void safe_remove_from_subclass_list(Class cls);
PRIVATE void objc_resolve_class(Class);
void objc_send_initialize(id object);
/**
* Calls C++ destructors in the correct order.
@ -522,9 +524,12 @@ Class class_setSuperclass(Class cls, Class newSuper)
CHECK_ARG(newSuper);
if (Nil == cls) { return Nil; }
LOCK_RUNTIME_FOR_SCOPE();
LOCK_RUNTIME();
if (cls->super_class == newSuper) { return newSuper; }
safe_remove_from_subclass_list(cls);
objc_resolve_class(newSuper);
Class oldSuper = cls->super_class;
cls->super_class = newSuper;
@ -533,32 +538,43 @@ Class class_setSuperclass(Class cls, Class newSuper)
cls->sibling_class = cls->super_class->subclass_list;
cls->super_class->subclass_list = cls;
if (!class_isMetaClass(cls))
if (UNLIKELY(class_isMetaClass(cls)))
{
// Update the metaclass's superclass.
class_setSuperclass(cls->isa, newSuper->isa);
// newSuper is presumably a metaclass. Its isa will therefore be the appropriate root metaclass.
cls->isa = newSuper->isa;
}
else
{
// newSuper is presumably a metaclass. Its isa will therefore be the appropriate root metaclass.
cls->isa = newSuper->isa;
Class meta = cls->isa, newSuperMeta = newSuper->isa;
// Update the metaclass's superclass.
safe_remove_from_subclass_list(meta);
objc_resolve_class(newSuperMeta);
meta->super_class = newSuperMeta;
meta->isa = newSuperMeta->isa;
// The super class's subclass list is used in certain method resolution scenarios.
meta->sibling_class = newSuperMeta->subclass_list;
newSuperMeta->subclass_list = meta;
}
// Make sure the superclass is initialized if we're initialized.
if (objc_test_class_flag(cls, objc_class_flag_initialized))
LOCK(&initialize_lock);
if (!objc_test_class_flag(cls, objc_class_flag_initialized))
{
objc_send_initialize(newSuper);
// Update the class's dtable to reflect its new superclass's dtable.
if (cls->dtable != uninstalled_dtable)
{
// we can't use objc_update_dtable_for_class here, as it doesn't take into account
// superclasses. It only walks downward.
free_dtable(cls->dtable);
cls->dtable = uninstalled_dtable;
cls->dtable = create_dtable_for_class(cls, uninstalled_dtable);
}
// Uninitialized classes don't have dtables to update
// and don't need their superclasses initialized.
UNLOCK(&initialize_lock);
UNLOCK_RUNTIME();
return oldSuper;
}
UNLOCK(&initialize_lock);
UNLOCK_RUNTIME();
objc_send_initialize(newSuper); // also initializes the metaclass
objc_update_dtable_for_new_superclass(cls->isa, newSuper->isa);
objc_update_dtable_for_new_superclass(cls, newSuper);
return oldSuper;
}
@ -828,8 +844,6 @@ const char *object_getClassName(id obj)
return class_getName(object_getClass(obj));
}
PRIVATE void objc_resolve_class(Class);
void objc_registerClassPair(Class cls)
{
LOCK_RUNTIME_FOR_SCOPE();

Loading…
Cancel
Save