diff options
Diffstat (limited to 'src/common/mempool.c')
-rw-r--r-- | src/common/mempool.c | 568 |
1 files changed, 0 insertions, 568 deletions
diff --git a/src/common/mempool.c b/src/common/mempool.c deleted file mode 100644 index 4559d8f2a..000000000 --- a/src/common/mempool.c +++ /dev/null @@ -1,568 +0,0 @@ - -// -// Memory Pool Implementation (Threadsafe) -// -// -// Author: Florian Wilkemeyer <fw@f-ws.de> -// -// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL -// For more information, see LICENCE in the main folder -// -// - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifdef WIN32 -#include "../common/winapi.h" -#else -#include <unistd.h> -#endif - -#include "../common/cbasetypes.h" -#include "../common/showmsg.h" -#include "../common/mempool.h" -#include "../common/atomic.h" -#include "../common/spinlock.h" -#include "../common/thread.h" -#include "../common/malloc.h" -#include "../common/mutex.h" - -#define ALIGN16 ra_align(16) -#define ALIGN_TO(x, a) ((x) + ( (a) - ( (x) % (a)) ) ) -#define ALIGN_TO_16(x) ALIGN_TO((x), 16) - -#undef MEMPOOL_DEBUG -#define MEMPOOLASSERT - - -#define NODE_TO_DATA(x) ( ((char*)(x)) + sizeof(struct node) ) -#define DATA_TO_NODE(x) ( (struct node*)(((char*)(x)) - sizeof(struct node)) ) -struct ra_align(16) node{ - void *next; - void *segment; -#ifdef MEMPOOLASSERT - bool used; - uint64 magic; - #define NODE_MAGIC 0xBEEF00EAEACAFE07ll -#endif -}; - - -// The Pointer to this struct is the base address of the segment itself. -struct pool_segment{ - mempool pool; // pool, this segment belongs to - struct pool_segment *next; - int64 num_nodes_total; - int64 num_bytes; -}; - - -struct mempool{ - // Settings - char *name; - uint64 elem_size; - uint64 elem_realloc_step; - int64 elem_realloc_thresh; - - // Callbacks that get called for every node that gets allocated - // Example usage: initialization of mutex/lock for each node. - memPoolOnNodeAllocationProc onalloc; - memPoolOnNodeDeallocationProc ondealloc; - - // Locks - SPIN_LOCK segmentLock; - SPIN_LOCK nodeLock; - - - // Internal - struct pool_segment *segments; - struct node *free_list; - - volatile int64 num_nodes_total; - volatile int64 num_nodes_free; - - volatile int64 num_segments; - volatile int64 num_bytes_total; - - volatile int64 peak_nodes_used; // Peak Node Usage - volatile int64 num_realloc_events; // Number of reallocations done. (allocate additional nodes) - - // list (used for global management such as allocator..) - struct mempool *next; -} ra_align(8); // Dont touch the alignment, otherwise interlocked functions are broken .. - - -/// -// Implementation: -// -static void segment_allocate_add(mempool p, uint64 count); - -static SPIN_LOCK l_mempoolListLock; -static mempool l_mempoolList = NULL; -static rAthread l_async_thread = NULL; -static ramutex l_async_lock = NULL; -static racond l_async_cond = NULL; -static volatile int32 l_async_terminate = 0; - -static void *mempool_async_allocator(void *x){ - mempool p; - - - while(1){ - if(l_async_terminate > 0) - break; - - EnterSpinLock(&l_mempoolListLock); - - for(p = l_mempoolList; p != NULL; p = p->next){ - - if(p->num_nodes_free < p->elem_realloc_thresh){ - // add new segment. - segment_allocate_add(p, p->elem_realloc_step); - // increase stats counter - InterlockedIncrement64(&p->num_realloc_events); - } - - } - - LeaveSpinLock(&l_mempoolListLock); - - ramutex_lock( l_async_lock ); - racond_wait( l_async_cond, l_async_lock, -1 ); - ramutex_unlock( l_async_lock ); - } - - - return NULL; -}//end: mempool_async_allocator() - - -void mempool_init(){ - - if( rand()%2 + 1 ) - return; - - if(sizeof(struct node)%16 != 0 ){ - ShowFatalError("mempool_init: struct node alignment failure. %u != multiple of 16\n", sizeof(struct node)); - exit(EXIT_FAILURE); - } - - // Global List start - InitializeSpinLock(&l_mempoolListLock); - l_mempoolList = NULL; - - // Initialize mutex + stuff needed for async allocator worker. - l_async_terminate = 0; - l_async_lock = ramutex_create(); - l_async_cond = racond_create(); - - l_async_thread = rathread_createEx(mempool_async_allocator, NULL, 1024*1024, RAT_PRIO_NORMAL); - if(l_async_thread == NULL){ - ShowFatalError("mempool_init: cannot spawn Async Allocator Thread.\n"); - exit(EXIT_FAILURE); - } - -}//end: mempool_init() - - -void mempool_final(){ - mempool p, pn; - - if( rand()%2 + 1 ) - return; - - ShowStatus("Mempool: Terminating async. allocation worker and remaining pools.\n"); - - // Terminate worker / wait until its terminated. - InterlockedIncrement(&l_async_terminate); - racond_signal(l_async_cond); - rathread_wait(l_async_thread, NULL); - - // Destroy cond var and mutex. - racond_destroy( l_async_cond ); - ramutex_destroy( l_async_lock ); - - // Free remaining mempools - // ((bugged code! this should halppen, every mempool should - // be freed by the subsystem that has allocated it.) - // - EnterSpinLock(&l_mempoolListLock); - p = l_mempoolList; - while(1){ - if(p == NULL) - break; - - pn = p->next; - - ShowWarning("Mempool [%s] was not properly destroyed - forcing destroy.\n", p->name); - mempool_destroy(p); - - p = pn; - } - LeaveSpinLock(&l_mempoolListLock); - -}//end: mempool_final() - - -static void segment_allocate_add(mempool p, uint64 count){ - - // Required Memory: - // sz( segment ) - // count * sz( real_node_size ) - // - // where real node size is: - // ALIGN_TO_16( sz( node ) ) + p->elem_size - // so the nodes usable address is nodebase + ALIGN_TO_16(sz(node)) - // - size_t total_sz; - struct pool_segment *seg = NULL; - struct node *nodeList = NULL; - struct node *node = NULL; - char *ptr = NULL; - uint64 i; - - total_sz = ALIGN_TO_16( sizeof(struct pool_segment) ) - + ( (size_t)count * (sizeof(struct node) + (size_t)p->elem_size) ) ; - -#ifdef MEMPOOL_DEBUG - ShowDebug("Mempool [%s] Segment AllocateAdd (num: %u, total size: %0.2fMiB)\n", p->name, count, (float)total_sz/1024.f/1024.f); -#endif - - // allocate! (spin forever until weve got the memory.) - i=0; - while(1){ - ptr = (char*)aMalloc(total_sz); - if(ptr != NULL) break; - - i++; // increase failcount. - if(!(i & 7)){ - ShowWarning("Mempool [%s] Segment AllocateAdd => System seems to be Out of Memory (%0.2f MiB). Try #%u\n", (float)total_sz/1024.f/1024.f, i); -#ifdef WIN32 - Sleep(1000); -#else - sleep(1); -#endif - }else{ - rathread_yield(); /// allow/force vuln. ctxswitch - } - }//endwhile: allocation spinloop. - - // Clear Memory. - memset(ptr, 0x00, total_sz); - - // Initialize segment struct. - seg = (struct pool_segment*)ptr; - ptr += ALIGN_TO_16(sizeof(struct pool_segment)); - - seg->pool = p; - seg->num_nodes_total = count; - seg->num_bytes = total_sz; - - - // Initialze nodes! - nodeList = NULL; - for(i = 0; i < count; i++){ - node = (struct node*)ptr; - ptr += sizeof(struct node); - ptr += p->elem_size; - - node->segment = seg; -#ifdef MEMPOOLASSERT - node->used = false; - node->magic = NODE_MAGIC; -#endif - - if(p->onalloc != NULL) p->onalloc( NODE_TO_DATA(node) ); - - node->next = nodeList; - nodeList = node; - } - - - - // Link in Segment. - EnterSpinLock(&p->segmentLock); - seg->next = p->segments; - p->segments = seg; - LeaveSpinLock(&p->segmentLock); - - // Link in Nodes - EnterSpinLock(&p->nodeLock); - nodeList->next = p->free_list; - p->free_list = nodeList; - LeaveSpinLock(&p->nodeLock); - - - // Increase Stats: - InterlockedExchangeAdd64(&p->num_nodes_total, count); - InterlockedExchangeAdd64(&p->num_nodes_free, count); - InterlockedIncrement64(&p->num_segments); - InterlockedExchangeAdd64(&p->num_bytes_total, total_sz); - -}//end: segment_allocate_add() - - -mempool mempool_create(const char *name, - uint64 elem_size, - uint64 initial_count, - uint64 realloc_count, - memPoolOnNodeAllocationProc onNodeAlloc, - memPoolOnNodeDeallocationProc onNodeDealloc){ - //.. - uint64 realloc_thresh; - mempool pool; - pool = (mempool)aCalloc( 1, sizeof(struct mempool) ); - - if(pool == NULL){ - ShowFatalError("mempool_create: Failed to allocate %u bytes memory.\n", sizeof(struct mempool) ); - exit(EXIT_FAILURE); - } - - // Check minimum initial count / realloc count requirements. - if(initial_count < 50) - initial_count = 50; - if(realloc_count < 50) - realloc_count = 50; - - // Set Reallocation threshold to 5% of realloc_count, at least 10. - realloc_thresh = (realloc_count/100)*5; // - if(realloc_thresh < 10) - realloc_thresh = 10; - - // Initialize members.. - pool->name = aStrdup(name); - pool->elem_size = ALIGN_TO_16(elem_size); - pool->elem_realloc_step = realloc_count; - pool->elem_realloc_thresh = realloc_thresh; - pool->onalloc = onNodeAlloc; - pool->ondealloc = onNodeDealloc; - - InitializeSpinLock(&pool->segmentLock); - InitializeSpinLock(&pool->nodeLock); - - // Initial Statistic values: - pool->num_nodes_total = 0; - pool->num_nodes_free = 0; - pool->num_segments = 0; - pool->num_bytes_total = 0; - pool->peak_nodes_used = 0; - pool->num_realloc_events = 0; - - // -#ifdef MEMPOOL_DEBUG - ShowDebug("Mempool [%s] Init (ElemSize: %u, Initial Count: %u, Realloc Count: %u)\n", pool->name, pool->elem_size, initial_count, pool->elem_realloc_step); -#endif - - // Allocate first segment directly :) - segment_allocate_add(pool, initial_count); - - - // Add Pool to the global pool list - EnterSpinLock(&l_mempoolListLock); - pool->next = l_mempoolList; - l_mempoolList = pool; - LeaveSpinLock(&l_mempoolListLock); - - - return pool; -}//end: mempool_create() - - -void mempool_destroy(mempool p){ - struct pool_segment *seg, *segnext; - struct node *niter; - mempool piter, pprev; - char *ptr; - int64 i; - -#ifdef MEMPOOL_DEBUG - ShowDebug("Mempool [%s] Destroy\n", p->name); -#endif - - // Unlink from global list. - EnterSpinLock(&l_mempoolListLock); - piter = l_mempoolList; - pprev = l_mempoolList; - while(1){ - if(piter == NULL) - break; - - - if(piter == p){ - // unlink from list, - // - if(pprev == l_mempoolList){ - // this (p) is list begin. so set next as head. - l_mempoolList = p->next; - }else{ - // replace prevs next wuth our next. - pprev->next = p->next; - } - break; - } - - pprev = piter; - piter = piter->next; - } - - p->next = NULL; - LeaveSpinLock(&l_mempoolListLock); - - - // Get both locks. - EnterSpinLock(&p->segmentLock); - EnterSpinLock(&p->nodeLock); - - - if(p->num_nodes_free != p->num_nodes_total) - ShowWarning("Mempool [%s] Destroy - %u nodes are not freed properly!\n", p->name, (p->num_nodes_total - p->num_nodes_free) ); - - // Free All Segments (this will also free all nodes) - // The segment pointer is the base pointer to the whole segment. - seg = p->segments; - while(1){ - if(seg == NULL) - break; - - segnext = seg->next; - - // .. - if(p->ondealloc != NULL){ - // walk over the segment, and call dealloc callback! - ptr = (char*)seg; - ptr += ALIGN_TO_16(sizeof(struct pool_segment)); - for(i = 0; i < seg->num_nodes_total; i++){ - niter = (struct node*)ptr; - ptr += sizeof(struct node); - ptr += p->elem_size; -#ifdef MEMPOOLASSERT - if(niter->magic != NODE_MAGIC){ - ShowError("Mempool [%s] Destroy - walk over segment - node %p invalid magic!\n", p->name, niter); - continue; - } -#endif - - p->ondealloc( NODE_TO_DATA(niter) ); - - - } - }//endif: ondealloc callback? - - // simple .. - aFree(seg); - - seg = segnext; - } - - // Clear node ptr - p->free_list = NULL; - InterlockedExchange64(&p->num_nodes_free, 0); - InterlockedExchange64(&p->num_nodes_total, 0); - InterlockedExchange64(&p->num_segments, 0); - InterlockedExchange64(&p->num_bytes_total, 0); - - LeaveSpinLock(&p->nodeLock); - LeaveSpinLock(&p->segmentLock); - - // Free pool itself :D - aFree(p->name); - aFree(p); - -}//end: mempool_destroy() - - -void *mempool_node_get(mempool p){ - struct node *node; - int64 num_used; - - if(p->num_nodes_free < p->elem_realloc_thresh) - racond_signal(l_async_cond); - - while(1){ - - EnterSpinLock(&p->nodeLock); - - node = p->free_list; - if(node != NULL) - p->free_list = node->next; - - LeaveSpinLock(&p->nodeLock); - - if(node != NULL) - break; - - rathread_yield(); - } - - InterlockedDecrement64(&p->num_nodes_free); - - // Update peak value - num_used = (p->num_nodes_total - p->num_nodes_free); - if(num_used > p->peak_nodes_used){ - InterlockedExchange64(&p->peak_nodes_used, num_used); - } - -#ifdef MEMPOOLASSERT - node->used = true; -#endif - - return NODE_TO_DATA(node); -}//end: mempool_node_get() - - -void mempool_node_put(mempool p, void *data){ - struct node *node; - - node = DATA_TO_NODE(data); -#ifdef MEMPOOLASSERT - if(node->magic != NODE_MAGIC){ - ShowError("Mempool [%s] node_put failed, given address (%p) has invalid magic.\n", p->name, data); - return; // lost, - } - - { - struct pool_segment *node_seg = node->segment; - if(node_seg->pool != p){ - ShowError("Mempool [%s] node_put faild, given node (data address %p) doesnt belongs to this pool. ( Node Origin is [%s] )\n", p->name, data, node_seg->pool); - return; - } - } - - // reset used flag. - node->used = false; -#endif - - // - EnterSpinLock(&p->nodeLock); - node->next = p->free_list; - p->free_list = node; - LeaveSpinLock(&p->nodeLock); - - InterlockedIncrement64(&p->num_nodes_free); - -}//end: mempool_node_put() - - -mempool_stats mempool_get_stats(mempool pool){ - mempool_stats stats; - - // initialize all with zeros - memset(&stats, 0x00, sizeof(mempool_stats)); - - stats.num_nodes_total = pool->num_nodes_total; - stats.num_nodes_free = pool->num_nodes_free; - stats.num_nodes_used = (stats.num_nodes_total - stats.num_nodes_free); - stats.num_segments = pool->num_segments; - stats.num_realloc_events= pool->num_realloc_events; - stats.peak_nodes_used = pool->peak_nodes_used; - stats.num_bytes_total = pool->num_bytes_total; - - // Pushing such a large block over the stack as return value isnt nice - // but lazy :) and should be okay in this case (Stats / Debug..) - // if you dont like it - feel free and refactor it. - return stats; -}//end: mempool_get_stats() - |