Don't do hidden-class transforms for classes, just dangle the look-aside data off the class structure.

Individually lock classes so that +initialize can be sent concurrently to two different classes in two different threads.
main
theraven 15 years ago
parent 24de067017
commit 987ab88a94

@ -181,12 +181,12 @@ static Class allocateHiddenClass(Class superclass)
// Set the superclass pointer to the name. The runtime will fix this when
// the class links are resolved.
newClass->name = superclass->name;
newClass->info = objc_class_flag_resolved | objc_class_flag_initialized |
newClass->info = objc_class_flag_resolved |
objc_class_flag_class | objc_class_flag_user_created |
objc_class_flag_new_abi | objc_class_flag_hidden_class |
objc_class_flag_assoc_class;
newClass->super_class = superclass;
newClass->dtable = objc_copy_dtable_for_class(superclass->dtable, newClass);
newClass->dtable = uninstalled_dtable;
newClass->instance_size = superclass->instance_size;
if (objc_test_class_flag(superclass, objc_class_flag_meta))
{
@ -235,13 +235,25 @@ static void deallocHiddenClass(id obj, SEL _cmd)
free(hiddenClass);
}
void objc_setAssociatedObject(id object,
void *key,
id value,
objc_AssociationPolicy policy)
static struct reference_list* referenceListForObject(id object, BOOL create)
{
if (class_isMetaClass(object->isa))
{
Class cls = (Class)object;
if ((NULL == cls->extra_data) && create)
{
int *lock = lock_for_pointer(cls);
lock_spinlock(lock);
if (NULL == cls->extra_data)
{
cls->extra_data = calloc(1, sizeof(struct reference_list));
}
unlock_spinlock(lock);
}
return cls->extra_data;
}
Class hiddenClass = findHiddenClass(object);
if (NULL == hiddenClass)
if ((NULL == hiddenClass) && create)
{
int *lock = lock_for_pointer(object);
lock_spinlock(lock);
@ -252,15 +264,22 @@ void objc_setAssociatedObject(id object,
}
unlock_spinlock(lock);
}
struct reference_list *list = object_getIndexedIvars(hiddenClass);
return hiddenClass ? object_getIndexedIvars(hiddenClass) : NULL;
}
void objc_setAssociatedObject(id object,
void *key,
id value,
objc_AssociationPolicy policy)
{
struct reference_list *list = referenceListForObject(object, YES);
setReference(list, key, value, policy);
}
id objc_getAssociatedObject(id object, void *key)
{
Class hiddenClass = findHiddenClass(object);
if (NULL == hiddenClass) { return nil; }
struct reference_list *list = object_getIndexedIvars(hiddenClass);
struct reference_list *list = referenceListForObject(object, NO);
if (NULL == list) { return nil; }
struct reference *r = findReference(list, key);
return r ? r->object : nil;
}
@ -268,8 +287,5 @@ id objc_getAssociatedObject(id object, void *key)
void objc_removeAssociatedObjects(id object)
{
Class hiddenClass = findHiddenClass(object);
if (NULL == hiddenClass) { return; }
struct reference_list *list = object_getIndexedIvars(hiddenClass);
cleanupReferenceList(list);
cleanupReferenceList(referenceListForObject(object, NO));
}

