static void test_cas_ptr(TestBatch *batch) { int foo = 1; int bar = 2; int *foo_pointer = &foo; int *bar_pointer = &bar; int *target = NULL; TEST_TRUE(batch, Atomic_cas_ptr((void**)&target, NULL, foo_pointer), "cas_ptr returns true on success"); TEST_TRUE(batch, target == foo_pointer, "cas_ptr sets target"); target = NULL; TEST_FALSE(batch, Atomic_cas_ptr((void**)&target, bar_pointer, foo_pointer), "cas_ptr returns false when it old_value doesn't match"); TEST_TRUE(batch, target == NULL, "cas_ptr doesn't do anything to target when old_value doesn't match"); target = foo_pointer; TEST_TRUE(batch, Atomic_cas_ptr((void**)&target, foo_pointer, bar_pointer), "cas_ptr from one value to another"); TEST_TRUE(batch, target == bar_pointer, "cas_ptr sets target"); }
void Bool_init_class() { Boolean *true_obj = (Boolean*)Class_Make_Obj(BOOLEAN); true_obj->value = true; true_obj->string = Str_newf("true"); if (!Atomic_cas_ptr((void**)&Bool_true_singleton, NULL, true_obj)) { Bool_Destroy(true_obj); } Boolean *false_obj = (Boolean*)Class_Make_Obj(BOOLEAN); false_obj->value = false; false_obj->string = Str_newf("false"); if (!Atomic_cas_ptr((void**)&Bool_false_singleton, NULL, false_obj)) { Bool_Destroy(false_obj); } }
void Class_init_registry() { LockFreeRegistry *reg = LFReg_new(256); if (Atomic_cas_ptr((void*volatile*)&Class_registry, NULL, reg)) { return; } else { DECREF(reg); } }
void Class_bootstrap(const cfish_ParcelSpec *parcel_spec) { const ClassSpec *specs = parcel_spec->class_specs; const NovelMethSpec *novel_specs = parcel_spec->novel_specs; const OverriddenMethSpec *overridden_specs = parcel_spec->overridden_specs; const InheritedMethSpec *inherited_specs = parcel_spec->inherited_specs; uint32_t num_classes = parcel_spec->num_classes; /* Pass 1: * - Allocate memory. * - Initialize global Class pointers. */ for (uint32_t i = 0; i < num_classes; ++i) { const ClassSpec *spec = &specs[i]; Class *parent = NULL; if (spec->parent) { parent = *spec->parent; if (!parent) { // Wrong order of class specs or inheritance cycle. fprintf(stderr, "Parent class of '%s' not initialized\n", spec->name); abort(); } } uint32_t novel_offset = parent ? parent->class_alloc_size : offsetof(Class, vtable); uint32_t class_alloc_size = novel_offset + spec->num_novel_meths * sizeof(cfish_method_t); Class *klass = (Class*)CALLOCATE(class_alloc_size, 1); // Needed to calculate size of subclasses. klass->class_alloc_size = class_alloc_size; // Initialize the global pointer to the Class. if (!Atomic_cas_ptr((void**)spec->klass, NULL, klass)) { // Another thread beat us to it. FREEMEM(klass); } } /* Pass 2: * - Initialize IVARS_OFFSET. * - Initialize 'klass' ivar and refcount by calling Init_Obj. * - Initialize parent, flags, obj_alloc_size, class_alloc_size. * - Assign parcel_spec. * - Initialize method pointers and offsets. */ uint32_t num_novel = 0; uint32_t num_overridden = 0; uint32_t num_inherited = 0; for (uint32_t i = 0; i < num_classes; ++i) { const ClassSpec *spec = &specs[i]; Class *klass = *spec->klass; Class *parent = spec->parent ? *spec->parent : NULL; uint32_t ivars_offset = 0; if (spec->ivars_offset_ptr != NULL) { if (parent) { Class *ancestor = parent; while (ancestor && ancestor->parcel_spec == parcel_spec) { ancestor = ancestor->parent; } ivars_offset = ancestor ? ancestor->obj_alloc_size : 0; *spec->ivars_offset_ptr = ivars_offset; } else { *spec->ivars_offset_ptr = 0; } } // CLASS->obj_alloc_size is always 0, so Init_Obj doesn't clear any // values set in the previous pass or by another thread. Class_Init_Obj_IMP(CLASS, klass); klass->parent = parent; klass->parcel_spec = parcel_spec; // CLASS->obj_alloc_size must stay at 0. if (klass != CLASS) { klass->obj_alloc_size = ivars_offset + spec->ivars_size; } if (cfish_Class_bootstrap_hook1 != NULL) { cfish_Class_bootstrap_hook1(klass); } klass->flags = 0; if (klass == CLASS || klass == METHOD || klass == BOOLEAN || klass == STRING ) { klass->flags |= CFISH_fREFCOUNTSPECIAL; } if (spec->flags & cfish_ClassSpec_FINAL) { klass->flags |= CFISH_fFINAL; } if (parent) { // Copy parent vtable. uint32_t parent_vt_size = parent->class_alloc_size - offsetof(Class, vtable); memcpy(klass->vtable, parent->vtable, parent_vt_size); } for (size_t i = 0; i < spec->num_inherited_meths; ++i) { const InheritedMethSpec *mspec = &inherited_specs[num_inherited++]; *mspec->offset = *mspec->parent_offset; } for (size_t i = 0; i < spec->num_overridden_meths; ++i) { const OverriddenMethSpec *mspec = &overridden_specs[num_overridden++]; *mspec->offset = *mspec->parent_offset; Class_Override_IMP(klass, mspec->func, *mspec->offset); } uint32_t novel_offset = parent ? parent->class_alloc_size : offsetof(Class, vtable); for (size_t i = 0; i < spec->num_novel_meths; ++i) { const NovelMethSpec *mspec = &novel_specs[num_novel++]; *mspec->offset = novel_offset; novel_offset += sizeof(cfish_method_t); Class_Override_IMP(klass, mspec->func, *mspec->offset); } } /* Now it's safe to call methods. * * Pass 3: * - Inititalize name and method array. * - Register class. */ num_novel = 0; num_overridden = 0; num_inherited = 0; for (uint32_t i = 0; i < num_classes; ++i) { const ClassSpec *spec = &specs[i]; Class *klass = *spec->klass; String *name_internal = Str_new_from_trusted_utf8(spec->name, strlen(spec->name)); if (!Atomic_cas_ptr((void**)&klass->name_internal, NULL, name_internal) ) { DECREF(name_internal); name_internal = klass->name_internal; } String *name = Str_new_wrap_trusted_utf8(Str_Get_Ptr8(name_internal), Str_Get_Size(name_internal)); if (!Atomic_cas_ptr((void**)&klass->name, NULL, name)) { DECREF(name); name = klass->name; } Method **methods = (Method**)MALLOCATE((spec->num_novel_meths + 1) * sizeof(Method*)); // Only store novel methods for now. for (size_t i = 0; i < spec->num_novel_meths; ++i) { const NovelMethSpec *mspec = &novel_specs[num_novel++]; String *name = SSTR_WRAP_C(mspec->name); Method *method = Method_new(name, mspec->callback_func, *mspec->offset); methods[i] = method; } methods[spec->num_novel_meths] = NULL; if (!Atomic_cas_ptr((void**)&klass->methods, NULL, methods)) { // Another thread beat us to it. for (size_t i = 0; i < spec->num_novel_meths; ++i) { Method_Destroy(methods[i]); } FREEMEM(methods); } Class_add_to_registry(klass); } }