summaryrefslogtreecommitdiff
path: root/src/common/mempool.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/mempool.c')
-rw-r--r--src/common/mempool.c568
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()
-