Exemplo n.º 1
0
void Allocator__disown(struct Allocator* parentAlloc,
                       struct Allocator* allocToDisown,
                       const char* fileName,
                       int lineNum)
{
    struct Allocator_pvt* parent = Identity_check((struct Allocator_pvt*) parentAlloc);
    struct Allocator_pvt* child = Identity_check((struct Allocator_pvt*) allocToDisown);

    if (parent->pub.isFreeing || child->pub.isFreeing) { return; }

    if (child->parent == parent) {
        // The child's natural parent has been freed and it has pivoted to the adopted parent
        // Do a normal Allocator_free() and it will either pivot once again to another adopter
        // or it will drop from the tree and free.
        Allocator__free(&child->pub, fileName, lineNum);
        return;
    }

    if (isAncestorOf(child, parent)) {
        // Rare but possible way that the child would never have been adopted.
        return;
    }

    disconnectAdopted(parent, child);
}
Exemplo n.º 2
0
// Shallow first search to prevent lots of flapping while we tear down the tree.
static int pivotChildrenToAdoptedParents0(struct Allocator_pvt* context,
                                          int depth,
                                          int maxDepth,
                                          const char* file,
                                          int line)
{
    int out = 0;
    if (depth == maxDepth) {
        if (context->pub.isFreeing) { return 0; }
        if (context->adoptions) {
            // Attempt to pivot around to a parent in order to save this allocator
            if (context->adoptions->parents) {
                Assert_true(!context->adoptions->parents->alloc->pub.isFreeing);
                disconnect(context);
                connect(context->adoptions->parents->alloc, context, file, line);
                disconnectAdopted(context->adoptions->parents->alloc, context);
                return 0;
            }
            // No saving it, drop it's adoptions.
            for (struct Allocator_List* c = context->adoptions->children; c; c = c->next) {
                Assert_true(!c->alloc->pub.isFreeing);
                disconnectAdopted(context, c->alloc);
            }
        }
        Assert_true(!context->pub.isFreeing);
        context->pub.isFreeing = 1;
        out++;
    } else {
        struct Allocator_pvt* child = context->firstChild;
        while (child) {
            Assert_ifParanoid(child != context);
            struct Allocator_pvt* nextChild = child->nextSibling;
            out += pivotChildrenToAdoptedParents0(child, depth+1, maxDepth, file, line);
            child = nextChild;
        }
    }
    return out;
}
Exemplo n.º 3
0
/**
 * 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);
}