summaryrefslogtreecommitdiff
path: root/src/common/ers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/ers.c')
-rw-r--r--src/common/ers.c115
1 files changed, 60 insertions, 55 deletions
diff --git a/src/common/ers.c b/src/common/ers.c
index 22269a51f..c8a11d2a9 100644
--- a/src/common/ers.c
+++ b/src/common/ers.c
@@ -13,16 +13,16 @@
* If it has reusable entries (freed entry), it uses one. *
* So no assumption should be made about the data of the entry. *
* Entries should be freed in the manager they where allocated from. *
- * Failure to do so can lead to unexpected behaviours. *
+ * Failure to do so can lead to unexpected behaviors. *
* *
* <H2>Advantages:</H2> *
* - The same manager is used for entries of the same size. *
* So entries freed in one instance of the manager can be used by other *
* instances of the manager. *
* - Much less memory allocation/deallocation - program will be faster. *
- * - Avoids memory fragmentaion - program will run better for longer. *
+ * - Avoids memory fragmentation - program will run better for longer. *
* *
- * <H2>Disavantages:</H2> *
+ * <H2>Disadvantages:</H2> *
* - Unused entries are almost inevitable - memory being wasted. *
* - A manager will only auto-destroy when all of its instances are *
* destroyed so memory will usually only be recovered near the end. *
@@ -39,12 +39,18 @@
* @encoding US-ASCII *
* @see common#ers.h *
\*****************************************************************************/
+
+#define HERCULES_CORE
+
+#include "ers.h"
+
#include <stdlib.h>
+#include <string.h>
#include "../common/cbasetypes.h"
#include "../common/malloc.h" // CREATE, RECREATE, aMalloc, aFree
+#include "../common/nullpo.h"
#include "../common/showmsg.h" // ShowMessage, ShowError, ShowFatalError, CL_BOLD, CL_NORMAL
-#include "ers.h"
#ifndef DISABLE_ERS
@@ -72,7 +78,7 @@ typedef struct ers_cache
// Memory blocks array
unsigned char **Blocks;
- // Max number of blocks
+ // Max number of blocks
unsigned int Max;
// Free objects count
@@ -87,6 +93,9 @@ typedef struct ers_cache
// Default = ERS_BLOCK_ENTRIES, can be adjusted for performance for individual cache sizes.
unsigned int ChunkSize;
+ // Misc options, some options are shared from the instance
+ enum ERSOptions Options;
+
// Linked list
struct ers_cache *Next, *Prev;
} ers_cache_t;
@@ -95,7 +104,7 @@ struct ers_instance_t {
// Interface to ERS
struct eri VTable;
- // Name, used for debbuging purpouses
+ // Name, used for debugging purposes
char *Name;
// Misc options
@@ -110,24 +119,23 @@ struct ers_instance_t {
#ifdef DEBUG
/* for data analysis [Ind/Hercules] */
unsigned int Peak;
- struct ers_instance_t *Next, *Prev;
#endif
-
+ struct ers_instance_t *Next, *Prev;
};
// Array containing a pointer for all ers_cache structures
static ers_cache_t *CacheList = NULL;
-#ifdef DEBUG
- static struct ers_instance_t *InstanceList = NULL;
-#endif
+static struct ers_instance_t *InstanceList = NULL;
-static ers_cache_t *ers_find_cache(unsigned int size)
-{
+/**
+ * @param Options the options from the instance seeking a cache, we use it to give it a cache with matching configuration
+ **/
+static ers_cache_t *ers_find_cache(unsigned int size, enum ERSOptions Options) {
ers_cache_t *cache;
for (cache = CacheList; cache; cache = cache->Next)
- if (cache->ObjectSize == size)
+ if ( cache->ObjectSize == size && cache->Options == ( Options & ERS_CACHE_OPTIONS ) )
return cache;
CREATE(cache, ers_cache_t, 1);
@@ -140,6 +148,7 @@ static ers_cache_t *ers_find_cache(unsigned int size)
cache->UsedObjs = 0;
cache->Max = 0;
cache->ChunkSize = ERS_BLOCK_ENTRIES;
+ cache->Options = (Options & ERS_CACHE_OPTIONS);
if (CacheList == NULL)
{
@@ -172,34 +181,28 @@ static void ers_free_cache(ers_cache_t *cache, bool remove)
CacheList = cache->Next;
aFree(cache->Blocks);
+
aFree(cache);
}
-static void *ers_obj_alloc_entry(ERS self)
+static void *ers_obj_alloc_entry(ERS *self)
{
struct ers_instance_t *instance = (struct ers_instance_t *)self;
void *ret;
- if (instance == NULL)
- {
+ if (instance == NULL) {
ShowError("ers_obj_alloc_entry: NULL object, aborting entry freeing.\n");
return NULL;
}
- if (instance->Cache->ReuseList != NULL)
- {
+ if (instance->Cache->ReuseList != NULL) {
ret = (void *)((unsigned char *)instance->Cache->ReuseList + sizeof(struct ers_list));
instance->Cache->ReuseList = instance->Cache->ReuseList->Next;
- }
- else if (instance->Cache->Free > 0)
- {
+ } else if (instance->Cache->Free > 0) {
instance->Cache->Free--;
ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
- }
- else
- {
- if (instance->Cache->Used == instance->Cache->Max)
- {
+ } else {
+ if (instance->Cache->Used == instance->Cache->Max) {
instance->Cache->Max = (instance->Cache->Max * 4) + 3;
RECREATE(instance->Cache->Blocks, unsigned char *, instance->Cache->Max);
}
@@ -222,47 +225,45 @@ static void *ers_obj_alloc_entry(ERS self)
return ret;
}
-static void ers_obj_free_entry(ERS self, void *entry)
+static void ers_obj_free_entry(ERS *self, void *entry)
{
struct ers_instance_t *instance = (struct ers_instance_t *)self;
struct ers_list *reuse = (struct ers_list *)((unsigned char *)entry - sizeof(struct ers_list));
- if (instance == NULL)
- {
+ if (instance == NULL) {
ShowError("ers_obj_free_entry: NULL object, aborting entry freeing.\n");
return;
- }
- else if (entry == NULL)
- {
+ } else if (entry == NULL) {
ShowError("ers_obj_free_entry: NULL entry, nothing to free.\n");
return;
}
+ if( instance->Cache->Options & ERS_OPT_CLEAN )
+ memset((unsigned char*)reuse + sizeof(struct ers_list), 0, instance->Cache->ObjectSize - sizeof(struct ers_list));
+
reuse->Next = instance->Cache->ReuseList;
instance->Cache->ReuseList = reuse;
instance->Count--;
instance->Cache->UsedObjs--;
}
-static size_t ers_obj_entry_size(ERS self)
+static size_t ers_obj_entry_size(ERS *self)
{
struct ers_instance_t *instance = (struct ers_instance_t *)self;
- if (instance == NULL)
- {
+ if (instance == NULL) {
ShowError("ers_obj_entry_size: NULL object, aborting entry freeing.\n");
return 0;
- }
+ }
return instance->Cache->ObjectSize;
}
-static void ers_obj_destroy(ERS self)
+static void ers_obj_destroy(ERS *self)
{
struct ers_instance_t *instance = (struct ers_instance_t *)self;
- if (instance == NULL)
- {
+ if (instance == NULL) {
ShowError("ers_obj_destroy: NULL object, aborting entry freeing.\n");
return;
}
@@ -274,7 +275,6 @@ static void ers_obj_destroy(ERS self)
if (--instance->Cache->ReferenceCount <= 0)
ers_free_cache(instance->Cache, true);
-#ifdef DEBUG
if (instance->Next)
instance->Next->Prev = instance->Prev;
@@ -282,7 +282,6 @@ static void ers_obj_destroy(ERS self)
instance->Prev->Next = instance->Next;
else
InstanceList = instance->Next;
-#endif
if( instance->Options & ERS_OPT_FREE_NAME )
aFree(instance->Name);
@@ -290,19 +289,20 @@ static void ers_obj_destroy(ERS self)
aFree(instance);
}
-void ers_cache_size(ERS self, unsigned int new_size) {
+void ers_cache_size(ERS *self, unsigned int new_size) {
struct ers_instance_t *instance = (struct ers_instance_t *)self;
- if (instance == NULL) {//change as per piotrhalaczkiewicz comment
- ShowError("ers_cache_size: NULL object, skipping...\n");
- return;
+ nullpo_retv(instance);
+
+ if( !(instance->Cache->Options&ERS_OPT_FLEX_CHUNK) ) {
+ ShowWarning("ers_cache_size: '%s' has adjusted its chunk size to '%d', however ERS_OPT_FLEX_CHUNK is missing!\n",instance->Name,new_size);
}
instance->Cache->ChunkSize = new_size;
}
-ERS ers_new(uint32 size, char *name, enum ERSOptions options)
+ERS *ers_new(uint32 size, char *name, enum ERSOptions options)
{
struct ers_instance_t *instance;
CREATE(instance,struct ers_instance_t, 1);
@@ -320,9 +320,10 @@ ERS ers_new(uint32 size, char *name, enum ERSOptions options)
instance->Name = ( options & ERS_OPT_FREE_NAME ) ? aStrdup(name) : name;
instance->Options = options;
- instance->Cache = ers_find_cache(size);
+ instance->Cache = ers_find_cache(size,instance->Options);
+
instance->Cache->ReferenceCount++;
-#ifdef DEBUG
+
if (InstanceList == NULL) {
InstanceList = instance;
} else {
@@ -331,7 +332,6 @@ ERS ers_new(uint32 size, char *name, enum ERSOptions options)
InstanceList = instance;
InstanceList->Prev = NULL;
}
-#endif
instance->Count = 0;
@@ -379,12 +379,17 @@ void ers_report(void) {
ShowInfo("ers_report: '"CL_WHITE"%u"CL_NORMAL"' blocks total, consuming '"CL_WHITE"%.2f MB"CL_NORMAL"' \n",blocks_a,(double)((memory_t)/1024)/1024);
}
-void ers_force_destroy_all(void)
-{
- ers_cache_t *cache;
+/**
+ * Call on shutdown to clear remaining entries
+ **/
+void ers_final(void) {
+ struct ers_instance_t *instance = InstanceList, *next;
- for (cache = CacheList; cache; cache = cache->Next)
- ers_free_cache(cache, false);
+ while( instance ) {
+ next = instance->Next;
+ ers_obj_destroy((ERS*)instance);
+ instance = next;
+ }
}
#endif