alloc.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. alloc.c -- Optional WebServer memory allocator
  3. This file implements a fast block allocation scheme suitable for operating systems whoes malloc suffers from
  4. fragmentation. This allocator maintains block class queues for rapid allocation and minimal fragmentation. This
  5. allocator does not coalesce blocks. The storage space may be populated statically or via the traditional malloc
  6. mechanisms. Large blocks greater than the maximum class size may be allocated from the O/S or run-time system via
  7. malloc. To permit the use of malloc, call wopen with flags set to WEBS_USE_MALLOC (this is the default). It is
  8. recommended that wopen be called first thing in the application. If it is not, it will be called with default
  9. values on the first call to walloc(). Note that this code is not designed for multi-threading purposes and it
  10. depends on newly declared variables being initialized to zero.
  11. Copyright (c) All Rights Reserved. See details at the end of the file.
  12. */
  13. /********************************* Includes ***********************************/
  14. #include "goahead.h"
  15. /********************************** Locals ************************************/
  16. static WebsMemNotifier memNotifier;
  17. PUBLIC void websSetMemNotifier(WebsMemNotifier cback)
  18. {
  19. memNotifier = cback;
  20. }
  21. /********************************* Defines ************************************/
  22. #if ME_GOAHEAD_REPLACE_MALLOC
  23. /*
  24. ROUNDUP4(size) returns the next higher integer value of size that is divisible by 4, or the value of size if size is
  25. divisible by 4. ROUNDUP4() is used in aligning memory allocations on 4-byte boundaries.
  26. Note: ROUNDUP4() is only required on some operating systems.
  27. */
  28. #define ROUNDUP4(size) ((size) % 4) ? (size) + (4 - ((size) % 4)) : (size)
  29. /*
  30. qhead blocks are created as the original memory allocation is freed up. See wfree.
  31. */
  32. static WebsAlloc *qhead[WEBS_MAX_CLASS]; /* Per class block q head */
  33. static char *freeBuf; /* Pointer to free memory */
  34. static char *freeNext; /* Pointer to next free mem */
  35. static int freeSize; /* Size of free memory */
  36. static int freeLeft; /* Size of free left for use */
  37. static int controlFlags = WEBS_USE_MALLOC; /* Default to auto-malloc */
  38. static int wopenCount = 0; /* Num tasks using walloc */
  39. static int wallocGetSize(ssize size, int *q);
  40. #endif /* ME_GOAHEAD_REPLACE_MALLOC */
  41. /********************************** Code **************************************/
  42. /*
  43. Initialize the walloc module. wopenAlloc should be called the very first thing after the application starts and
  44. wcloseAlloc should be called the last thing before exiting. If wopenAlloc is not called, it will be called on the first
  45. allocation with default values. "buf" points to memory to use of size "bufsize". If buf is NULL, memory is allocated
  46. using malloc. flags may be set to WEBS_USE_MALLOC if using malloc is okay. This routine will allocate * an initial
  47. buffer of size bufsize for use by the application.
  48. */
  49. PUBLIC int wopenAlloc(void *buf, int bufsize, int flags)
  50. {
  51. #if ME_GOAHEAD_REPLACE_MALLOC
  52. controlFlags = flags;
  53. /*
  54. If wopen already called by a shared process, just increment the count and return
  55. */
  56. if (++wopenCount > 1) {
  57. return 0;
  58. }
  59. if (buf == NULL) {
  60. if (bufsize == 0) {
  61. bufsize = WEBS_DEFAULT_MEM;
  62. }
  63. bufsize = ROUNDUP4(bufsize);
  64. if ((buf = malloc(bufsize)) == NULL) {
  65. /*
  66. Resetting wopenCount so client code can decide to call wopenAlloc() again with a smaller memory request.
  67. */
  68. --wopenCount;
  69. return -1;
  70. }
  71. } else {
  72. controlFlags |= WEBS_USER_BUF;
  73. }
  74. freeSize = freeLeft = bufsize;
  75. freeBuf = freeNext = buf;
  76. memset(qhead, 0, sizeof(qhead));
  77. #endif /* ME_GOAHEAD_REPLACE_MALLOC */
  78. return 0;
  79. }
  80. PUBLIC void wcloseAlloc()
  81. {
  82. #if ME_GOAHEAD_REPLACE_MALLOC
  83. if (--wopenCount <= 0 && !(controlFlags & WEBS_USER_BUF)) {
  84. free(freeBuf);
  85. wopenCount = 0;
  86. }
  87. #endif /* ME_GOAHEAD_REPLACE_MALLOC */
  88. }
  89. #if ME_GOAHEAD_REPLACE_MALLOC
  90. /*
  91. Allocate a block of the requested size. First check the block queues for a suitable one.
  92. */
  93. PUBLIC void *walloc(ssize size)
  94. {
  95. WebsAlloc *bp;
  96. int q, memSize;
  97. /*
  98. Call wopen with default values if the application has not yet done so
  99. */
  100. if (freeBuf == NULL) {
  101. if (wopenAlloc(NULL, WEBS_DEFAULT_MEM, 0) < 0) {
  102. return NULL;
  103. }
  104. }
  105. if (size < 0) {
  106. return NULL;
  107. }
  108. memSize = wallocGetSize(size, &q);
  109. if (q >= WEBS_MAX_CLASS) {
  110. /*
  111. Size if bigger than the maximum class. Malloc if use has been okayed
  112. */
  113. if (controlFlags & WEBS_USE_MALLOC) {
  114. memSize = ROUNDUP4(memSize);
  115. bp = (WebsAlloc*) malloc(memSize);
  116. if (bp == NULL) {
  117. if (memNotifier) {
  118. (memNotifier)(memSize);
  119. }
  120. return NULL;
  121. }
  122. } else {
  123. if (memNotifier) {
  124. (memNotifier)(memSize);
  125. }
  126. return NULL;
  127. }
  128. /*
  129. the u.size is the actual size allocated for data
  130. */
  131. bp->u.size = memSize - sizeof(WebsAlloc);
  132. bp->flags = WEBS_MALLOCED;
  133. } else if ((bp = qhead[q]) != NULL) {
  134. /*
  135. Take first block off the relevant q if non-empty
  136. */
  137. qhead[q] = bp->u.next;
  138. bp->u.size = memSize - sizeof(WebsAlloc);
  139. bp->flags = 0;
  140. } else {
  141. if (freeLeft > memSize) {
  142. /*
  143. The q was empty, and the free list has spare memory so create a new block out of the primary free block
  144. */
  145. bp = (WebsAlloc*) freeNext;
  146. freeNext += memSize;
  147. freeLeft -= memSize;
  148. bp->u.size = memSize - sizeof(WebsAlloc);
  149. bp->flags = 0;
  150. } else if (controlFlags & WEBS_USE_MALLOC) {
  151. /*
  152. Nothing left on the primary free list, so malloc a new block
  153. */
  154. memSize = ROUNDUP4(memSize);
  155. if ((bp = (WebsAlloc*) malloc(memSize)) == NULL) {
  156. if (memNotifier) {
  157. (memNotifier)(memSize);
  158. }
  159. return NULL;
  160. }
  161. bp->u.size = memSize - sizeof(WebsAlloc);
  162. bp->flags = WEBS_MALLOCED;
  163. } else {
  164. if (memNotifier) {
  165. (memNotifier)(memSize);
  166. }
  167. return NULL;
  168. }
  169. }
  170. bp->flags |= WEBS_INTEGRITY;
  171. return (void*) ((char*) bp + sizeof(WebsAlloc));
  172. }
  173. /*
  174. Free a block back to the relevant free q. We don't free back to the O/S or run time system unless the block is
  175. greater than the maximum class size. We also do not coalesce blocks.
  176. */
  177. PUBLIC void wfree(void *mp)
  178. {
  179. WebsAlloc *bp;
  180. int q;
  181. if (mp == 0) {
  182. return;
  183. }
  184. bp = (WebsAlloc*) ((char*) mp - sizeof(WebsAlloc));
  185. assert((bp->flags & WEBS_INTEGRITY_MASK) == WEBS_INTEGRITY);
  186. if ((bp->flags & WEBS_INTEGRITY_MASK) != WEBS_INTEGRITY) {
  187. return;
  188. }
  189. wallocGetSize(bp->u.size, &q);
  190. if (bp->flags & WEBS_MALLOCED) {
  191. free(bp);
  192. return;
  193. }
  194. /*
  195. Simply link onto the head of the relevant q
  196. */
  197. bp->u.next = qhead[q];
  198. qhead[q] = bp;
  199. bp->flags = WEBS_FILL_WORD;
  200. }
  201. /*
  202. Reallocate a block. Allow NULL pointers and just do a malloc. Note: if the realloc fails, we return NULL and the
  203. previous buffer is preserved.
  204. */
  205. PUBLIC void *wrealloc(void *mp, ssize newsize)
  206. {
  207. WebsAlloc *bp;
  208. void *newbuf;
  209. if (mp == NULL) {
  210. return walloc(newsize);
  211. }
  212. bp = (WebsAlloc*) ((char*) mp - sizeof(WebsAlloc));
  213. assert((bp->flags & WEBS_INTEGRITY_MASK) == WEBS_INTEGRITY);
  214. /*
  215. If the allocated memory already has enough room just return the previously allocated address.
  216. */
  217. if (bp->u.size >= newsize) {
  218. return mp;
  219. }
  220. if ((newbuf = walloc(newsize)) != NULL) {
  221. memcpy(newbuf, mp, bp->u.size);
  222. wfree(mp);
  223. }
  224. return newbuf;
  225. }
  226. /*
  227. Find the size of the block to be walloc'ed. It takes in a size, finds the smallest binary block it fits into, adds
  228. an overhead amount and returns. q is the binary size used to keep track of block sizes in use. Called from both
  229. walloc and wfree.
  230. */
  231. static int wallocGetSize(ssize size, int *q)
  232. {
  233. int mask;
  234. mask = (size == 0) ? 0 : (size-1) >> WEBS_SHIFT;
  235. for (*q = 0; mask; mask >>= 1) {
  236. *q = *q + 1;
  237. }
  238. return ((1 << (WEBS_SHIFT + *q)) + sizeof(WebsAlloc));
  239. }
  240. #else /* !ME_GOAHEAD_REPLACE_MALLOC */
  241. PUBLIC void *walloc(ssize num)
  242. {
  243. void *mem;
  244. if ((mem = malloc(num)) == 0) {
  245. if (memNotifier) {
  246. (memNotifier)(num);
  247. }
  248. }
  249. return mem;
  250. }
  251. PUBLIC void wfree(void *mem)
  252. {
  253. if (mem) {
  254. free(mem);
  255. }
  256. }
  257. PUBLIC void *wrealloc(void *mem, ssize num)
  258. {
  259. void *old;
  260. old = mem;
  261. if ((mem = realloc(mem, num)) == 0) {
  262. if (memNotifier) {
  263. (memNotifier)(num);
  264. }
  265. free(old);
  266. }
  267. return mem;
  268. }
  269. #endif /* ME_GOAHEAD_REPLACE_MALLOC */
  270. PUBLIC void *wdup(cvoid *ptr, size_t usize)
  271. {
  272. char *newp;
  273. if ((newp = walloc(usize)) != 0) {
  274. memcpy(newp, ptr, usize);
  275. }
  276. return newp;
  277. }
  278. /*
  279. Copyright (c) Embedthis Software. All Rights Reserved.
  280. This software is distributed under commercial and open source licenses.
  281. You may use the Embedthis GoAhead open source license or you may acquire
  282. a commercial license from Embedthis Software. You agree to be fully bound
  283. by the terms of either license. Consult the LICENSE.md distributed with
  284. this software for full details and other copyrights.
  285. */