summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/Makefile.in2
-rw-r--r--src/common/atomic.h3
-rw-r--r--src/common/core.c5
-rw-r--r--src/common/mempool.c562
-rw-r--r--src/common/mempool.h100
-rw-r--r--src/common/mutex.c2
-rw-r--r--src/common/mutex.h3
-rw-r--r--src/common/raconf.c584
-rw-r--r--src/common/raconf.h59
-rw-r--r--src/common/thread.c7
-rw-r--r--src/common/thread.h3
12 files changed, 1333 insertions, 3 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 46c32c236..b32c33611 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -79,6 +79,9 @@ set( COMMON_BASE_HEADERS
"${COMMON_SOURCE_DIR}/atomic.h"
"${COMMON_SOURCE_DIR}/spinlock.h"
"${COMMON_SOURCE_DIR}/thread.h"
+ "${COMMON_SOURCE_DIR}/mutex.h"
+ "${COMMON_SOURCE_DIR}/raconf.h"
+ "${COMMON_SOURCE_DIR}/mempool.h"
${LIBCONFIG_HEADERS} # needed by conf.h/showmsg.h
CACHE INTERNAL "common_base headers" )
set( COMMON_BASE_SOURCES
@@ -100,6 +103,9 @@ set( COMMON_BASE_SOURCES
"${COMMON_SOURCE_DIR}/timer.c"
"${COMMON_SOURCE_DIR}/utils.c"
"${COMMON_SOURCE_DIR}/thread.c"
+ "${COMMON_SOURCE_DIR}/mutex.c"
+ "${COMMON_SOURCE_DIR}/mempool.c"
+ "${COMMON_SOURCE_DIR}/raconf.c"
${LIBCONFIG_SOURCES} # needed by conf.c/showmsg.c
CACHE INTERNAL "common_base sources" )
set( COMMON_BASE_INCLUDE_DIRS
diff --git a/src/common/Makefile.in b/src/common/Makefile.in
index 3f03982d7..b6713b6a1 100644
--- a/src/common/Makefile.in
+++ b/src/common/Makefile.in
@@ -3,7 +3,7 @@ COMMON_OBJ = obj_all/core.o obj_all/socket.o obj_all/timer.o obj_all/db.o obj_al
obj_all/nullpo.o obj_all/malloc.o obj_all/showmsg.o obj_all/strlib.o obj_all/utils.o \
obj_all/grfio.o obj_all/mapindex.o obj_all/ers.o obj_all/md5calc.o \
obj_all/minicore.o obj_all/minisocket.o obj_all/minimalloc.o obj_all/random.o obj_all/des.o \
- obj_all/conf.o obj_all/thread.o obj_all/mutex.o
+ obj_all/conf.o obj_all/thread.o obj_all/mutex.o obj_all/raconf.o obj_all/mempool.o
COMMON_H = $(shell ls ../common/*.h)
diff --git a/src/common/atomic.h b/src/common/atomic.h
index 7a9e8c4cc..c09d8d386 100644
--- a/src/common/atomic.h
+++ b/src/common/atomic.h
@@ -1,3 +1,6 @@
+// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
#ifndef _rA_ATOMIC_H_
#define _rA_ATOMIC_H_
diff --git a/src/common/core.c b/src/common/core.c
index 4e276fcdc..2923b4de7 100644
--- a/src/common/core.c
+++ b/src/common/core.c
@@ -10,6 +10,7 @@
#include "../common/socket.h"
#include "../common/timer.h"
#include "../common/thread.h"
+#include "../common/mempool.h"
#endif
#include <stdio.h>
@@ -280,7 +281,7 @@ int main (int argc, char **argv)
usercheck();
rathread_init();
-
+ mempool_init();
db_init();
signals_init();
@@ -306,7 +307,7 @@ int main (int argc, char **argv)
timer_final();
socket_final();
db_final();
-
+ mempool_final();
rathread_final();
#endif
diff --git a/src/common/mempool.c b/src/common/mempool.c
new file mode 100644
index 000000000..ab401f5e0
--- /dev/null
+++ b/src/common/mempool.c
@@ -0,0 +1,562 @@
+
+//
+// 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(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, 512*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;
+
+ 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()
+
diff --git a/src/common/mempool.h b/src/common/mempool.h
new file mode 100644
index 000000000..aeaebe7fe
--- /dev/null
+++ b/src/common/mempool.h
@@ -0,0 +1,100 @@
+#ifndef _rA_MEMPOOL_H_
+#define _rA_MEMPOOL_H_
+
+#include "../common/cbasetypes.h"
+
+typedef struct mempool *mempool;
+
+typedef void (*memPoolOnNodeAllocationProc)(void *ptr);
+typedef void (*memPoolOnNodeDeallocationProc)(void *ptr);
+
+typedef struct mempool_stats{
+ int64 num_nodes_total;
+ int64 num_nodes_free;
+ int64 num_nodes_used;
+
+ int64 num_segments;
+ int64 num_realloc_events;
+
+ int64 peak_nodes_used;
+
+ int64 num_bytes_total;
+} mempool_stats;
+
+
+//
+void mempool_init();
+void mempool_final();
+
+
+/**
+ * Creates a new Mempool
+ *
+ * @param name - Name of the pool (used for debug / error messages)
+ * @param elem_size - size of each element
+ * @param initial_count - preallocation count
+ * @param realloc_count - #no of nodes being allocated when pool is running empty.
+ * @param onNodeAlloc - Node Allocation callback (see @note!)
+ * @param onNodeDealloc - Node Deallocation callback (see @note!)
+ *
+ * @note:
+ * The onNode(De)alloc callbacks are only called once during segment allocation
+ * (pool initialization / rallocation )
+ * you can use this callbacks for example to initlaize a mutex or somethingelse
+ * you definitly need during runtime
+ *
+ * @return not NULL
+ */
+mempool mempool_create(const char *name,
+ uint64 elem_size,
+ uint64 initial_count,
+ uint64 realloc_count,
+
+ memPoolOnNodeAllocationProc onNodeAlloc,
+ memPoolOnNodeDeallocationProc onNodeDealloc);
+
+
+/**
+ * Destroys a Mempool
+ *
+ * @param pool - the mempool to destroy
+ *
+ * @note:
+ * Everything gets deallocated, regardless if everything was freed properly!
+ * So you have to ensure that all references are cleared properly!
+ */
+void mempool_destroy(mempool pool);
+
+
+/**
+ * Gets a new / empty node from the given mempool.
+ *
+ * @param pool - the pool to get an empty node from.
+ *
+ * @return Address of empty Node
+ */
+void *mempool_node_get(mempool pool);
+
+
+/**
+ * Returns the given node to the given mempool
+ *
+ * @param pool - the pool to put the node, to
+ * @param node - the node to return
+ */
+void mempool_node_put(mempool pool, void *node);
+
+
+/**
+ * Returns Statistics for the given mempool
+ *
+ * @param pool - the pool to get thats for
+ *
+ * @note: i dont like pushing masses of values over the stack, too - but its lazy and okay for stats. (blacksirius)
+ *
+ * @return stats struct
+ */
+mempool_stats mempool_get_stats(mempool pool);
+
+
+#endif
diff --git a/src/common/mutex.c b/src/common/mutex.c
index 874b81fa2..367574248 100644
--- a/src/common/mutex.c
+++ b/src/common/mutex.c
@@ -1,3 +1,5 @@
+// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
#ifdef WIN32
#include "../common/winapi.h"
diff --git a/src/common/mutex.h b/src/common/mutex.h
index 4a32bcc8a..1999627cd 100644
--- a/src/common/mutex.h
+++ b/src/common/mutex.h
@@ -1,3 +1,6 @@
+// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
#ifndef _rA_MUTEX_H_
#define _rA_MUTEX_H_
diff --git a/src/common/raconf.c b/src/common/raconf.c
new file mode 100644
index 000000000..2703560ff
--- /dev/null
+++ b/src/common/raconf.c
@@ -0,0 +1,584 @@
+//
+// Athena style config parser
+// (would be better to have "one" implementation instead of .. 4 :)
+//
+//
+// 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 "../common/cbasetypes.h"
+#include "../common/showmsg.h"
+#include "../common/db.h"
+#include "../common/malloc.h"
+
+#include "../common/raconf.h"
+
+#define SECTION_LEN 32
+#define VARNAME_LEN 64
+
+struct raconf {
+ DBMap *db;
+};
+
+
+struct conf_value{
+ int64 intval;
+ bool bval;
+ double floatval;
+ size_t strval_len; // not includung \0
+ char strval[16];
+};
+
+
+
+static struct conf_value *makeValue(const char *key, char *val, size_t val_len){
+ struct conf_value *v;
+ char *p;
+ size_t sz;
+
+ sz = sizeof(struct conf_value);
+ if(val_len >= sizeof(v->strval))
+ sz += (val_len - sizeof(v->strval) + 1);
+
+ v = (struct conf_value*)aCalloc(1, sizeof(struct conf_value));
+ if(v == NULL){
+ ShowFatalError("raconf: makeValue => Out of Memory while allocating new node.\n");
+ return NULL;
+ }
+
+ memcpy(v->strval, val, val_len);
+ v->strval[val_len+1] = '\0';
+ v->strval_len = val_len;
+
+
+ // Parse boolean value:
+ if((val_len == 4) && (strncmpi("true", val, 4) == 0))
+ v->bval = true;
+ else if((val_len == 3) && (strncmpi("yes", val, 3) == 0))
+ v->bval = true;
+ else if((val_len == 3) && (strncmpi("oui", val, 3) == 0))
+ v->bval = true;
+ else if((val_len == 2) && (strncmpi("si", val, 2) == 0))
+ v->bval = true;
+ else if((val_len == 2) && (strncmpi("ja", val, 2) == 0))
+ v->bval = true;
+ else if((val_len == 1) && (*val == '1'))
+ v->bval = true;
+ else if((val_len == 5) && (strncmpi("false", val, 5) == 0))
+ v->bval = false;
+ else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
+ v->bval = false;
+ else if((val_len == 3) && (strncmpi("non", val, 3) == 0))
+ v->bval = false;
+ else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
+ v->bval = false;
+ else if((val_len == 4) && (strncmpi("nein", val, 4) == 0))
+ v->bval = false;
+ else if((val_len == 1) && (*val == '0'))
+ v->bval = false;
+ else
+ v->bval = false; // assume false.
+
+ // Parse number
+ // Supported formats:
+ // prefix: 0x hex .
+ // postix: h for hex
+ // b for bin (dual)
+ if( (val_len >= 1 && (val[val_len] == 'h')) || (val_len >= 2 && (val[0] == '0' && val[1] == 'x')) ){//HEX!
+ if(val[val_len] == 'h'){
+ val[val_len]= '\0';
+ v->intval = strtoull(val, NULL, 16);
+ val[val_len] = 'h';
+ }else
+ v->intval = strtoull(&val[2], NULL, 16);
+ }else if( val_len >= 1 && (val[val_len] == 'b') ){ //BIN
+ val[val_len] = '\0';
+ v->intval = strtoull(val, NULL, 2);
+ val[val_len] = 'b';
+ }else if( *val >='0' && *val <= '9'){ // begins with normal digit, so assume its dec.
+ // is it float?
+ bool is_float = false;
+
+ for(p = val; *p != '\0'; p++){
+ if(*p == '.'){
+ v->floatval = strtod(val, NULL);
+ v->intval = (int64) v->floatval;
+ is_float = true;
+ break;
+ }
+ }
+
+ if(is_float == false){
+ v->intval = strtoull(val, NULL, 10);
+ v->floatval = (double) v->intval;
+ }
+ }else{
+ // Everything else: lets use boolean for fallback
+ if(v->bval == true)
+ v->intval = 1;
+ else
+ v->intval = 0;
+ }
+
+ return v;
+}//end: makeValue()
+
+
+static bool configParse(raconf inst, const char *fileName){
+ FILE *fp;
+ char line[4096];
+ char currentSection[SECTION_LEN];
+ char *p;
+ char c;
+ int linecnt;
+ size_t linelen;
+ size_t currentSection_len;
+
+ fp = fopen(fileName, "r");
+ if(fp == NULL){
+ ShowError("configParse: cannot open '%s' for reading.\n", fileName);
+ return false;
+ }
+
+
+ // Start with empty section:
+ currentSection[0] = '\0';
+ currentSection_len = 0;
+
+ //
+ linecnt = 0;
+ while(1){
+ linecnt++;
+
+ if(fgets(line, sizeof(line), fp) != line)
+ break;
+
+ linelen = strlen(line);
+ p = line;
+
+ // Skip whitespaces from beginning (space and tab)
+ _line_begin_skip_whities:
+ c = *p;
+ if(c == ' ' || c == '\t'){
+ p++;
+ linelen--;
+ goto _line_begin_skip_whities;
+ }
+
+ // Remove linebreaks as (cr or lf) and whitespaces from line end!
+ _line_end_skip_whities_and_breaks:
+ c = p[linelen-1];
+ if(c == '\r' || c == '\n' || c == ' ' || c == '\t'){
+ p[--linelen] = '\0';
+ goto _line_end_skip_whities_and_breaks;
+ }
+
+ // Empty line?
+ // or line starts with comment (commented out)?
+ if(linelen == 0 || (p[0] == '/' && p[1] == '/') || p[0] == ';')
+ continue;
+
+ // Variable names can contain:
+ // A-Za-z-_.0-9
+ //
+ // Sections start with [ .. ] (INI Style)
+ //
+ c = *p;
+
+ // check what we have.. :)
+ if(c == '['){ // got section!
+ // Got Section!
+ // Search for ]
+ char *start = (p+1);
+
+ while(1){
+ ++p;
+ c = *p;
+
+ if(c == '\0'){
+ ShowError("Syntax Error: unterminated Section name in %s:%u (expected ']')\n", fileName, linecnt);
+ fclose(fp);
+ return false;
+ }else if(c == ']'){ // closing backet (section name termination)
+ if( (p - start + 1) > (sizeof(currentSection) ) ){
+ ShowError("Syntax Error: Section name in %s:%u is too large (max Supported length: %u chars)\n", fileName, linecnt, sizeof(currentSection)-1);
+ fclose(fp);
+ return false;
+ }
+
+ // Set section!
+ *p = '\0'; // add termination here.
+ memcpy(currentSection, start, (p-start)+1 ); // we'll copy \0, too! (we replaced the ] backet with \0.)
+ currentSection_len = (p-start);
+
+ break;
+
+ }else if( (c >= '0' && c <= '9') || (c == '-') || (c == ' ') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
+ // skip .. (allowed char / specifier)
+ continue;
+ }else{
+ ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Section name.\n", c, fileName, linecnt, (p-line));
+ fclose(fp);
+ return false;
+ }
+
+ }//endwhile: parse section name
+
+
+ }else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
+ // Got variable!
+ // Search for '=' or ':' wich termiantes the name
+ char *start = p;
+ char *valuestart = NULL;
+ size_t start_len;
+
+ while(1){
+ ++p;
+ c = *p;
+
+ if(c == '\0'){
+ ShowError("Syntax Error: unterminated Variable name in %s:%u\n", fileName, linecnt);
+ fclose(fp);
+ return false;
+ }else if( (c == '=') || (c == ':') ){
+ // got name termination
+
+ *p = '\0'; // Terminate it so (start) will hold the pointer to the name.
+
+ break;
+
+ }else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
+ // skip .. allowed char
+ continue;
+ }else{
+ ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Variable name.\n", c, fileName, linecnt, (p-line));
+ fclose(fp);
+ return false;
+ }
+
+ }//endwhile: parse var name
+
+ start_len = (p-start);
+ if(start_len >= VARNAME_LEN){
+ ShowError("%s:%u Variable length exceeds limit of %u Characters.\n", fileName, linecnt, VARNAME_LEN-1);
+ fclose(fp);
+ return false;
+ }else if(start_len == 0){
+ ShowError("%s:%u Empty Variable name is not allowed.\n", fileName, linecnt);
+ fclose(fp);
+ return false;
+ }
+
+
+ valuestart = (p+1);
+
+
+ // Skip whitespace from begin of value (tab and space)
+ _skip_value_begin_whities:
+ c = *valuestart;
+ if(c == ' ' || c == '\t'){
+ valuestart++;
+ goto _skip_value_begin_whities;
+ }
+
+ // Scan for value termination,
+ // wich can be \0 or comment start (// or ; (INI) )
+ //
+ p = valuestart;
+ while(1){
+ c = *p;
+ if(c == '\0'){
+ // Terminated by line end.
+ break;
+ }else if(c == '/' && p[1] == '/'){
+ // terminated by c++ style comment.
+ *p = '\0';
+ break;
+ }else if(c == ';'){
+ // terminated by ini style comment.
+ *p = '\0';
+ break;
+ }
+
+ p++;
+ }//endwhile: search var value end.
+
+
+ // Strip whitespaces from end of value.
+ if(valuestart != p){ // not empty!
+ p--;
+ _strip_value_end_whities:
+ c = *p;
+ if(c == ' ' || c == '\t'){
+ *p = '\0';
+ p--;
+ goto _strip_value_end_whities;
+ }
+ p++;
+ }
+
+
+ // Buildin Hook:
+ if( stricmp(start, "import") == 0){
+ if( configParse(inst, valuestart) != true){
+ ShowError("%s:%u - Import of '%s' failed!\n", fileName, linecnt, valuestart);
+ }
+ }else{
+ // put it to db.
+ struct conf_value *v, *o;
+ char key[ (SECTION_LEN+VARNAME_LEN+1+1) ]; //+1 for delimiter, +1 for termination.
+ size_t section_len;
+
+ if(*currentSection == '\0'){ // empty / none
+ strncpy(key, "<unnamed>",9);
+ section_len = 9;
+ }else{
+ strncpy(key, currentSection, currentSection_len);
+ section_len = currentSection_len;
+ }
+
+ key[section_len] = '.'; // Delim
+
+ strncpy(&key[section_len+1], start, start_len);
+
+ key[section_len + start_len + 1] = '\0';
+
+
+ v = makeValue(key, valuestart, (p-valuestart) );
+
+ // Try to get the old one before
+ o = strdb_get(inst->db, key);
+ if(o != NULL){
+ strdb_remove(inst->db, key);
+ aFree(o); //
+ }
+
+ strdb_put( inst->db, key, v);
+ }
+
+
+ }else{
+ ShowError("Syntax Error: unexpected Character '%c' in %s:%u (offset %u)\n", c, fileName, linecnt, (p-line) );
+ fclose(fp);
+ return false;
+ }
+
+
+
+ }
+
+
+
+ fclose(fp);
+ return true;
+}//end: configParse()
+
+
+#define MAKEKEY(dest, section, key) { size_t section_len, key_len; \
+ if(section == NULL || *section == '\0'){ \
+ strncpy(dest, "<unnamed>", 9); \
+ section_len = 9; \
+ }else{ \
+ section_len = strlen(section); \
+ strncpy(dest, section, section_len); \
+ } \
+ \
+ dest[section_len] = '.'; \
+ \
+ key_len = strlen(key); \
+ strncpy(&dest[section_len+1], key, key_len); \
+ dest[section_len + key_len + 1] = '\0'; \
+ }
+
+
+raconf raconf_parse(const char *file_name){
+ struct raconf *rc;
+
+ rc = aCalloc(1, sizeof(struct raconf) );
+ if(rc == NULL){
+ ShowFatalError("raconf_parse: failed to allocate memory for new handle\n");
+ return NULL;
+ }
+
+ rc->db = strdb_alloc(DB_OPT_BASE | DB_OPT_DUP_KEY, 98);
+ //
+
+ if(configParse(rc, file_name) != true){
+ ShowError("Failed to Parse Configuration file '%s'\n", file_name);
+ }
+
+ return rc;
+}//end: raconf_parse()
+
+
+void raconf_destroy(raconf rc){
+ DBIterator *iter;
+ struct conf_value *v;
+
+ // Clear all entrys in db.
+ iter = db_iterator(rc->db);
+ for( v = (struct conf_value*)dbi_first(iter); dbi_exists(iter); v = (struct conf_value*)dbi_next(iter) ){
+ aFree(v);
+ }
+ dbi_destroy(iter);
+
+ db_destroy(rc->db);
+
+ aFree(rc);
+
+}//end: raconf_destroy()
+
+bool raconf_getbool(raconf rc, const char *section, const char *key, bool _default){
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
+ struct conf_value *v;
+
+ MAKEKEY(keystr, section, key);
+
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL)
+ return _default;
+ else
+ return v->bval;
+}//end: raconf_getbool()
+
+
+float raconf_getfloat(raconf rc,const char *section, const char *key, float _default){
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
+ struct conf_value *v;
+
+ MAKEKEY(keystr, section, key);
+
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL)
+ return _default;
+ else
+ return (float)v->floatval;
+}//end: raconf_getfloat()
+
+
+int64 raconf_getint(raconf rc, const char *section, const char *key, int64 _default){
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
+ struct conf_value *v;
+
+ MAKEKEY(keystr, section, key);
+
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL)
+ return _default;
+ else
+ return v->intval;
+
+}//end: raconf_getint()
+
+
+const char* raconf_getstr(raconf rc, const char *section, const char *key, const char *_default){
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
+ struct conf_value *v;
+
+ MAKEKEY(keystr, section, key);
+
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL)
+ return _default;
+ else
+ return v->strval;
+}//end: raconf_getstr()
+
+
+bool raconf_getboolEx(raconf rc, const char *section, const char *fallback_section, const char *key, bool _default){
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
+ struct conf_value *v;
+
+ MAKEKEY(keystr, section, key);
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL){
+
+ MAKEKEY(keystr, fallback_section, key);
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL){
+ return _default;
+ }else{
+ return v->bval;
+ }
+
+ }else{
+ return v->bval;
+ }
+}//end: raconf_getboolEx()
+
+
+float raconf_getfloatEx(raconf rc,const char *section, const char *fallback_section, const char *key, float _default){
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
+ struct conf_value *v;
+
+ MAKEKEY(keystr, section, key);
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL){
+
+ MAKEKEY(keystr, fallback_section, key);
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL){
+ return _default;
+ }else{
+ return (float)v->floatval;
+ }
+
+ }else{
+ return (float)v->floatval;
+ }
+
+}//end: raconf_getfloatEx()
+
+
+int64 raconf_getintEx(raconf rc, const char *section, const char *fallback_section, const char *key, int64 _default){
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
+ struct conf_value *v;
+
+ MAKEKEY(keystr, section, key);
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL){
+
+ MAKEKEY(keystr, fallback_section, key);
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL){
+ return _default;
+ }else{
+ return v->intval;
+ }
+
+ }else{
+ return v->intval;
+ }
+
+}//end: raconf_getintEx()
+
+
+const char* raconf_getstrEx(raconf rc, const char *section, const char *fallback_section, const char *key, const char *_default){
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
+ struct conf_value *v;
+
+ MAKEKEY(keystr, section, key);
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL){
+
+ MAKEKEY(keystr, fallback_section, key);
+ v = strdb_get(rc->db, keystr);
+ if(v == NULL){
+ return _default;
+ }else{
+ return v->strval;
+ }
+
+ }else{
+ return v->strval;
+ }
+
+}//end: raconf_getstrEx()
diff --git a/src/common/raconf.h b/src/common/raconf.h
new file mode 100644
index 000000000..68a2b51b2
--- /dev/null
+++ b/src/common/raconf.h
@@ -0,0 +1,59 @@
+// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _rA_CONF_H_
+#define _rA_CONF_H_
+
+#include "../common/cbasetypes.h"
+
+// rAthena generic configuration file parser
+//
+// Config file Syntax is athena style
+// extended with ini style support (including sections)
+//
+// Comments are started with // or ; (ini style)
+//
+
+typedef struct raconf *raconf;
+
+
+/**
+ * Parses a rAthna Configuration file
+ *
+ * @param file_name path to the file to parse
+ *
+ * @returns not NULL incase of success
+ */
+raconf raconf_parse(const char *file_name);
+
+
+/**
+ * Frees a Handle received from raconf_parse
+ *
+ * @param rc - the handle to free
+ */
+void raconf_destroy(raconf rc);
+
+
+/**
+ * Gets the value for Section / Key pair, if key not exists returns _default!
+ *
+ */
+bool raconf_getbool(raconf rc, const char *section, const char *key, bool _default);
+float raconf_getfloat(raconf rc,const char *section, const char *key, float _default);
+int64 raconf_getint(raconf rc, const char *section, const char *key, int64 _default);
+const char* raconf_getstr(raconf rc, const char *section, const char *key, const char *_default);
+
+/**
+ * Gets the value for Section / Key pair, but has fallback section option if not found in section,
+ * if not found in both - default gets returned.
+ *
+ */
+bool raconf_getboolEx(raconf rc, const char *section, const char *fallback_section, const char *key, bool _default);
+float raconf_getfloatEx(raconf rc,const char *section, const char *fallback_section, const char *key, float _default);
+int64 raconf_getintEx(raconf rc, const char *section, const char *fallback_section, const char *key, int64 _default);
+const char* raconf_getstrEx(raconf rc, const char *section, const char *fallback_section, const char *key, const char *_default);
+
+
+
+#endif
diff --git a/src/common/thread.c b/src/common/thread.c
index 728c6c66a..baf4171da 100644
--- a/src/common/thread.c
+++ b/src/common/thread.c
@@ -1,3 +1,10 @@
+//
+// Basic Threading abstraction (for pthread / win32 based systems)
+//
+// 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
#ifdef WIN32
#include "../common/winapi.h"
diff --git a/src/common/thread.h b/src/common/thread.h
index 8d3441868..a5a66e954 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -1,3 +1,6 @@
+// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
#pragma once
#ifndef _rA_THREAD_H_
#define _rA_THREAD_H_