Allocator.c 24 KB

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