diff --git a/ANNOUNCE b/ANNOUNCE index e196222..26a3f7d 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -6,8 +6,18 @@ libobjc2). This runtime was designed to support the features of Objective-C 2 for use with GNUstep and other Objective-C programs. Highlights of this release include: -- Support for the associated reference APIs introduced with OS X 10.6 -- Better hiding of local symbols +- Support for the associated reference APIs introduced with OS X 10.6. This + allows storing arbitrary objects associated with another object. +- Concurrent, thread-safe, +initialize. The runtime will now send +initialize + messages to different classes concurrently in multiple threads, but still + ensures that no class receives another message until it has returned from + +initialize. Exceptions can now safely propagate out of +initialize methods. +- Better hiding of local symbols. Now the internal runtime functions are not + visible from outside of the runtime. +- Dispatch table updates have been improved. Category loading no longer + triggers dtable creation and partial dtable updates are faster. +- Improvements to the low memory profile. Uses 5-10% less memory running Gorm, + and now passes the entire GNUstep and EtoileFoundation test suites. You may obtain the code for this release from subversion at the following subversion branch: diff --git a/dtable.c b/dtable.c index dd6790e..5493cd8 100644 --- a/dtable.c +++ b/dtable.c @@ -1,3 +1,4 @@ +#define __BSD_VISIBLE 1 #include #include #include "objc/runtime.h" @@ -165,27 +166,9 @@ static void insert_slot(dtable_t dtable, struct objc_slot *slot, uint32_t idx) dtable->slots[dtable->slot_count++].idx = idx; } -static void update_dtable(dtable_t dtable) +static void add_slot_to_dtable(uint32_t idx, dtable_t dtable, uint32_t + old_slot_count, struct objc_method *m, Class cls) { - Class cls = dtable->cls; - - if (NULL == cls->methods) { return; } - - SparseArray *methods = SparseArrayNewWithDepth(dtable_depth); - collectMethodsForMethodListToSparseArray((void*)cls->methods, methods, YES); - - if (NULL == dtable->slots) - { - dtable->slots = calloc(sizeof(struct slots_list), 16); - dtable->slot_size = 16; - } - - uint32_t old_slot_count = dtable->slot_count; - struct objc_method *m; - uint32_t idx = 0; - while ((m = SparseArrayNext(methods, &idx))) - { - uint32_t idx = m->selector->index; struct slots_list *s = find_slot(idx, dtable->slots, old_slot_count); if (NULL != s) { @@ -205,6 +188,31 @@ static void update_dtable(dtable_t dtable) } } } +} +static void update_dtable(dtable_t dtable) +{ + Class cls = dtable->cls; + + if (NULL == cls->methods) { return; } + + SparseArray *methods = SparseArrayNewWithDepth(dtable_depth); + collectMethodsForMethodListToSparseArray((void*)cls->methods, methods, YES); + + if (NULL == dtable->slots) + { + dtable->slots = calloc(sizeof(struct slots_list), 16); + dtable->slot_size = 16; + } + + uint32_t old_slot_count = dtable->slot_count; + struct objc_method *m; + uint32_t idx = 0; + while ((m = SparseArrayNext(methods, &idx))) + { + add_slot_to_dtable(m->selector->index, dtable, old_slot_count, m, cls); +#ifdef TYPE_DEPENDENT_DISPATCH + add_slot_to_dtable(get_untyped_idx(m->selector), dtable, old_slot_count, m, cls); +#endif } mergesort(dtable->slots, dtable->slot_count, sizeof(struct slots_list), slot_cmp); @@ -223,6 +231,11 @@ PRIVATE void objc_update_dtable_for_class(Class cls) update_dtable(dtable); } +PRIVATE void add_method_list_to_class(Class cls, + struct objc_method_list *list) +{ + objc_update_dtable_for_class(cls); +} PRIVATE struct objc_slot* objc_dtable_lookup(dtable_t dtable, uint32_t uid) { @@ -246,17 +259,21 @@ PRIVATE struct objc_slot* objc_dtable_lookup(dtable_t dtable, uint32_t uid) slot = s->slot; int i = HASH_UID(uid); volatile struct cache_line *cache = &dtable->cache[i]; - cache->idx = 0; + // Simplified multiword atomic exchange. First we write a value that + // is an invalid but recognisable UID and then a memory barrier. Then + // we complete the update and set the index pointer if and only if + // there have been no other modifications in the meantime + cache->idx = -uid; + __sync_synchronize(); cache->version = slot->version; cache->slot = slot; - __sync_synchronize(); - cache->idx = uid; + __sync_bool_compare_and_swap(&cache->idx, -uid, uid); return slot; } if (NULL != dtable->cls->super_class) { - return objc_dtable_lookup(dtable->cls->super_class->dtable, uid); + return objc_dtable_lookup(dtable_for_class(dtable->cls->super_class), uid); } return NULL; } @@ -274,7 +291,7 @@ PRIVATE void free_dtable(dtable_t dtable) { free(dtable->slots); } - DESTROY_LOCK(dtable->lock); + DESTROY_LOCK(&dtable->lock); free(dtable); } @@ -585,13 +602,6 @@ PRIVATE void objc_send_initialize(id object) // If this class is already initialized (e.g. in another thread), give up. if (objc_test_class_flag(class, objc_class_flag_initialized)) { return; } - // Grab a lock to make sure we are only sending one +initialize message at - // once. - // - // 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. - // Make sure that the class is resolved. objc_resolve_class(class);