ers.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*****************************************************************************\
  2. * Copyright (c) Athena Dev Teams - Licensed under GNU GPL *
  3. * For more information, see LICENCE in the main folder *
  4. * *
  5. * <H1>Entry Reusage System</H1> *
  6. * *
  7. * There are several root entry managers, each with a different entry size. *
  8. * Each manager will keep track of how many instances have been 'created'. *
  9. * They will only automatically destroy themselves after the last instance *
  10. * is destroyed. *
  11. * *
  12. * Entries can be allocated from the managers. *
  13. * If it has reusable entries (freed entry), it uses one. *
  14. * So no assumption should be made about the data of the entry. *
  15. * Entries should be freed in the manager they where allocated from. *
  16. * Failure to do so can lead to unexpected behaviours. *
  17. * *
  18. * <H2>Advantages:</H2> *
  19. * - The same manager is used for entries of the same size. *
  20. * So entries freed in one instance of the manager can be used by other *
  21. * instances of the manager. *
  22. * - Much less memory allocation/deallocation - program will be faster. *
  23. * - Avoids memory fragmentaion - program will run better for longer. *
  24. * *
  25. * <H2>Disavantages:</H2> *
  26. * - Unused entries are almost inevitable - memory being wasted. *
  27. * - A manager will only auto-destroy when all of its instances are *
  28. * destroyed so memory will usually only be recovered near the end. *
  29. * - Always wastes space for entries smaller than a pointer. *
  30. * *
  31. * WARNING: The system is not thread-safe at the moment. *
  32. * *
  33. * HISTORY: *
  34. * 0.1 - Initial version *
  35. * 1.0 - ERS Rework *
  36. * *
  37. * @version 1.0 - ERS Rework *
  38. * @author GreenBox @ rAthena Project *
  39. * @encoding US-ASCII *
  40. * @see common#ers.h *
  41. \*****************************************************************************/
  42. #include <stdlib.h>
  43. #include "../common/cbasetypes.h"
  44. #include "../common/malloc.h" // CREATE, RECREATE, aMalloc, aFree
  45. #include "../common/showmsg.h" // ShowMessage, ShowError, ShowFatalError, CL_BOLD, CL_NORMAL
  46. #include "ers.h"
  47. #ifndef DISABLE_ERS
  48. #define ERS_ROOT_SIZE 256
  49. #define ERS_BLOCK_ENTRIES 4096
  50. struct ers_list
  51. {
  52. struct ers_list *Next;
  53. };
  54. typedef struct ers_cache
  55. {
  56. // Allocated object size, including ers_list size
  57. unsigned int ObjectSize;
  58. // Number of ers_instances referencing this
  59. int ReferenceCount;
  60. // Reuse linked list
  61. struct ers_list *ReuseList;
  62. // Memory blocks array
  63. unsigned char **Blocks;
  64. // Max number of blocks
  65. unsigned int Max;
  66. // Free objects count
  67. unsigned int Free;
  68. // Used objects count
  69. unsigned int Used;
  70. // Linked list
  71. struct ers_cache *Next, *Prev;
  72. } ers_cache_t;
  73. typedef struct
  74. {
  75. // Interface to ERS
  76. struct eri VTable;
  77. // Name, used for debbuging purpouses
  78. char *Name;
  79. // Misc options
  80. enum ERSOptions Options;
  81. // Our cache
  82. ers_cache_t *Cache;
  83. // Count of objects in use, used for detecting memory leaks
  84. unsigned int Count;
  85. } ers_instance_t;
  86. // Array containing a pointer for all ers_cache structures
  87. static ers_cache_t *CacheList;
  88. static ers_cache_t *ers_find_cache(unsigned int size)
  89. {
  90. ers_cache_t *cache;
  91. for (cache = CacheList; cache; cache = cache->Next)
  92. if (cache->ObjectSize == size)
  93. return cache;
  94. CREATE(cache, ers_cache_t, 1);
  95. cache->ObjectSize = size;
  96. cache->ReferenceCount = 0;
  97. cache->ReuseList = NULL;
  98. cache->Blocks = NULL;
  99. cache->Free = 0;
  100. cache->Used = 0;
  101. cache->Max = 0;
  102. if (CacheList == NULL)
  103. {
  104. CacheList = cache;
  105. }
  106. else
  107. {
  108. CacheList->Next = cache;
  109. cache->Prev = CacheList;
  110. }
  111. return cache;
  112. }
  113. static void ers_free_cache(ers_cache_t *cache, bool remove)
  114. {
  115. unsigned int i;
  116. for (i = 0; i < cache->Used; i++)
  117. aFree(cache->Blocks[i]);
  118. if (cache->Prev)
  119. cache->Prev->Next = cache->Next;
  120. if (cache->Next)
  121. cache->Next->Prev = cache->Prev;
  122. if (CacheList == cache)
  123. CacheList = cache->Next;
  124. aFree(cache->Blocks);
  125. aFree(cache);
  126. }
  127. static void *ers_obj_alloc_entry(ERS self)
  128. {
  129. ers_instance_t *instance = (ers_instance_t *)self;
  130. void *ret;
  131. if (instance == NULL)
  132. {
  133. ShowError("ers_obj_alloc_entry: NULL object, aborting entry freeing.\n");
  134. return NULL;
  135. }
  136. if (instance->Cache->ReuseList != NULL)
  137. {
  138. ret = (void *)((unsigned char *)instance->Cache->ReuseList + sizeof(struct ers_list));
  139. instance->Cache->ReuseList = instance->Cache->ReuseList->Next;
  140. }
  141. else if (instance->Cache->Free > 0)
  142. {
  143. instance->Cache->Free--;
  144. ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
  145. }
  146. else
  147. {
  148. if (instance->Cache->Used == instance->Cache->Max)
  149. {
  150. instance->Cache->Max = (instance->Cache->Max * 4) + 3;
  151. RECREATE(instance->Cache->Blocks, unsigned char *, instance->Cache->Max);
  152. }
  153. CREATE(instance->Cache->Blocks[instance->Cache->Used], unsigned char, instance->Cache->ObjectSize * ERS_BLOCK_ENTRIES);
  154. instance->Cache->Used++;
  155. instance->Cache->Free = ERS_BLOCK_ENTRIES -1;
  156. ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
  157. }
  158. instance->Count++;
  159. return ret;
  160. }
  161. static void ers_obj_free_entry(ERS self, void *entry)
  162. {
  163. ers_instance_t *instance = (ers_instance_t *)self;
  164. struct ers_list *reuse = (struct ers_list *)((unsigned char *)entry - sizeof(struct ers_list));
  165. if (instance == NULL)
  166. {
  167. ShowError("ers_obj_free_entry: NULL object, aborting entry freeing.\n");
  168. return;
  169. }
  170. else if (entry == NULL)
  171. {
  172. ShowError("ers_obj_free_entry: NULL entry, nothing to free.\n");
  173. return;
  174. }
  175. reuse->Next = instance->Cache->ReuseList;
  176. instance->Cache->ReuseList = reuse;
  177. instance->Count--;
  178. }
  179. static size_t ers_obj_entry_size(ERS self)
  180. {
  181. ers_instance_t *instance = (ers_instance_t *)self;
  182. if (instance == NULL)
  183. {
  184. ShowError("ers_obj_entry_size: NULL object, aborting entry freeing.\n");
  185. return 0;
  186. }
  187. return instance->Cache->ObjectSize;
  188. }
  189. static void ers_obj_destroy(ERS self)
  190. {
  191. ers_instance_t *instance = (ers_instance_t *)self;
  192. if (instance == NULL)
  193. {
  194. ShowError("ers_obj_destroy: NULL object, aborting entry freeing.\n");
  195. return;
  196. }
  197. if (instance->Count > 0)
  198. if (!(instance->Options & ERS_OPT_CLEAR))
  199. ShowWarning("Memory leak detected at ERS '%s', %d objects not freed.\n", instance->Name, instance->Count);
  200. if (--instance->Cache->ReferenceCount <= 0)
  201. ers_free_cache(instance->Cache, true);
  202. aFree(instance);
  203. }
  204. ERS ers_new(uint32 size, char *name, enum ERSOptions options)
  205. {
  206. ers_instance_t *instance;
  207. CREATE(instance, ers_instance_t, 1);
  208. size += sizeof(struct ers_list);
  209. if (size % ERS_ALIGNED)
  210. size += ERS_ALIGNED - size % ERS_ALIGNED;
  211. instance->VTable.alloc = ers_obj_alloc_entry;
  212. instance->VTable.free = ers_obj_free_entry;
  213. instance->VTable.entry_size = ers_obj_entry_size;
  214. instance->VTable.destroy = ers_obj_destroy;
  215. instance->Name = name;
  216. instance->Options = options;
  217. instance->Cache = ers_find_cache(size);
  218. instance->Cache->ReferenceCount++;
  219. instance->Count = 0;
  220. return &instance->VTable;
  221. }
  222. void ers_report(void)
  223. {
  224. // FIXME: Someone use this? Is it really needed?
  225. }
  226. void ers_force_destroy_all(void)
  227. {
  228. ers_cache_t *cache;
  229. for (cache = CacheList; cache; cache = cache->Next)
  230. ers_free_cache(cache, false);
  231. }
  232. #endif