void Allocator__free(struct Allocator* alloc, const char* file, int line) { struct Allocator_pvt* context = Identity_check((struct Allocator_pvt*) alloc); check(context); // It's really difficult to know that you didn't get called back inside of a freeing of a // parent of a parent allocator which causes your allocator to be in isFreeing state so // lets be forgiving here. if (context->pub.isFreeing) { return; } if (context->rootAlloc == (struct Allocator_FirstCtx*)context) { struct Allocator_FirstCtx* rootAlloc = Identity_check((struct Allocator_FirstCtx*)context); if (bytesAllocated(context) + rootAlloc->spaceAvailable != (uint64_t)rootAlloc->maxSpace) { failure(context, "unaccounted for memory", file, line); } } check(context); if (!pivotChildrenToAdoptedParents(context, file, line)) { return; } check(context); marshalOnFreeJobs(context, context); check(context); doOnFreeJobs(context); check(context); if (!context->onFree) { freeAllocator(context); } }
static void freeAllocator(struct Allocator_pvt* context) { Assert_true(context->pub.isFreeing); int isTop = !context->parent->pub.isFreeing; if (isTop) { check(context); disconnect(context); } struct Allocator_pvt* child = context->firstChild; while (child) { struct Allocator_pvt* nextChild = child->nextSibling; freeAllocator(child); child = nextChild; } // Grab out the provider and provider context in case the root allocator is freed. struct Allocator_FirstCtx* rootAlloc = Identity_check(context->rootAlloc); Allocator_Provider provider = rootAlloc->provider; Allocator_Provider_CONTEXT_TYPE* providerCtx = rootAlloc->providerContext; releaseMemory(context, provider, providerCtx); if (isTop) { check((struct Allocator_pvt*)rootAlloc); } }
static void childFreed(struct Allocator_pvt* child) { struct Allocator_pvt* parent = child->parent; // disconnect the child and if there are no children left then call freeAllocator() // on the parent a second time. If child == parent then it's a root allocator and // we do not want to double-free it. disconnect(child); if (parent && parent != child && !parent->firstChild && parent->pub.isFreeing) { freeAllocator(parent, child->pub.fileName, child->pub.lineNum); } }
void Allocator_onFreeComplete(struct Allocator_OnFreeJob* onFreeJob) { struct Allocator_OnFreeJob_pvt* job = (struct Allocator_OnFreeJob_pvt*) onFreeJob; struct Allocator_pvt* context = Identity_check(job->alloc); if (removeJob(job)) { failure(context, "OnFreeJob->complete() called multiple times", job->file, job->line); } if (!context->onFree) { // There are no more jobs, release the memory. freeAllocator(context); } }
void Allocator__free(struct Allocator* alloc, const char* file, int line) { struct Allocator_pvt* context = Identity_check((struct Allocator_pvt*) alloc); freeAllocator(context, file, line); }
/** * Triggered when freeAllocator() is called and the allocator nolonger * has any remaining links to the allocator tree. */ static void freeAllocator(struct Allocator_pvt* context, const char* file, int line) { if (context->adoptions && context->adoptions->parents) { disconnect(context); connect(context->adoptions->parents->alloc, context, file, line); disconnectAdopted(context->adoptions->parents->alloc, context); return; } // When the last child calls us back via childFreed() we will be called the last time and // if this is not set, the child will be disconnected from us and we will be left. context->pub.isFreeing = 1; // from now on, fileName/line will point to the place of freeing. // this allows childFreed() to tell the truth when calling us back. context->pub.fileName = file; context->pub.lineNum = line; // Disconnect adopted children. struct Allocator_List* childL = context->adoptions ? context->adoptions->children : NULL; while (childL) { disconnectAdopted(context, childL->alloc); childL = childL->next; } // Do the onFree jobs. struct Allocator_OnFreeJob_pvt** jobP = &context->onFree; while (*jobP != NULL) { struct Allocator_OnFreeJob_pvt* job = *jobP; if (!job->pub.callback) { // no callback, remove the job Assert_true(!removeJob(job)); continue; } else if (!job->done) { if (job->pub.callback(&job->pub) != Allocator_ONFREE_ASYNC) { Assert_true(!removeJob(job)); continue; } // asynchronously completing, don't bother it again. job->done = 1; } jobP = &job->next; } if (context->onFree) { // onFreeComplete() will call us back. return; } // Free children struct Allocator_pvt* child = context->firstChild; if (child) { while (child) { struct Allocator_pvt* nextChild = child->nextSibling; freeAllocator(child, file, line); child = nextChild; } // childFreed() will call us back. return; } // Grab out the provider and provider context in case the root allocator is freed. Allocator_Provider provider = context->rootAlloc->provider; Allocator_Provider_CONTEXT_TYPE* providerCtx = context->rootAlloc->providerContext; childFreed(context); releaseMemory(context, provider, providerCtx); }