-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathFABatching.h
169 lines (153 loc) · 8.78 KB
/
FABatching.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
#ifdef __APPLE__
#import <libkern/OSAtomic.h>
#else
typedef struct _NSZone NSZone;
typedef int32_t OSSpinLock;
#define OS_SPINLOCK_INIT (0)
static inline void OSSpinLockLock(volatile OSSpinLock *__lock )
{
while(__sync_lock_test_and_set(__lock, 1));
}
static inline bool OSSpinLockTry(volatile OSSpinLock *__lock )
{
return *__lock != 1;
}
static inline void OSSpinLockUnlock(volatile OSSpinLock *__lock )
{
__sync_lock_release(__lock);
}
#endif
// the size needed for the batch, with proper alignment for objects
#define FABatchAlignment 8
#define FAObjectsPerBatch 64
#define FABatchSize ((sizeof(FABatch) + (FABatchAlignment - 1)) & ~(FABatchAlignment - 1))
#define FABatchPoolSize 128
typedef struct
{
long instanceSize;
int32_t freed;
int32_t allocated;
int32_t _reserved;
} FABatch;
typedef struct
{
long poolSize;
long low, high;
FABatch *currentBatch;
FABatch **batches;
OSSpinLock spinLock;
} FABatchPool;
static inline FABatch *FANewObjectBatch(FABatchPool *pool, long batchInstanceSize)
{
unsigned long len;
unsigned long size;
FABatch *batch;
// Empty/Full pool => allocate new batch
if(pool->low == pool->high || ((pool->high + 1) % pool->poolSize) == pool->low) {
batchInstanceSize = (batchInstanceSize + (FABatchAlignment - 1)) & ~(FABatchAlignment - 1);
size = batchInstanceSize + sizeof(int);
len = size * FAObjectsPerBatch + FABatchSize;
if(!(batch = (FABatch *)calloc(1, len))){
NSLog(@"Failed to allocate object. Out of memory?");
return NULL;
}
batch->instanceSize = batchInstanceSize;
} else {
// Otherwise we recycle an existing batch
batch = pool->batches[pool->low];
pool->low = (pool->low + 1) % pool->poolSize;
}
return batch;
}
static inline void FARecycleObjectBatch(FABatchPool *pool, FABatch *batch)
{
unsigned long next = (pool->high + 1) % pool->poolSize;
if(next == pool->low) // Full?
free(batch);
else {
batch->freed = 0;
batch->allocated = 0;
pool->batches[pool->high] = batch;
pool->high = next;
__sync_val_compare_and_swap(&pool->currentBatch, batch, pool->batches[next]);
}
}
static inline BOOL FASizeFitsObjectBatch(FABatch *p, long size)
{
// We can't deal with subclasses larger than what we first allocated
return p && size <= p->instanceSize;
}
static inline BOOL FABatchIsExhausted(FABatch *p)
{
return p->allocated == FAObjectsPerBatch;
}
#define FA_BATCH_IVARS \
/* The batch the object is in */ \
FABatch *_batch; \
/* It's minus one so we don't have to initialize it to 1 */ \
NSInteger _retainCountMinusOne;
#define FA_BATCH_IMPL(Klass) \
static FABatchPool _BatchPool; \
\
static inline Klass *FABatchAlloc##Klass(Class self) \
{ \
size_t instanceSize = class_getInstanceSize(self); \
OSSpinLockLock(&_BatchPool.spinLock); \
if(__builtin_expect(!_BatchPool.batches, 0)) { \
_BatchPool.poolSize = FABatchPoolSize; \
_BatchPool.batches = (FABatch **)malloc(sizeof(void*) * _BatchPool.poolSize); \
_BatchPool.currentBatch = FANewObjectBatch(&_BatchPool, instanceSize); \
} \
\
Klass *obj = nil; \
FABatch *batch = _BatchPool.currentBatch; \
if(__builtin_expect(FASizeFitsObjectBatch(_BatchPool.currentBatch, instanceSize), 1)) \
{ \
/* Grab an object from the current batch */ \
/* and place isa pointer there */ \
unsigned long offset; \
offset = FABatchSize + batch->instanceSize * batch->allocated; \
obj = (id)((char *)batch + offset); \
obj->_batch = batch; \
obj->_retainCountMinusOne = 0; \
\
batch->allocated++; \
*(Class *)obj = self; \
} else { \
NSCAssert(NO, @"Unable to get %@ from batch", self); \
} \
\
/* Batch full? => Make a new one for next time */ \
if(FABatchIsExhausted(batch) && _BatchPool.currentBatch == batch) \
_BatchPool.currentBatch = FANewObjectBatch(&_BatchPool, instanceSize); \
\
OSSpinLockUnlock(&_BatchPool.spinLock); \
return obj; \
} \
\
+ (id)allocWithZone:(NSZone *)zone { return FABatchAlloc##Klass(self); } \
+ (id)alloc { return FABatchAlloc##Klass(self); } \
\
- (id)retain \
{ \
__sync_add_and_fetch(&_retainCountMinusOne, 1); \
return self; \
} \
- (oneway void)release \
{ \
if(__builtin_expect(__sync_sub_and_fetch(&_retainCountMinusOne, 1) < 0, 0)) \
[self dealloc]; \
}
#define FA_BATCH_DEALLOC \
/* Recycle the entire batch if all the objects in it are unreferenced */ \
if(__builtin_expect(__sync_add_and_fetch(&_batch->freed, 1) == FAObjectsPerBatch, 0)) { \
OSSpinLockLock(&_BatchPool.spinLock); \
FARecycleObjectBatch(&_BatchPool, _batch); \
OSSpinLockUnlock(&_BatchPool.spinLock); \
} \
return; \
__builtin_unreachable(); \
[super dealloc]; /* Silence compiler warning about not calling super dealloc */