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

@ -76,7 +76,7 @@ struct objc_class
/** /**
* Linked list of extra data attached to this 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 * 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 * 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++) 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, SparseArrayInsert(sarray, list->methods[i].selector->index,
(void*)&list->methods[i]); (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 // Don't create a dtable for a class that already has one
if (classHasDtable(class)) { return dtable_for_class(class); } 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. // waiting on the lock.
if (classHasDtable(class)) { return dtable_for_class(class); } if (classHasDtable(class)) { return dtable_for_class(class); }
Class super = class_getSuperclass(class);
/* Allocate dtable if necessary */ /* Allocate dtable if necessary */
dtable_t dtable = calloc(1, sizeof(struct objc_dtable)); dtable_t dtable = calloc(1, sizeof(struct objc_dtable));
dtable->cls = class; 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) static void update_dtable(dtable_t dtable)
{ {
//fprintf(stderr, "Updating dtable for %s! %d\n", dtable->cls->name, dtable_depth);
Class cls = dtable->cls; Class cls = dtable->cls;
if (NULL == cls->methods) { return; } 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); 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); struct slots_list *s = find_slot(uid, dtable->slots, dtable->slot_count);
if (NULL != s) if (NULL != s)
{ {
@ -302,7 +297,6 @@ static BOOL installMethodInDtable(Class class,
struct objc_slot *slot = SparseArrayLookup(dtable, sel_id); struct objc_slot *slot = SparseArrayLookup(dtable, sel_id);
if (NULL != slot) if (NULL != slot)
{ {
//fprintf(stderr, "Slot already exists...\n");
// If this method is the one already installed, pretend to install it again. // If this method is the one already installed, pretend to install it again.
if (slot->method == method->imp) { return NO; } 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 // Don't replace methods if we're not meant to (if they're from
// later in a method list, for example) // later in a method list, for example)
if (!replaceExisting) { return NO; } 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; slot->method = method->imp;
return YES; return YES;
} }
@ -328,13 +321,11 @@ static BOOL installMethodInDtable(Class class,
{ {
if (installedFor == owner) 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; return NO;
} }
} }
} }
struct objc_slot *oldSlot = slot; 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); slot = new_slot_for_method_in_class((void*)method, owner);
SparseArrayInsert(dtable, sel_id, slot); SparseArrayInsert(dtable, sel_id, slot);
// In TDD mode, we also register the first typed method that we // 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. // Invalidate the old slot, if there is one.
if (NULL != oldSlot) 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++; oldSlot->version++;
} }
return YES; return YES;
@ -398,11 +388,9 @@ PRIVATE void objc_update_dtable_for_class(Class cls)
{ {
// Only update real dtables // Only update real dtables
if (!classHasDtable(cls)) { return; } if (!classHasDtable(cls)) { return; }
//fprintf(stderr, "Updating dtable for %s\n", cls->name);
LOCK_RUNTIME_FOR_SCOPE(); LOCK_RUNTIME_FOR_SCOPE();
//fprintf(stderr, "Adding methods to %s\n", cls->name);
SparseArray *methods = SparseArrayNewWithDepth(dtable_depth); SparseArray *methods = SparseArrayNewWithDepth(dtable_depth);
collectMethodsForMethodListToSparseArray((void*)cls->methods, methods); collectMethodsForMethodListToSparseArray((void*)cls->methods, methods);
installMethodsInClass(cls, cls, methods, YES); installMethodsInClass(cls, cls, methods, YES);
@ -422,7 +410,7 @@ PRIVATE void update_dispatch_table_for_class(Class cls)
objc_update_dtable_for_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 // Don't create a dtable for a class that already has one
if (classHasDtable(class)) { return dtable_for_class(class); } 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); dtable_t super_dtable = dtable_for_class(super);
if (super_dtable == uninstalled_dtable) 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 // 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); 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. * 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 // NOTE: Ideally, we would actually lock on the class object using
// objc_sync_enter(). This should be fixed once sync.m contains a (fast) // objc_sync_enter(). This should be fixed once sync.m contains a (fast)
// special case for classes. // special case for classes.
LOCK_FOR_SCOPE(&initialize_lock);
// Make sure that the class is resolved. // Make sure that the class is resolved.
objc_resolve_class(class); objc_resolve_class(class);
@ -543,19 +550,48 @@ PRIVATE void objc_send_initialize(id object)
objc_send_initialize((id)class->super_class); objc_send_initialize((id)class->super_class);
} }
LOCK(&initialize_lock);
// Superclass +initialize might possibly send a message to this class, in // Superclass +initialize might possibly send a message to this class, in
// which case this method would be called again. See NSObject and // which case this method would be called again. See NSObject and
// NSAutoreleasePool +initialize interaction in GNUstep. // 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 // Set the initialized flag on both this class and its metaclass, to make
// sure that +initialize is only ever sent once. // sure that +initialize is only ever sent once.
objc_set_class_flag(class, objc_class_flag_initialized); objc_set_class_flag(class, objc_class_flag_initialized);
objc_set_class_flag(meta, 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. // Create a temporary dtable, to be installed later.
dtable_t class_dtable = create_dtable_for_class(class);
InitializingDtable buffer = { class, class_dtable, temporary_dtables }; InitializingDtable buffer = { class, class_dtable, temporary_dtables };
temporary_dtables = &buffer; 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 // 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 // buffer if the receiver's dtable is not installed, and block if
// attempting to send a message to this class. // attempting to send a message to this class.
dtable_t dtable = create_dtable_for_class(meta);
InitializingDtable meta_buffer = { meta, dtable, temporary_dtables }; InitializingDtable meta_buffer = { meta, dtable, temporary_dtables };
temporary_dtables = &meta_buffer; temporary_dtables = &meta_buffer;
UNLOCK(&initialize_lock);
// Store the buffer in the temporary dtables list. Note that it is safe to // 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, // 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... // FIXME: This will actually break if +initialize throws an exception...
static SEL initializeSel = 0; initializeSlot->method((id)class, initializeSel);
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; meta->dtable = dtable;
class->dtable = class_dtable; class->dtable = class_dtable;
LOCK(&initialize_lock);
// Remove the look-aside buffer entry. // Remove the look-aside buffer entry.
if (temporary_dtables == &meta_buffer) if (temporary_dtables == &meta_buffer)
{ {
@ -625,4 +632,5 @@ PRIVATE void objc_send_initialize(id object)
} }
prev->next = buffer.next; prev->next = buffer.next;
} }
UNLOCK(&initialize_lock);
} }

