Merge pull request #10 from gunyarakun/fix-invalid-return
[cocotron.git] / objc / objc_arc.c
blob3f7f7bb200a8ce65b85b4a3f151581459b4569dc
1 #import <objc/runtime.h>
2 #import "objc_tls.h"
3 #import "objc_lock.h"
4 #import <objc/message.h>
5 #import <pthread.h>
7 typedef unsigned long objc_uinteger;
8 typedef signed long objc_integer;
10 typedef struct RefCountBucket {
11 struct RefCountBucket *next;
12 void *object;
13 objc_uinteger count;
14 } RefCountBucket;
16 typedef struct {
17 objc_uinteger count;
18 objc_uinteger nBuckets;
19 RefCountBucket **buckets;
20 } RefCountTable;
22 static objc_lock RefCountLock = 0;
24 static inline RefCountTable *CreateRefCountTable() {
25 RefCountTable *table;
27 table = malloc(sizeof(RefCountTable));
28 table->count = 0;
29 table->nBuckets = 1024;
30 table->buckets = calloc(table->nBuckets, sizeof(RefCountBucket *));
32 return table;
35 static inline RefCountBucket *AllocBucketFromTable(RefCountTable *table) {
36 return malloc(sizeof(RefCountBucket));
39 static inline void FreeBucketFromTable(RefCountTable *table, RefCountBucket *bucket) {
40 free(bucket);
43 static inline objc_uinteger hashObject(id ptr) {
44 return (objc_uinteger)ptr >> 4;
47 static inline RefCountBucket *XXHashGet(RefCountTable *table, id object) {
48 objc_uinteger i = hashObject(object) % table->nBuckets;
49 RefCountBucket *check;
51 for(check = table->buckets[i]; check != NULL; check = check->next)
52 if(check->object == object)
53 return check;
55 return NULL;
58 static inline void XXHashInsert(RefCountTable *table, RefCountBucket *insert) {
59 objc_uinteger hash = hashObject(insert->object);
60 objc_uinteger i = hash % table->nBuckets;
62 if(table->count >= table->nBuckets) {
63 objc_integer oldnBuckets = table->nBuckets;
64 RefCountBucket **buckets = table->buckets;
66 table->nBuckets = oldnBuckets * 2;
67 table->buckets = calloc(table->nBuckets, sizeof(RefCountBucket *));
68 for(i = 0; i < oldnBuckets; i++) {
69 RefCountBucket *check, *next;
71 for(check = buckets[i]; check != NULL; check = next) {
72 objc_uinteger newi = hashObject(check->object) % table->nBuckets;
73 next = check->next;
74 check->next = table->buckets[newi];
75 table->buckets[newi] = check;
78 free(buckets);
79 i = hash % table->nBuckets;
82 insert->next = table->buckets[i];
83 table->buckets[i] = insert;
84 table->count++;
87 static inline void XXHashRemove(RefCountTable *table, RefCountBucket *remove) {
88 objc_uinteger i = hashObject(remove->object) % table->nBuckets;
89 RefCountBucket *check = table->buckets[i], *prev = check;
91 for(; check != NULL; check = check->next) {
92 if(check == remove) {
93 if(prev == check)
94 table->buckets[i] = check->next;
95 else
96 prev->next = check->next;
98 FreeBucketFromTable(table, check);
99 table->count--;
100 return;
102 prev = check;
106 static inline RefCountTable *refTable(void) {
107 static RefCountTable *refCountTable = NULL;
109 if(refCountTable == NULL)
110 refCountTable = CreateRefCountTable();
112 return refCountTable;
115 void objc_IncrementExtraRefCount(id object) {
116 RefCountBucket *refCount;
117 RefCountTable *table = refTable();
119 objc_lock_lock(&RefCountLock);
120 if((refCount = XXHashGet(table, object)) == NULL) {
121 refCount = AllocBucketFromTable(table);
122 refCount->object = object;
123 refCount->count = 1;
124 XXHashInsert(refTable(), refCount);
126 refCount->count++;
127 objc_lock_unlock(&RefCountLock);
130 bool objc_DecrementExtraRefCountWasZero(id object) {
131 bool result = false;
132 RefCountBucket *refCount;
134 objc_lock_lock(&RefCountLock);
135 if((refCount = XXHashGet(refTable(), object)) == NULL)
136 result = true;
137 else {
138 refCount->count--;
139 if(refCount->count == 1)
140 XXHashRemove(refTable(), refCount);
142 objc_lock_unlock(&RefCountLock);
144 return result;
147 objc_uinteger objc_ExtraRefCount(id object) {
148 objc_uinteger result = 1;
149 RefCountBucket *refCount;
151 objc_lock_lock(&RefCountLock);
152 if((refCount = XXHashGet(refTable(), object)) != NULL)
153 result = refCount->count;
154 objc_lock_unlock(&RefCountLock);
156 return result;
159 void object_incrementExternalRefCount(id value) {
160 objc_IncrementExtraRefCount(value);
163 bool object_decrementExternalRefCount(id value) {
164 return objc_DecrementExtraRefCountWasZero(value);
167 unsigned long object_externalRefCount(id value) {
168 return objc_ExtraRefCount(value);
171 id objc_retain(id value) {
172 objc_IncrementExtraRefCount(value);
173 return value;
176 void objc_release(id value) {
177 if(objc_DecrementExtraRefCountWasZero(value)) {
178 static SEL selector = NULL;
180 if(selector == NULL)
181 selector = sel_registerName("dealloc");
183 IMP dealloc = objc_msg_lookup(value, selector);
185 dealloc(value, selector);
189 static objc_autoreleasepool *objc_autoreleaseCurrentPool() {
190 return objc_tlsCurrent()->pool;
193 static void objc_autoreleaseSetCurrentPool(objc_autoreleasepool *pool) {
194 objc_tlsCurrent()->pool = pool;
197 #define PAGESIZE 1024
199 void *objc_autoreleasePoolPush() {
200 objc_autoreleasepool *current = objc_autoreleaseCurrentPool();
201 objc_autoreleasepool *pool = malloc(sizeof(objc_autoreleasepool));
203 pool->_parent = current;
205 pool->_pageCount = 1;
206 pool->_pages = malloc(pool->_pageCount * sizeof(id *));
207 pool->_pages[0] = malloc(PAGESIZE * sizeof(id));
208 pool->_nextSlot = 0;
210 if(current != NULL)
211 current->_childPool = pool;
212 pool->_childPool = NULL;
214 objc_autoreleaseSetCurrentPool(pool);
216 return pool;
219 void objc_autoreleasePoolPop(void *poolX) {
220 objc_autoreleasepool *pool = poolX;
221 int i;
223 if(pool == NULL)
224 return;
226 objc_autoreleasePoolPop(pool->_childPool);
228 for(i = 0; i < pool->_nextSlot; i++) {
229 // NS_DURING
230 id object = pool->_pages[i / PAGESIZE][i % PAGESIZE];
232 objc_release(object);
234 // NS_HANDLER
235 // NSLog("Exception while autoreleasing %@",localException);
236 // NS_ENDHANDLER
239 for(i = 0; i < pool->_pageCount; i++)
240 free(pool->_pages[i]);
242 free(pool->_pages);
244 objc_autoreleaseSetCurrentPool(pool->_parent);
246 if(pool->_parent != NULL)
247 pool->_parent->_childPool = NULL;
249 free(pool);
252 void objc_autoreleaseNoPool(id object) {
253 // NSCLog("autorelease pool is nil, leaking %x %s",object,object_getClassName(object));
256 void objc_autoreleasePoolAdd(objc_autoreleasepool *pool, id object) {
257 if(pool->_nextSlot >= pool->_pageCount * PAGESIZE) {
258 pool->_pageCount++;
259 pool->_pages = realloc(pool->_pages, pool->_pageCount * sizeof(id *));
260 pool->_pages[pool->_pageCount - 1] = malloc(PAGESIZE * sizeof(id));
263 pool->_pages[pool->_nextSlot / PAGESIZE][pool->_nextSlot % PAGESIZE] = object;
264 pool->_nextSlot++;
267 id objc_autorelease(id object) {
268 objc_autoreleasepool *pool = objc_autoreleaseCurrentPool();
270 if(pool == NULL) {
271 objc_autoreleaseNoPool(object);
272 return object;
275 objc_autoreleasePoolAdd(pool, object);
277 return object;