#include "objc/runtime.h" #include #include #include #include #include "class.h" #include "properties.h" #ifdef __MINGW32__ #include static unsigned sleep(unsigned seconds) { Sleep(seconds*1000); return 0; } #endif // Subset of NSObject interface needed for properties. @interface NSObject {} - (id)retain; - (id)copy; - (id)autorelease; - (void)release; @end /** * Number of spinlocks. This allocates one page on 32-bit platforms. */ #define spinlock_count (1<<10) const int spinlock_mask = spinlock_count - 1; /** * Integers used as spinlocks for atomic property access. */ static int spinlocks[spinlock_count]; /** * Get a spin lock from a pointer. We want to prevent lock contention between * properties in the same object - if someone is stupid enough to be using * atomic property access, they are probably stupid enough to do it for * multiple properties in the same object. We also want to try to avoid * contention between the same property in different objects, so we can't just * use the ivar offset. */ static inline int *lock_for_pointer(void *ptr) { intptr_t hash = (intptr_t)ptr; // Most properties will be pointers, so disregard the lowest few bits hash >>= sizeof(void*) == 4 ? 2 : 8; intptr_t low = hash & spinlock_mask; hash >>= 16; hash |= low; return spinlocks + (hash & spinlock_mask); } /** * Unlocks the spinlock. This is not an atomic operation. We are only ever * modifying the lowest bit of the spinlock word, so it doesn't matter if this * is two writes because there is no contention among the high bit. There is * no possibility of contention among calls to this, because it may only be * called by the thread owning the spin lock. */ inline static void unlock_spinlock(int *spinlock) { *spinlock = 0; } /** * Attempts to lock a spinlock. This is heavily optimised for the uncontended * case, because property access should (generally) not be contended. In the * uncontended case, this is a single atomic compare and swap instruction and a * branch. Atomic CAS is relatively expensive (can be a pipeline flush, and * may require locking a cache line in a cache-coherent SMP system, but it's a * lot cheaper than a system call). * * If the lock is contended, then we just sleep and then try again after the * other threads have run. Note that there is no upper bound on the potential * running time of this function, which is one of the great many reasons that * using atomic accessors is a terrible idea, but in the common case it should * be very fast. */ inline static void lock_spinlock(int *spinlock) { int count = 0; // Set the spin lock value to 1 if it is 0. while(!__sync_bool_compare_and_swap(spinlock, 0, 1)) { count++; if (0 == count % 10) { // If it is already 1, let another thread play with the CPU for a // bit then try again. sleep(0); } } } /** * Public function for getting a property. */ id objc_getProperty(id obj, SEL _cmd, ptrdiff_t offset, BOOL isAtomic) { if (nil == obj) { return nil; } char *addr = (char*)obj; addr += offset; id ret; if (isAtomic) { int *lock = lock_for_pointer(addr); lock_spinlock(lock); ret = *(id*)addr; ret = [ret retain]; unlock_spinlock(lock); } else { ret = *(id*)addr; ret = [ret retain]; } return [ret autorelease]; } void objc_setProperty(id obj, SEL _cmd, ptrdiff_t offset, id arg, BOOL isAtomic, BOOL isCopy) { if (nil == obj) { return; } if (isCopy) { arg = [arg copy]; } else { arg = [arg retain]; } char *addr = (char*)obj; addr += offset; id old; if (isAtomic) { int *lock = lock_for_pointer(addr); lock_spinlock(lock); old = *(id*)addr; *(id*)addr = arg; unlock_spinlock(lock); } else { old = *(id*)addr; *(id*)addr = arg; } [old release]; } /** * Structure copy function. This is provided for compatibility with the Apple * APIs (it's an ABI function, so it's semi-public), but it's a bad design so * it's not used. The problem is that it does not identify which of the * pointers corresponds to the object, which causes some excessive locking to * be needed. */ void objc_copyPropertyStruct(void *dest, void *src, ptrdiff_t size, BOOL atomic, BOOL strong) { if (atomic) { int *lock = lock_for_pointer(src); int *lock2 = lock_for_pointer(src); lock_spinlock(lock); lock_spinlock(lock2); memcpy(dest, src, size); unlock_spinlock(lock); unlock_spinlock(lock2); } else { memcpy(dest, src, size); } } /** * Get property structure function. Copies a structure from an ivar to another * variable. Locks on the address of src. */ void objc_getPropertyStruct(void *dest, void *src, ptrdiff_t size, BOOL atomic, BOOL strong) { if (atomic) { int *lock = lock_for_pointer(src); lock_spinlock(lock); memcpy(dest, src, size); unlock_spinlock(lock); } else { memcpy(dest, src, size); } } /** * Set property structure function. Copes a structure to an ivar. Locks on * dest. */ void objc_setPropertyStruct(void *dest, void *src, ptrdiff_t size, BOOL atomic, BOOL strong) { if (atomic) { int *lock = lock_for_pointer(dest); lock_spinlock(lock); memcpy(dest, src, size); unlock_spinlock(lock); } else { memcpy(dest, src, size); } } objc_property_t class_getProperty(Class cls, const char *name) { // Old ABI classes don't have declared properties if (Nil == cls || !objc_test_class_flag(cls, objc_class_flag_new_abi)) { return NULL; } struct objc_property_list *properties = cls->properties; while (NULL != properties) { for (int i=0 ; icount ; i++) { objc_property_t p = &properties->properties[i]; if (strcmp(p->name, name) == 0) { return p; } } properties = properties->next; } return NULL; } objc_property_t* class_copyPropertyList(Class cls, unsigned int *outCount) { if (Nil == cls || !objc_test_class_flag(cls, objc_class_flag_new_abi)) { if (NULL != outCount) { *outCount = 0; } return NULL; } struct objc_property_list *properties = cls->properties; unsigned int count = 0; for (struct objc_property_list *l=properties ; NULL!=l ; l=l->next) { count += l->count; } if (NULL != outCount) { *outCount = count; } if (0 == count) { return NULL; } objc_property_t *list = calloc(sizeof(objc_property_t), count); unsigned int out = 0; for (struct objc_property_list *l=properties ; NULL!=l ; l=l->next) { for (int i=0 ; icount ; i++) { list[out] = &l->properties[i]; } } return list; } const char *property_getName(objc_property_t property) { return property->name; }