@ -4,6 +4,7 @@
#include "objc/slot.h" #include "objc/slot.h"
#include "visibility.h" #include "visibility.h"
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#ifdef __OBJC_LOW_MEMORY__ #ifdef __OBJC_LOW_MEMORY__
typedef struct objc_dtable* dtable_t; typedef struct objc_dtable* dtable_t;
@ -56,6 +57,7 @@ static inline dtable_t dtable_for_class(Class cls)
{ {
return cls->dtable; return cls->dtable;
} }
LOCK_FOR_SCOPE(&initialize_lock); LOCK_FOR_SCOPE(&initialize_lock);
if (classHasInstalledDtable(cls)) 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) 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, // 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 // although this is not particularly useful because the nil method can be
// inlined trivially. // inlined trivially.
@ -140,7 +139,6 @@ Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender)
void *receiverPlaneID = *((void**)receiver - 1); void *receiverPlaneID = *((void**)receiver - 1);
if (senderPlaneID == receiverPlaneID) if (senderPlaneID == receiverPlaneID)
{ {
//fprintf(stderr, "Intraplane message\n");
return objc_msg_lookup_internal(receiver, selector, sender); return objc_msg_lookup_internal(receiver, selector, sender);
} }
return objc_plane_lookup(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. */ /* Install the dtable if it hasn't already been initialized. */
if (dtable == uninstalled_dtable) if (dtable == uninstalled_dtable)
{ {
//objc_send_initialize((id)cls);
dtable = dtable_for_class(cls); dtable = dtable_for_class(cls);
result = objc_dtable_lookup(dtable, selector->index); result = objc_dtable_lookup(dtable, selector->index);
} }
@ -270,6 +267,7 @@ Slot_t objc_get_slot(Class cls, SEL selector)
// weren't looking // weren't looking
result = objc_dtable_lookup(dtable, selector->index); result = objc_dtable_lookup(dtable, selector->index);
} }
Class class = cls;
if (NULL == result) if (NULL == result)
{ {
if (!isSelRegistered(selector)) if (!isSelRegistered(selector))

Loading…
Cancel
Save