You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
6.8 KiB
Objective-C
285 lines
6.8 KiB
Objective-C
#include "objc/runtime.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include "class.h"
|
|
#include "properties.h"
|
|
|
|
#ifdef __MINGW32__
|
|
#include <windows.h>
|
|
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, int 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, int 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 ; i<properties->count ; 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 ; i<properties->count ; i++)
|
|
{
|
|
list[out] = &l->properties[i];
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
const char *property_getName(objc_property_t property)
|
|
{
|
|
return property->name;
|
|
}
|