1
0

MallocAllocator.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #define string_strrchr
  16. #include "util/platform/libc/string.h"
  17. #include "memory/Allocator.h"
  18. #include "memory/MallocAllocator.h"
  19. #include "memory/MallocAllocator_pvt.h"
  20. #include "util/Bits.h"
  21. #include "util/log/Log.h"
  22. #include <inttypes.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <stdbool.h>
  26. /** This provides the padding for each line based on the depth in the stack. */
  27. struct Unroller;
  28. struct Unroller
  29. {
  30. const char* const content;
  31. const struct Unroller* const last;
  32. };
  33. static void writeUnroller(const struct Unroller* unroller)
  34. {
  35. if (unroller) {
  36. writeUnroller(unroller->last);
  37. fprintf(stderr, "%s", unroller->content);
  38. }
  39. }
  40. static void unroll(struct MallocAllocator_pvt* context, struct Unroller* unroller)
  41. {
  42. writeUnroller(unroller);
  43. const char* ident = (context->identFile) ? strrchr(context->identFile, '/') : "UNKNOWN";
  44. ident = ident ? ident : context->identFile;
  45. fprintf(stderr, "%s:%d [%lu] bytes%s\n",
  46. ident,
  47. context->identLine,
  48. context->allocatedHere,
  49. (context->freeing) ? " (freeing)" : "");
  50. if (context->firstChild) {
  51. unroll(context->firstChild, &(struct Unroller) {
  52. .content = ((context->nextSibling) ? "| " : " "),
  53. .last = unroller
  54. });
  55. }
  56. if (context->nextSibling) {
  57. unroll(context->nextSibling, unroller);
  58. }
  59. }
  60. Gcc_NORETURN
  61. static void failure(struct MallocAllocator_pvt* context,
  62. const char* message,
  63. const char* identFile,
  64. int identLine)
  65. {
  66. // get the root allocator.
  67. struct MallocAllocator_pvt* rootAlloc = context;
  68. while (rootAlloc->lastSibling && rootAlloc->lastSibling != rootAlloc) {
  69. rootAlloc = rootAlloc->lastSibling;
  70. }
  71. // can't use this allocator because it failed.
  72. unroll(rootAlloc, NULL);
  73. Assert_failure("%s:%d Fatal error: [%s] totalBytes [%lu] remaining [%lu]",
  74. identFile, identLine, message,
  75. (unsigned long)context->rootAlloc->maxSpace,
  76. (unsigned long)context->rootAlloc->spaceAvailable);
  77. }
  78. static inline unsigned long getRealSize(unsigned long requestedSize)
  79. {
  80. return ((requestedSize + (sizeof(char*) - 1)) & ~(sizeof(char*) - 1)) // align
  81. + sizeof(struct MallocAllocator_Allocation)
  82. #ifdef MallocAllocator_USE_CANARIES
  83. + sizeof(long)
  84. #endif
  85. ;
  86. }
  87. #define END_CANARY(alloc) ((long*) alloc)[ (alloc->size / sizeof(long)) - 1 ]
  88. static inline void setCanaries(struct MallocAllocator_Allocation* alloc,
  89. struct MallocAllocator_pvt* context)
  90. {
  91. #ifdef MallocAllocator_USE_CANARIES
  92. alloc->beginCanary = context->canary;
  93. END_CANARY(alloc) = alloc->beginCanary;
  94. #endif
  95. }
  96. static inline void checkCanaries(struct MallocAllocator_Allocation* alloc,
  97. struct MallocAllocator_pvt* context)
  98. {
  99. #ifdef MallocAllocator_USE_CANARIES
  100. char* canary;
  101. if (alloc->beginCanary != context->canary) {
  102. canary = "begin";
  103. } else if (END_CANARY(alloc) != alloc->beginCanary) {
  104. canary = "end";
  105. } else {
  106. return;
  107. }
  108. Assert_failure("%s:%d Fatal error: invalid [%s] canary",
  109. context->identFile, context->identLine, canary);
  110. #endif
  111. }
  112. static inline void* newAllocation(struct MallocAllocator_pvt* context,
  113. unsigned long size,
  114. const char* identFile,
  115. int identLine)
  116. {
  117. int64_t realSize = getRealSize(size);
  118. if (context->rootAlloc->spaceAvailable <= realSize) {
  119. failure(context, "Out of memory, limit exceeded", identFile, identLine);
  120. }
  121. context->rootAlloc->spaceAvailable -= realSize;
  122. context->allocatedHere += realSize;
  123. struct MallocAllocator_Allocation* alloc = malloc(realSize);
  124. if (alloc == NULL) {
  125. failure(context, "Out of memory, malloc() returned NULL", identFile, identLine);
  126. }
  127. alloc->next = context->allocations;
  128. alloc->size = realSize;
  129. context->allocations = alloc;
  130. setCanaries(alloc, context);
  131. return (void*) (alloc + 1);
  132. }
  133. static int removeJob(struct MallocAllocator_OnFreeJob* job)
  134. {
  135. struct MallocAllocator_pvt* context = Identity_cast(job->alloc);
  136. struct MallocAllocator_OnFreeJob* j = context->onFree;
  137. struct MallocAllocator_OnFreeJob** jP = &context->onFree;
  138. while (j && j != job) {
  139. jP = &j->next;
  140. j = j->next;
  141. }
  142. if (j == job) {
  143. *jP = j->next;
  144. return 0;
  145. } else {
  146. return -1;
  147. failure(context, "OnFreeJob->complete() called multiple times", job->file, job->line);
  148. }
  149. }
  150. static void releaseMemory(struct MallocAllocator_pvt* context)
  151. {
  152. // Free all of the allocations including the one which holds the allocator.
  153. #ifdef PARANOIA
  154. unsigned long allocatedHere = context->allocatedHere;
  155. #endif
  156. context->rootAlloc->spaceAvailable += context->allocatedHere;
  157. struct MallocAllocator_Allocation* loc = context->allocations;
  158. while (loc != NULL) {
  159. #ifdef PARANOIA
  160. allocatedHere -= loc->size;
  161. #endif
  162. struct MallocAllocator_Allocation* nextLoc = loc->next;
  163. checkCanaries(loc, context);
  164. // TODO: make this optional.
  165. Bits_memset(loc, 0xee, loc->size);
  166. free(loc);
  167. loc = nextLoc;
  168. }
  169. #ifdef PARANOIA
  170. Assert_always(allocatedHere == 0);
  171. #endif
  172. }
  173. /**
  174. * Change the root allocator for a given subtree.
  175. * @param alloc an allocator.
  176. */
  177. static void changeRoot(struct MallocAllocator_pvt* alloc,
  178. struct MallocAllocator_FirstCtx* root,
  179. struct MallocAllocator_pvt* first,
  180. const char* file,
  181. int line)
  182. {
  183. Assert_true(first != alloc);
  184. if (!first) {
  185. first = alloc;
  186. }
  187. if (alloc->rootAlloc != NULL) {
  188. alloc->rootAlloc->spaceAvailable += alloc->allocatedHere;
  189. }
  190. if (root != NULL) {
  191. if (root->spaceAvailable < (int64_t)alloc->allocatedHere) {
  192. failure(alloc, "Out of memory, limit exceeded", file, line);
  193. }
  194. root->spaceAvailable -= alloc->allocatedHere;
  195. }
  196. alloc->rootAlloc = root;
  197. struct MallocAllocator_pvt* child = alloc->firstChild;
  198. while (child) {
  199. struct MallocAllocator_pvt* nextChild = child->nextSibling;
  200. changeRoot(child, root, first, file, line);
  201. child = nextChild;
  202. }
  203. }
  204. // disconnect an allocator from it's parent.
  205. static void disconnect(struct MallocAllocator_pvt* context)
  206. {
  207. // Remove this allocator from the sibling list.
  208. Assert_true(context->lastSibling);
  209. if (context->lastSibling->nextSibling == context) {
  210. context->lastSibling->nextSibling = context->nextSibling;
  211. } else if (context->lastSibling->firstChild == context) {
  212. context->lastSibling->firstChild = context->nextSibling;
  213. } else if (context->lastSibling == context) {
  214. // root alloc
  215. Assert_true(!context->nextSibling);
  216. }
  217. if (context->nextSibling) {
  218. context->nextSibling->lastSibling = context->lastSibling;
  219. context->nextSibling = NULL;
  220. }
  221. context->lastSibling = context;
  222. }
  223. // connect an allocator to a new parent.
  224. static void connect(struct MallocAllocator_pvt* parent,
  225. struct MallocAllocator_pvt* child,
  226. const char* file,
  227. int line)
  228. {
  229. Assert_true(child->lastSibling == child);
  230. Assert_true(child->nextSibling == NULL);
  231. child->nextSibling = parent->firstChild;
  232. parent->firstChild = child;
  233. child->lastSibling = parent;
  234. changeRoot(child, parent->rootAlloc, NULL, file, line);
  235. }
  236. static struct MallocAllocator_pvt* getParent(struct MallocAllocator_pvt* child)
  237. {
  238. struct MallocAllocator_pvt* ls = child->lastSibling;
  239. while (ls) {
  240. if (ls->firstChild == child) {
  241. return ls;
  242. }
  243. if (ls == ls->lastSibling) {
  244. // root alloc
  245. return NULL;
  246. }
  247. child = ls;
  248. ls = ls->lastSibling;
  249. }
  250. Assert_true(0);
  251. }
  252. static void freeAllocator(struct MallocAllocator_pvt* context, const char* file, int line);
  253. static void childFreed(struct MallocAllocator_pvt* child)
  254. {
  255. struct MallocAllocator_pvt* parent = getParent(child);
  256. // disconnect the child and if there are no children left then call freeAllocator()
  257. // on the parent a second time.
  258. disconnect(child);
  259. if (parent && !parent->firstChild && parent->freeing) {
  260. freeAllocator(parent, child->identFile, child->identLine);
  261. }
  262. }
  263. static int onFreeComplete(struct Allocator_OnFreeJob* onFreeJob)
  264. {
  265. struct MallocAllocator_OnFreeJob* job = (struct MallocAllocator_OnFreeJob*) onFreeJob;
  266. struct MallocAllocator_pvt* context = Identity_cast(job->alloc);
  267. if (removeJob(job)) {
  268. failure(context, "OnFreeJob->complete() called multiple times", job->file, job->line);
  269. }
  270. if (!context->onFree) {
  271. //childFreed(context);
  272. // There are no more jobs, release the memory.
  273. freeAllocator(context, context->identFile, context->identLine);
  274. }
  275. return 0;
  276. }
  277. static void disconnectAdopted(struct MallocAllocator_pvt* parent, struct MallocAllocator_pvt* child)
  278. {
  279. Assert_true(parent->adoptions);
  280. Assert_true(parent->adoptions->children);
  281. struct MallocAllocator_List** cpp = &parent->adoptions->children;
  282. struct MallocAllocator_List* cp;
  283. int found = 0;
  284. while ((cp = *cpp)) {
  285. if (cp->alloc == child) {
  286. *cpp = cp->next;
  287. found = 1;
  288. break;
  289. }
  290. cpp = &cp->next;
  291. }
  292. Assert_true(found);
  293. Assert_true(child->adoptions);
  294. Assert_true(child->adoptions->parents);
  295. cpp = &child->adoptions->parents;
  296. found = 0;
  297. while ((cp = *cpp)) {
  298. if (cp->alloc == parent) {
  299. *cpp = cp->next;
  300. found = 1;
  301. break;
  302. }
  303. cpp = &cp->next;
  304. }
  305. Assert_true(found);
  306. }
  307. /**
  308. * Triggered when freeAllocator() is called and the allocator nolonger
  309. * has any remaining links to the allocator tree.
  310. */
  311. static void freeAllocator(struct MallocAllocator_pvt* context, const char* file, int line)
  312. {
  313. // When the last child calls us back via childFreed() we will be called the last time and
  314. // if this is not set, the child will be disconnected from us and we will be left.
  315. context->freeing = 1;
  316. if (context->adoptions && context->adoptions->parents) {
  317. disconnect(context);
  318. connect(context->adoptions->parents->alloc, context, file, line);
  319. disconnectAdopted(context->adoptions->parents->alloc, context);
  320. return;
  321. }
  322. // from now on, identFile/line will point to the place of freeing.
  323. // this allows childFreed() to tell the truth when calling us back.
  324. context->identFile = file;
  325. context->identLine = line;
  326. // Disconnect adopted children.
  327. struct MallocAllocator_List* childL = context->adoptions ? context->adoptions->children : NULL;
  328. while (childL) {
  329. disconnectAdopted(context, childL->alloc);
  330. childL = childL->next;
  331. }
  332. // Do the onFree jobs.
  333. struct MallocAllocator_OnFreeJob** jobP = &context->onFree;
  334. while (*jobP != NULL) {
  335. struct MallocAllocator_OnFreeJob* job = *jobP;
  336. if (!job->generic.callback) {
  337. // no callback, remove the job
  338. Assert_true(!removeJob(job));
  339. continue;
  340. } else if (!job->done) {
  341. if (job->generic.callback(&job->generic) != Allocator_ONFREE_ASYNC) {
  342. Assert_true(!removeJob(job));
  343. continue;
  344. }
  345. // asynchronously completing, don't bother it again.
  346. job->done = true;
  347. }
  348. jobP = &job->next;
  349. }
  350. if (context->onFree) {
  351. // onFreeComplete() will call us back.
  352. return;
  353. }
  354. // Free children
  355. struct MallocAllocator_pvt* child = context->firstChild;
  356. if (child) {
  357. while (child) {
  358. struct MallocAllocator_pvt* nextChild = child->nextSibling;
  359. freeAllocator(child, file, line);
  360. child = nextChild;
  361. }
  362. // childFreed() will call us back.
  363. return;
  364. }
  365. childFreed(context);
  366. releaseMemory(context);
  367. }
  368. /**
  369. * Disconnect an allocator from it's parent and free it if there are no more links to the tree.
  370. */
  371. static void disconnectAllocator(struct Allocator* allocator, const char* file, int line)
  372. {
  373. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  374. freeAllocator(context, file, line);
  375. }
  376. /** @see Allocator->malloc() */
  377. static void* allocatorMalloc(unsigned long length,
  378. struct Allocator* allocator,
  379. const char* identFile,
  380. int identLine)
  381. {
  382. struct MallocAllocator_pvt* ctx = Identity_cast((struct MallocAllocator_pvt*) allocator);
  383. return newAllocation(ctx, length, identFile, identLine);
  384. }
  385. /** @see Allocator->calloc() */
  386. static void* allocatorCalloc(unsigned long length,
  387. unsigned long count,
  388. struct Allocator* allocator,
  389. const char* identFile,
  390. int identLine)
  391. {
  392. void* pointer = allocatorMalloc(length * count, allocator, identFile, identLine);
  393. Bits_memset(pointer, 0, length * count);
  394. return pointer;
  395. }
  396. /** @see Allocator->clone() */
  397. static void* allocatorClone(unsigned long length,
  398. struct Allocator* allocator,
  399. const void* toClone,
  400. const char* identFile,
  401. int identLine)
  402. {
  403. void* pointer = allocatorMalloc(length, allocator, identFile, identLine);
  404. Bits_memcpy(pointer, toClone, length);
  405. return pointer;
  406. }
  407. /** @see Allocator->realloc() */
  408. static void* allocatorRealloc(const void* original,
  409. unsigned long size,
  410. struct Allocator* allocator,
  411. const char* identFile,
  412. int identLine)
  413. {
  414. if (original == NULL) {
  415. return allocatorMalloc(size, allocator, identFile, identLine);
  416. }
  417. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  418. struct MallocAllocator_Allocation** locPtr = &context->allocations;
  419. struct MallocAllocator_Allocation* origLoc =
  420. ((struct MallocAllocator_Allocation*) original) - 1;
  421. for (;;) {
  422. struct MallocAllocator_Allocation* loc = *locPtr;
  423. if (loc == NULL) {
  424. failure(context,
  425. "Reallocation of memory which was not allocated using this allocator.",
  426. identFile,
  427. identLine);
  428. }
  429. checkCanaries(loc, context);
  430. if (loc == origLoc) {
  431. break;
  432. }
  433. locPtr = &loc->next;
  434. }
  435. struct MallocAllocator_Allocation* nextLoc = origLoc->next;
  436. size_t realSize = getRealSize(size);
  437. if (context->rootAlloc->spaceAvailable + origLoc->size < realSize) {
  438. failure(context, "Out of memory, limit exceeded.", identFile, identLine);
  439. }
  440. context->rootAlloc->spaceAvailable += origLoc->size;
  441. context->rootAlloc->spaceAvailable -= realSize;
  442. context->allocatedHere -= origLoc->size;
  443. context->allocatedHere += realSize;
  444. struct MallocAllocator_Allocation* alloc = realloc(origLoc, realSize);
  445. if (alloc == NULL) {
  446. failure(context, "Out of memory, realloc() returned NULL.", identFile, identLine);
  447. }
  448. alloc->next = nextLoc;
  449. alloc->size = realSize;
  450. *locPtr = alloc;
  451. setCanaries(alloc, context);
  452. return (void*) (alloc + 1);
  453. }
  454. /** @see Allocator_child() */
  455. static struct Allocator* childAllocator(struct Allocator* allocator, const char* file, int line)
  456. {
  457. struct MallocAllocator_pvt* parent = Identity_cast((struct MallocAllocator_pvt*) allocator);
  458. struct MallocAllocator_pvt* child =
  459. newAllocation(parent, sizeof(struct MallocAllocator_pvt), file, line);
  460. Bits_memset(child, 0, sizeof(struct MallocAllocator_pvt));
  461. Bits_memcpyConst(&child->pub, &parent->pub, sizeof(struct Allocator));
  462. Identity_set(child);
  463. // Add the child to it's own allocation list.
  464. child->allocations = parent->allocations;
  465. // Remove the child from the parent's allocation list.
  466. parent->allocations = parent->allocations->next;
  467. parent->allocatedHere -= getRealSize(sizeof(struct MallocAllocator_pvt));
  468. child->allocatedHere += getRealSize(sizeof(struct MallocAllocator_pvt));
  469. // Drop the rest of the linked list from the child's allocation
  470. child->allocations->next = NULL;
  471. // Link the child into the parent's allocator list
  472. child->lastSibling = parent;
  473. child->nextSibling = parent->firstChild;
  474. if (parent->firstChild != NULL) {
  475. parent->firstChild->lastSibling = child;
  476. }
  477. child->rootAlloc = parent->rootAlloc;
  478. child->identFile = file;
  479. child->identLine = line;
  480. child->nextCanary = child->canary = parent->nextCanary;
  481. parent->firstChild = child;
  482. child->rootAlloc = parent->rootAlloc;
  483. // Now set the child to use it's own canaries so it will free properly.
  484. setCanaries(child->allocations, child);
  485. return &child->pub;
  486. }
  487. static int removeOnFreeJob(struct Allocator_OnFreeJob* toRemove)
  488. {
  489. struct MallocAllocator_OnFreeJob* job = (struct MallocAllocator_OnFreeJob*) toRemove;
  490. struct MallocAllocator_pvt* context = Identity_cast(job->alloc);
  491. struct MallocAllocator_OnFreeJob** jobPtr = &(context->onFree);
  492. while (*jobPtr != NULL) {
  493. if (*jobPtr == job) {
  494. *jobPtr = (*jobPtr)->next;
  495. return 0;
  496. }
  497. jobPtr = &(*jobPtr)->next;
  498. }
  499. return -1;
  500. }
  501. static bool isAncestorOf(struct MallocAllocator_pvt* maybeParent,
  502. struct MallocAllocator_pvt* maybeChild)
  503. {
  504. if (maybeParent == maybeChild) {
  505. return true;
  506. }
  507. if (maybeParent == NULL || maybeChild == NULL) {
  508. return false;
  509. }
  510. if (isAncestorOf(maybeParent, getParent(maybeChild))) {
  511. return true;
  512. }
  513. if (maybeChild->adoptions) {
  514. struct MallocAllocator_List* al = maybeChild->adoptions->parents;
  515. while (al) {
  516. if (isAncestorOf(maybeParent, al->alloc)) {
  517. return true;
  518. }
  519. }
  520. }
  521. return false;
  522. }
  523. static void adopt(struct Allocator* adoptedParent,
  524. struct Allocator* childToAdopt,
  525. const char* file,
  526. int line)
  527. {
  528. struct MallocAllocator_pvt* parent = Identity_cast((struct MallocAllocator_pvt*) adoptedParent);
  529. struct MallocAllocator_pvt* child = Identity_cast((struct MallocAllocator_pvt*) childToAdopt);
  530. if (isAncestorOf(child, parent)) {
  531. // The child is a parent of the parent, this means an adoption would be meaningless
  532. // because if the child is otherwise freed, it will take the parent along with it.
  533. return;
  534. }
  535. if (!parent->adoptions) {
  536. parent->adoptions =
  537. allocatorCalloc(sizeof(struct MallocAllocator_Adoptions), 1, adoptedParent, file, line);
  538. }
  539. if (!child->adoptions) {
  540. child->adoptions =
  541. allocatorCalloc(sizeof(struct MallocAllocator_Adoptions), 1, childToAdopt, file, line);
  542. }
  543. struct MallocAllocator_List* pl =
  544. allocatorCalloc(sizeof(struct MallocAllocator_List), 1, adoptedParent, file, line);
  545. pl->alloc = child;
  546. pl->next = parent->adoptions->children;
  547. parent->adoptions->children = pl;
  548. struct MallocAllocator_List* cl =
  549. allocatorCalloc(sizeof(struct MallocAllocator_List), 1, childToAdopt, file, line);
  550. cl->alloc = parent;
  551. cl->next = child->adoptions->parents;
  552. child->adoptions->parents = cl;
  553. }
  554. /** @see Allocator_onFree() */
  555. static struct Allocator_OnFreeJob* addOnFreeJob(struct Allocator* allocator,
  556. const char* file,
  557. int line)
  558. {
  559. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  560. struct MallocAllocator_OnFreeJob* newJob =
  561. Allocator_clone(allocator, (&(struct MallocAllocator_OnFreeJob) {
  562. .generic = {
  563. .cancel = removeOnFreeJob,
  564. .complete = onFreeComplete
  565. },
  566. .alloc = context,
  567. .file = file,
  568. .line = line
  569. }));
  570. Identity_set(&newJob->generic);
  571. struct MallocAllocator_OnFreeJob* job = context->onFree;
  572. if (job == NULL) {
  573. context->onFree = newJob;
  574. } else {
  575. while (job->next != NULL) {
  576. job = job->next;
  577. }
  578. job->next = newJob;
  579. }
  580. return &newJob->generic;
  581. }
  582. /** @see MallocAllocator.h */
  583. struct Allocator* MallocAllocator_newWithIdentity(unsigned long sizeLimit,
  584. const char* identFile,
  585. int identLine)
  586. {
  587. // Add in the size of the allocator so that a very small sizeLimit is sane.
  588. sizeLimit += getRealSize(sizeof(struct MallocAllocator_FirstCtx));
  589. struct MallocAllocator_FirstCtx stackContext = {
  590. .spaceAvailable = (sizeLimit == 0) ? SIZE_MAX : sizeLimit,
  591. .context = {
  592. .identFile = identFile,
  593. .identLine = identLine,
  594. .canary = (long) 0x09F911029D74E35Bll,
  595. .nextCanary = (long) 0xD84156C5635688C0ll,
  596. }
  597. };
  598. stackContext.maxSpace = stackContext.spaceAvailable;
  599. stackContext.context.rootAlloc = &stackContext;
  600. Identity_set(&stackContext.context);
  601. struct MallocAllocator_FirstCtx* firstContext =
  602. allocatorClone(sizeof(struct MallocAllocator_FirstCtx),
  603. &stackContext.context.pub,
  604. &stackContext,
  605. identFile,
  606. identLine);
  607. struct MallocAllocator_pvt* context = &firstContext->context;
  608. context->rootAlloc = firstContext;
  609. struct Allocator allocator = {
  610. .free = disconnectAllocator,
  611. .malloc = allocatorMalloc,
  612. .calloc = allocatorCalloc,
  613. .clone = allocatorClone,
  614. .realloc = allocatorRealloc,
  615. .child = childAllocator,
  616. .onFree = addOnFreeJob,
  617. .adopt = adopt
  618. };
  619. Bits_memcpyConst(&context->pub, &allocator, sizeof(struct Allocator));
  620. context->lastSibling = context;
  621. Identity_set(context);
  622. return &context->pub;
  623. }
  624. unsigned long MallocAllocator_bytesAllocated(struct Allocator* allocator)
  625. {
  626. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) allocator);
  627. return context->rootAlloc->maxSpace - context->rootAlloc->spaceAvailable;
  628. }
  629. void MallocAllocator_setCanary(struct Allocator* alloc, long value)
  630. {
  631. #ifdef MallocAllocator_USE_CANARIES
  632. struct MallocAllocator_pvt* context = Identity_cast((struct MallocAllocator_pvt*) alloc);
  633. context->nextCanary ^= value;
  634. #endif
  635. }