2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #pragma GCC optimization_level 0
28 ///////////////////////////////////////////////////////////////////////////
29 // Fast stack implementation
30 // NOTE: non-locking only on systems that support atomic cas2 operations
31 ///////////////////////////////////////////////////////////////////////////
32 void lf_stack_init(lf_stack* pStack)
34 pStack->top.ptr = NULL;
38 void lf_stack_push(lf_stack* pStack, lf_node* pNode)
40 atomic_ptr top, newTop;
44 pNode->next.ptr = top.ptr; // Link in the new node
46 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
47 } while(cas((long*)&pStack->top, atomic_ptr_to_long(top), atomic_ptr_to_long(newTop)) != atomic_ptr_to_long(top));
49 newTop.version = top.version + 1;
50 } while(cas2((long long*)&pStack->top, atomic_ptr_to_long_long(top), atomic_ptr_to_long_long(newTop)) != atomic_ptr_to_long_long(top));
52 AtomicIncrement(&pStack->count);
55 lf_node* lf_stack_pop(lf_stack* pStack)
57 atomic_ptr top, newTop;
63 newTop.ptr = ((lf_node*)top.ptr)->next.ptr; // Unlink the current top node
64 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
65 } while(cas((long*)&pStack->top, atomic_ptr_to_long(top), atomic_ptr_to_long(newTop)) != atomic_ptr_to_long(top));
67 newTop.version = top.version + 1;
68 } while(cas2((long long*)&pStack->top, atomic_ptr_to_long_long(top), atomic_ptr_to_long_long(newTop)) != atomic_ptr_to_long_long(top));
70 AtomicDecrement(&pStack->count);
71 return (lf_node*)top.ptr;
74 ///////////////////////////////////////////////////////////////////////////
75 // Fast heap implementation
76 // NOTE: non-locking only on systems that support atomic cas2 operations
77 ///////////////////////////////////////////////////////////////////////////
78 // TODO: Implement auto-shrink based on chunk reference counts
80 // TODO: Read the page size from the OS or allow caller to specify
81 // Maybe have a minimum number of blocks...
82 #define MIN_ALLOC 4096
84 void lf_heap_init(lf_heap* pHeap, size_t blockSize, size_t initialSize /*= 0*/)
86 pHeap->alloc_lock = 0; // Initialize the allocation lock
87 pHeap->top_chunk = NULL;
89 lf_stack_init(&pHeap->free_list); // Initialize the free-list stack
91 // Perform a few sanity checks on the parameters
92 if (blockSize < sizeof(lf_node)) // Make sure we have blocks big enough to store in the free-list
93 blockSize = sizeof(lf_node);
94 pHeap->block_size = blockSize;
96 if (initialSize < 10 * blockSize)
97 initialSize = 10 * blockSize; // TODO: This should be more intelligent
99 lf_heap_grow(pHeap, initialSize); // Allocate the first chunk
102 void lf_heap_grow(lf_heap* pHeap, size_t size /*= 0*/)
105 long blockSize = pHeap->block_size; // This has already been checked for sanity
106 if (!size || size < MIN_ALLOC - sizeof(lf_heap_chunk)) // Allocate at least one page from the OS (TODO: Try valloc)
107 size = MIN_ALLOC - sizeof(lf_heap_chunk);
108 unsigned int blockCount = size / blockSize;
109 if (size % blockSize) // maxe sure we have complete blocks
110 size = blockSize * ++blockCount;
112 // Allocate the first chunk from the general heap and link it into the chunk list
113 long mallocSize = size + sizeof(lf_heap_chunk);
114 lf_heap_chunk* pChunk = (lf_heap_chunk*) malloc(mallocSize);
115 pChunk->size = mallocSize;
116 SPINLOCK_ACQUIRE(pHeap->alloc_lock); // Lock the chunk list. Contention here is VERY unlikely, so use the simplest possible sync mechanism.
117 pChunk->next = pHeap->top_chunk;
118 pHeap->top_chunk = pChunk; // Link it into the list
119 SPINLOCK_RELEASE(pHeap->alloc_lock); // The list is now consistent
121 // Add all blocks to the free-list
122 unsigned char* pBlock = (unsigned char*)pChunk + sizeof(lf_heap_chunk);
123 for ( unsigned int block = 0; block < blockCount; block++)
125 lf_stack_push(&pHeap->free_list, (lf_node*)pBlock);
130 void lf_heap_deinit(lf_heap* pHeap)
132 // Free all allocated chunks
133 lf_heap_chunk* pNext;
134 for(lf_heap_chunk* pChunk = pHeap->top_chunk; pChunk; pChunk = pNext)
136 pNext = pChunk->next;
141 void* lf_heap_alloc(lf_heap* pHeap)
143 void * p = lf_stack_pop(&pHeap->free_list);
146 lf_heap_grow(pHeap, 0);
147 // TODO: should we just call in recursively?
148 return lf_stack_pop(&pHeap->free_list); // If growing didn't help, something is wrong (or someone else took them all REALLY fast)
153 void lf_heap_free(lf_heap* pHeap, void* p)
155 if (!p) // Allow for NULL to pass safely
157 lf_stack_push(&pHeap->free_list, (lf_node*)p); // Return the block to the free list
160 ///////////////////////////////////////////////////////////////////////////
162 ///////////////////////////////////////////////////////////////////////////
163 void lf_queue_init(lf_queue* pQueue)
166 lf_heap_init(&pQueue->node_heap, sizeof(lf_queue_node)); // Intialize the node heap
167 lf_queue_node* pNode = lf_queue_new_node(pQueue); // Create the 'empty' node
168 pNode->next.ptr = NULL;
169 pNode->value = (void*)0xdeadf00d;
170 pQueue->head.ptr = pQueue->tail.ptr = pNode;
173 void lf_queue_deinit(lf_queue* pQueue)
175 lf_heap_deinit(&pQueue->node_heap); // Clean up the node heap
178 // TODO: template-ize
179 void lf_queue_enqueue(lf_queue* pQueue, void* value)
181 lf_queue_node* pNode = lf_queue_new_node(pQueue); // Get a container
182 pNode->value = value;
183 pNode->next.ptr = NULL;
184 atomic_ptr tail, next, node;
188 next = ((lf_queue_node*)tail.ptr)->next;
189 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
190 if (atomic_ptr_to_long(tail) == atomic_ptr_to_long(pQueue->tail)) // Check consistency
192 if (atomic_ptr_to_long_long(tail) == atomic_ptr_to_long_long(pQueue->tail)) // Check consistency
195 if (next.ptr == NULL) // Was tail pointing to the last node?
198 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
199 if (cas((long*)&((lf_queue_node*)tail.ptr)->next, atomic_ptr_to_long(next), atomic_ptr_to_long(node)) == atomic_ptr_to_long(next)) // Try to link node at end
201 node.version = next.version + 1;
202 if (cas2((long long*)&((lf_queue_node*)tail.ptr)->next, atomic_ptr_to_long_long(next), atomic_ptr_to_long_long(node)) == atomic_ptr_to_long_long(next)) // Try to link node at end
204 break; // enqueue is done.
206 else // tail was lagging, try to help...
209 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
210 cas((long*)&pQueue->tail, atomic_ptr_to_long(tail), atomic_ptr_to_long(node)); // We don't care if we are successful or not
212 node.version = tail.version + 1;
213 cas2((long long*)&pQueue->tail, atomic_ptr_to_long_long(tail), atomic_ptr_to_long_long(node)); // We don't care if we are successful or not
217 } while (true); // Keep trying until the enqueue is done
219 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
220 cas((long*)&pQueue->tail, atomic_ptr_to_long(tail), atomic_ptr_to_long(node)); // Try to swing the tail to the new node
222 node.version = tail.version + 1;
223 cas2((long long*)&pQueue->tail, atomic_ptr_to_long_long(tail), atomic_ptr_to_long_long(node)); // Try to swing the tail to the new node
225 AtomicIncrement(&pQueue->len);
228 // TODO: template-ize
229 void* lf_queue_dequeue(lf_queue* pQueue)
231 atomic_ptr head, tail, next, node;
237 next = ((lf_queue_node*)head.ptr)->next;
238 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
239 if (atomic_ptr_to_long(head) == atomic_ptr_to_long(pQueue->head)) // Check consistency
241 if (atomic_ptr_to_long_long(head) == atomic_ptr_to_long_long(pQueue->head)) // Check consistency
244 if (head.ptr == tail.ptr) // Queue is empty or tail is lagging
246 if (next.ptr == NULL) // Queue is empty
249 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
250 cas((long*)&pQueue->tail, atomic_ptr_to_long(tail), atomic_ptr_to_long(node)); // Tail is lagging. Try to advance it.
252 node.version = tail.version + 1;
253 cas2((long long*)&pQueue->tail, atomic_ptr_to_long_long(tail), atomic_ptr_to_long_long(node)); // Tail is lagging. Try to advance it.
256 else // Tail is consistent. No need to deal with it.
258 pVal = ((lf_queue_node*)next.ptr)->value;
260 #if defined(__ppc__) || defined(__powerpc__) || defined(__arm__)
261 if (cas((long*)&pQueue->head, atomic_ptr_to_long(head), atomic_ptr_to_long(node)) == atomic_ptr_to_long(head))
263 node.version = head.version + 1;
264 if (cas2((long long*)&pQueue->head, atomic_ptr_to_long_long(head), atomic_ptr_to_long_long(node)) == atomic_ptr_to_long_long(head))
266 break; // Dequeue is done
269 } while (true); // Keep trying until the dequeue is done or the queue empties
270 lf_queue_free_node(pQueue, head.ptr);
271 AtomicDecrement(&pQueue->len);
276 #pragma GCC optimization_level reset