@ -76,7 +76,7 @@ struct objc_class
/**
* Linked list of extra data attached to this class.
*/
struct class_annotation *extra_data;
struct reference_list *extra_data;
/**
* New ABI. The following fields are only available with classes compiled to
* support the new ABI. You may test whether any given class supports this

@ -28,7 +28,6 @@ static void collectMethodsForMethodListToSparseArray(
}
for (unsigned i=0 ; i<list->count ; i++)
{
//fprintf(stderr, "Adding method %s (%d)\n", sel_getName(list->methods[i].selector), PTR_TO_IDX(list->methods[i].selector->name));
SparseArrayInsert(sarray, list->methods[i].selector->index,
(void*)&list->methods[i]);
}
@ -74,7 +73,7 @@ PRIVATE void update_dispatch_table_for_class(Class cls)
}
}
static dtable_t create_dtable_for_class(Class class)
static SparseArray *create_dtable_for_class(Class class, dtable_t root_dtable)
{
// Don't create a dtable for a class that already has one
if (classHasDtable(class)) { return dtable_for_class(class); }
@ -85,8 +84,6 @@ static dtable_t create_dtable_for_class(Class class)
// waiting on the lock.
if (classHasDtable(class)) { return dtable_for_class(class); }
Class super = class_getSuperclass(class);
/* Allocate dtable if necessary */
dtable_t dtable = calloc(1, sizeof(struct objc_dtable));
dtable->cls = class;
@ -179,7 +176,6 @@ static void insert_slot(dtable_t dtable, struct objc_slot *slot, uint32_t idx)
static void update_dtable(dtable_t dtable)
{
//fprintf(stderr, "Updating dtable for %s! %d\n", dtable->cls->name, dtable_depth);
Class cls = dtable->cls;
if (NULL == cls->methods) { return; }
@ -253,7 +249,6 @@ PRIVATE struct objc_slot* objc_dtable_lookup(dtable_t dtable, uint32_t uid)
{
update_dtable(dtable);
}
//fprintf(stderr, "Using slow path: %d entries in table for %s\n", dtable->slot_count, dtable->cls->name);
struct slots_list *s = find_slot(uid, dtable->slots, dtable->slot_count);
if (NULL != s)
{
@ -302,7 +297,6 @@ static BOOL installMethodInDtable(Class class,
struct objc_slot *slot = SparseArrayLookup(dtable, sel_id);
if (NULL != slot)
{
//fprintf(stderr, "Slot already exists...\n");
// If this method is the one already installed, pretend to install it again.
if (slot->method == method->imp) { return NO; }
@ -314,7 +308,6 @@ static BOOL installMethodInDtable(Class class,
// Don't replace methods if we're not meant to (if they're from
// later in a method list, for example)
if (!replaceExisting) { return NO; }
//fprintf(stderr, "Replacing method %p %s in %s with %p\n", slot->method, sel_getName(method->selector), class->name, method->imp);
slot->method = method->imp;
return YES;
}
@ -328,13 +321,11 @@ static BOOL installMethodInDtable(Class class,
{
if (installedFor == owner)
{
//fprintf(stderr, "Not installing %s from %s in %s - already overridden from %s\n", sel_getName(method->selector), owner->name, class->name, slot->owner->name);
return NO;
}
}
}
struct objc_slot *oldSlot = slot;
//fprintf(stderr, "Installing method %p (%d) %s in %s (previous slot owned by %s)\n", method->imp, sel_id, sel_getName(method->selector), class->name, slot? oldSlot->owner->name: "(no one)");
slot = new_slot_for_method_in_class((void*)method, owner);
SparseArrayInsert(dtable, sel_id, slot);
// In TDD mode, we also register the first typed method that we
@ -345,7 +336,6 @@ static BOOL installMethodInDtable(Class class,
// Invalidate the old slot, if there is one.
if (NULL != oldSlot)
{
//fprintf(stderr, "Overriding method %p %s from %s in %s with %x\n", slot->method, sel_getName(method->selector), oldSlot->owner->name, class->name, method->imp);
oldSlot->version++;
}
return YES;
@ -398,11 +388,9 @@ PRIVATE void objc_update_dtable_for_class(Class cls)
{
// Only update real dtables
if (!classHasDtable(cls)) { return; }
//fprintf(stderr, "Updating dtable for %s\n", cls->name);
LOCK_RUNTIME_FOR_SCOPE();
//fprintf(stderr, "Adding methods to %s\n", cls->name);
SparseArray *methods = SparseArrayNewWithDepth(dtable_depth);
collectMethodsForMethodListToSparseArray((void*)cls->methods, methods);
installMethodsInClass(cls, cls, methods, YES);
@ -422,7 +410,7 @@ PRIVATE void update_dispatch_table_for_class(Class cls)
objc_update_dtable_for_class(cls);
}
static SparseArray *create_dtable_for_class(Class class)
static SparseArray *create_dtable_for_class(Class class, dtable_t root_dtable)
{
// Don't create a dtable for a class that already has one
if (classHasDtable(class)) { return dtable_for_class(class); }
@ -446,9 +434,16 @@ static SparseArray *create_dtable_for_class(Class class)
dtable_t super_dtable = dtable_for_class(super);
if (super_dtable == uninstalled_dtable)
{
super_dtable = create_dtable_for_class(super);
if (super->isa == class)
{
super_dtable = root_dtable;
}
else
{
abort();
}
}
dtable = SparseArrayCopy(dtable_for_class(super));
dtable = SparseArrayCopy(super_dtable);
}
// When constructing the initial dtable for a class, we iterate along the
@ -508,6 +503,19 @@ PRIVATE dtable_t objc_copy_dtable_for_class(dtable_t old, Class cls)
void objc_resolve_class(Class);
int objc_sync_enter(id);
int objc_sync_exit(id);
__attribute__((unused)) static void objc_release_object_lock(id *x)
{
objc_sync_exit(*x);
}
#define LOCK_OBJECT_FOR_SCOPE(obj) \
__attribute__((cleanup(objc_release_object_lock)))\
__attribute__((unused)) id lock_object_pointer = obj;\
objc_sync_enter(obj);
/**
* Send a +initialize message to the receiver, if required.
*/
@ -532,7 +540,6 @@ PRIVATE void objc_send_initialize(id object)
// NOTE: Ideally, we would actually lock on the class object using
// objc_sync_enter(). This should be fixed once sync.m contains a (fast)
// special case for classes.
LOCK_FOR_SCOPE(&initialize_lock);
// Make sure that the class is resolved.
objc_resolve_class(class);
@ -543,19 +550,48 @@ PRIVATE void objc_send_initialize(id object)
objc_send_initialize((id)class->super_class);
}
LOCK(&initialize_lock);
// 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.
if (objc_test_class_flag(class, objc_class_flag_initialized)) { return; }
if (objc_test_class_flag(class, objc_class_flag_initialized))
{
UNLOCK(&initialize_lock);
return;
}
// Set the initialized flag on both this class and its metaclass, to make
// sure that +initialize is only ever sent once.
objc_set_class_flag(class, objc_class_flag_initialized);
objc_set_class_flag(meta, objc_class_flag_initialized);
dtable_t class_dtable = create_dtable_for_class(class, uninstalled_dtable);
dtable_t dtable = create_dtable_for_class(meta, class_dtable);
static SEL initializeSel = 0;
if (0 == initializeSel)
{
initializeSel = sel_registerName("initialize");
}
struct objc_slot *initializeSlot =
objc_dtable_lookup(dtable, initializeSel->index);
// If there's no initialize method, then don't bother installing and
// removing the initialize dtable, just install both dtables correctly now
if (0 == initializeSlot || initializeSlot->owner != meta)
{
meta->dtable = dtable;
class->dtable = class_dtable;
UNLOCK(&initialize_lock);
return;
}
LOCK_OBJECT_FOR_SCOPE((id)class);
// Create a temporary dtable, to be installed later.
dtable_t class_dtable = create_dtable_for_class(class);
InitializingDtable buffer = { class, class_dtable, temporary_dtables };
temporary_dtables = &buffer;
@ -563,9 +599,9 @@ PRIVATE void objc_send_initialize(id object)
// a message to this class in future, the lookup function will check this
// buffer if the receiver's dtable is not installed, and block if
// attempting to send a message to this class.
dtable_t dtable = create_dtable_for_class(meta);
InitializingDtable meta_buffer = { meta, dtable, temporary_dtables };
temporary_dtables = &meta_buffer;
UNLOCK(&initialize_lock);
// Store the buffer in the temporary dtables list. Note that it is safe to
// insert it into a global list, even though it's a temporary variable,
@ -573,44 +609,15 @@ PRIVATE void objc_send_initialize(id object)
//
// FIXME: This will actually break if +initialize throws an exception...
static SEL initializeSel = 0;
if (0 == initializeSel)
{
initializeSel = sel_registerName("initialize");
}
struct objc_slot *initializeSlot =
objc_dtable_lookup(dtable, initializeSel->index);
if (0 != initializeSlot)
{
if (Nil != class->super_class)
{
// The dtable to use for sending messages to the superclass. This
// is the superclass's metaclass' dtable.
dtable_t super_dtable = dtable_for_class(class->super_class->isa);
struct objc_slot *superSlot = objc_dtable_lookup(super_dtable,
initializeSel->index);
// Check that this IMP comes from the class, not from its
// superclass. We still have to use dtable_for_class() here
// because our +initialize call might be in response to a message
// sent from a subclass (e.g. NSObject +initialize sending a
// message to NSAutoreleasePool: NSObject's dtable won't have been
// installed at this point.
if (0 == superSlot || superSlot->method != initializeSlot->method)
{
initializeSlot->method((id)class, initializeSel);
}
}
else
{
initializeSlot->method((id)class, initializeSel);
}
}
// Install the real dtable for both the class and the metaclass.
// Install the real dtable for both the class and the metaclass. We can do
// this without the lock, because now both ways of accessing the dtable are
// safe. We only need the lock when we remove the cached version.
meta->dtable = dtable;
class->dtable = class_dtable;
LOCK(&initialize_lock);
// Remove the look-aside buffer entry.
if (temporary_dtables == &meta_buffer)
{
@ -625,4 +632,5 @@ PRIVATE void objc_send_initialize(id object)
}
prev->next = buffer.next;
}
UNLOCK(&initialize_lock);
}

@ -4,6 +4,7 @@
#include "objc/slot.h"
#include "visibility.h"
#include <stdint.h>
#include <stdio.h>
#ifdef __OBJC_LOW_MEMORY__
typedef struct objc_dtable* dtable_t;
@ -56,6 +57,7 @@ static inline dtable_t dtable_for_class(Class cls)
{
return cls->dtable;
}
LOCK_FOR_SCOPE(&initialize_lock);
if (classHasInstalledDtable(cls))
{

@ -116,7 +116,6 @@ Slot_t (*objc_plane_lookup)(id *receiver, SEL op, id sender) =
*/
Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender)
{
//fprintf(stderr, "Looking up slot %s\n", sel_get_name(selector));
// Returning a nil slot allows the caller to cache the lookup for nil too,
// although this is not particularly useful because the nil method can be
// inlined trivially.
@ -140,7 +139,6 @@ Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender)
void *receiverPlaneID = *((void**)receiver - 1);
if (senderPlaneID == receiverPlaneID)
{
//fprintf(stderr, "Intraplane message\n");
return objc_msg_lookup_internal(receiver, selector, sender);
}
return objc_plane_lookup(receiver, selector, sender);
@ -260,7 +258,6 @@ Slot_t objc_get_slot(Class cls, SEL selector)
/* Install the dtable if it hasn't already been initialized. */
if (dtable == uninstalled_dtable)
{
//objc_send_initialize((id)cls);
dtable = dtable_for_class(cls);
result = objc_dtable_lookup(dtable, selector->index);
}
@ -270,6 +267,7 @@ Slot_t objc_get_slot(Class cls, SEL selector)
// weren't looking
result = objc_dtable_lookup(dtable, selector->index);
}
Class class = cls;
if (NULL == result)
{
if (!isSelRegistered(selector))

Loading…
Cancel
Save