4 //////////////////////////////////////////////////////////
6 // A memory pool implements POOLCREATE, POOLALLOC and
7 // POOLFREE to improve memory allocation by reusing records.
9 // This implementation uses a lock-free singly-linked list
10 // to store reusable records. The list is initialized with
11 // one valid record, and the list is considered empty when
12 // it has only one record; this allows the enqueue operation's
13 // CAS to assume tail can always be dereferenced.
15 // poolfree adds newly freed records to the list BACK
17 // poolalloc either takes records from FRONT or mallocs
19 //////////////////////////////////////////////////////////
27 #define CACHELINESIZE 64
28 #define DQ_POP_EMPTY NULL
29 #define DQ_POP_ABORT NULL
32 typedef struct sqMemPoolItem_t {
37 typedef struct sqMemPool_t {
42 // avoid cache line contention between producer/consumer...
43 char buffer[CACHELINESIZE];
51 typedef struct dequeItem_t {
53 struct dequeItem_t * next;
57 typedef struct deque_t {
60 // avoid cache line contention between producer/consumer...
61 char buffer[CACHELINESIZE - sizeof(void*)];
68 #define EXTRACTPTR(x) (x&0x0000ffffffffffff)
69 #define INCREMENTTAG 0x0001000000000000
71 // the memory pool must always have at least one
73 static void dqInit(deque *q) {
74 q->head = calloc( 1, sizeof(dequeItem) );
77 q->objret.itemSize=sizeof(dequeItem);
78 q->objret.head=calloc(1, sizeof(dequeItem));
79 q->objret.head->next=NULL;
80 q->objret.tail=q->objret.head;
83 static inline void tagpoolfreeinto( sqMemPool* p, void* ptr, void *realptr ) {
84 sqMemPoolItem* tailCurrent;
85 sqMemPoolItem* tailActual;
87 // set up the now unneeded record to as the tail of the
88 // free list by treating its first bytes as next pointer,
89 sqMemPoolItem* tailNew = (sqMemPoolItem*) realptr;
93 // make sure the null happens before the insertion,
94 // also makes sure that we reload tailCurrent, etc..
97 tailCurrent = p->tail;
98 tailActual = (sqMemPoolItem*)
99 CAS( &(p->tail), // ptr to set
100 (INTPTR) tailCurrent, // current tail's next should be NULL
101 (INTPTR) realptr); // try set to our new tail
103 if( tailActual == tailCurrent ) {
104 // success, update tail
105 tailCurrent->next = (sqMemPoolItem *) ptr;
109 // if CAS failed, retry entire operation
113 static inline void* tagpoolalloc( sqMemPool* p ) {
115 // to protect CAS in poolfree from dereferencing
116 // null, treat the queue as empty when there is
117 // only one item. The dequeue operation is only
118 // executed by the thread that owns the pool, so
119 // it doesn't require an atomic op
120 sqMemPoolItem* headCurrent = p->head;
121 sqMemPoolItem* realHead=(sqMemPoolItem *) EXTRACTPTR((INTPTR)headCurrent);
122 sqMemPoolItem* next=realHead->next;
125 // only one item, so don't take from pool
126 return (void*) RUNMALLOC( p->itemSize );
131 //////////////////////////////////////////////////////////
134 // static inline void prefetch(void *x)
136 // asm volatile("prefetcht0 %0" :: "m" (*(unsigned long *)x));
140 // but this built-in gcc one seems the most portable:
141 //////////////////////////////////////////////////////////
142 //__builtin_prefetch( &(p->head->next) );
143 sqMemPoolItem* realNext=(sqMemPoolItem *) EXTRACTPTR((INTPTR)next);
144 asm volatile( "prefetcht0 (%0)" :: "r" (realNext));
145 realNext=(sqMemPoolItem*)(((char *)realNext)+CACHELINESIZE);
146 asm volatile( "prefetcht0 (%0)" :: "r" (realNext));
148 return (void*)headCurrent;
154 // in: a ptr, expected old, desired new
155 // return: actual old
157 // Pass in a ptr, what you expect the old value is and
158 // what you want the new value to be.
159 // The CAS returns what the value is actually: if it matches
160 // your proposed old value then you assume the update was successful,
161 // otherwise someone did CAS before you, so try again (the return
162 // value is the old value you will pass next time.)
164 static inline void dqPushBottom( deque* p, void* work ) {
165 dequeItem *ptr=(dequeItem *) tagpoolalloc(&p->objret);
166 // dequeItem *ptr=(dequeItem *) calloc(1,sizeof(dequeItem));
167 dequeItem *realptr=(dequeItem *) EXTRACTPTR((INTPTR)ptr);
168 ptr=(dequeItem *) (((INTPTR)ptr)+INCREMENTTAG);
175 static inline void* dqPopTop(deque *p) {
176 dequeItem *ptr=p->head;
177 dequeItem *realptr=(dequeItem *) EXTRACTPTR((INTPTR)ptr);
178 dequeItem *next=realptr->next;
179 //remove if we can..steal work no matter what
180 if (likely(next!=NULL)) {
181 if (((dequeItem *)CAS(&(p->head),(INTPTR)ptr, (INTPTR)next))!=ptr)
184 item=(void *)LOCKXCHG((unsigned INTPTR*) &(realptr->work), (unsigned INTPTR) item);
187 tagpoolfreeinto(&p->objret,ptr, realptr);
191 item=(void *) LOCKXCHG((unsigned INTPTR*) &(realptr->work), (unsigned INTPTR) item);
196 #define dqPopBottom dqPopTop
199 #endif // ___MEMPOOL_H__