Curiosidade experimento allocator

16 views
Skip to first unread message

Thiago Adams

unread,
May 26, 2021, 6:51:43 AM5/26/21
to ccppbrasil

Gostaria de compartilhar um experimento de allocator aonde 
se pode informar um "destructor" no momento da alocação.
Este destructor é chamado antes do free se não for nulo.

Além disso, ao destruir o allocator toda memória que foi alocada
por ele também é liberada.

Funcionamento: Basicamente ao se alocar memória
se aloca um pouco mais para armazenar o "destructor " e um 
ponteiro para linkar as regiões de memórias alocadas.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>


union header {
    struct {
        union header* next; /* next block*/
        void (*DTOR)(void*);     /* destructor */
    } s;
    long double x;         /* force alignment of blocks */
};

struct allocator
{
    union header* head;
};

void* alloc(struct allocator* allocator, int nbytes, void (*F)(void*))
{
    int nunits = (nbytes + sizeof(union header) - 1) / sizeof(union header) + 1;
    union header* p = malloc(nunits * sizeof(union header));
    if (p)
    {        
        p->s.next = allocator->head;
        p->s.DTOR = F;        
        allocator->head = p;


        return (void*)(p + 1);
    }
    return NULL;
}

void allocator_destroy(struct allocator* allocator)
{
    union header* p = allocator->head;
    while (p)
    {
        union header* next = p->s.next;
        if (p->s.DTOR)
        {
            void* pobj = (void*)((union header*)p + 1);
            p->s.DTOR(pobj);
            free(p);
        }
        p = next;
    }
}

struct X
{
    char* name;
};

void DestroyX(struct X* p)
{
    printf("DestroyX");
}

int main()
{
    struct allocator allocator = { 0 };
    struct X* pX = alloc(&allocator, sizeof(struct X), DestroyX);
    if (pX)
    {
        pX->name = alloc(&allocator, strlen("teste"), NULL);
        strcpy(pX->name, "teste");
        
    }
    

    allocator_destroy(&allocator);
}

Uma variante é também permitir free em qualquer momento.
Para isso pode-se usar uma lista duplamente encadeada para
acelerar a remoção do nó.


union header {
    struct {
        union header* prev; /* next block*/
        union header* next; /* next block*/
        void (*DTOR)(void*);     /* destructor */
    } s;
    long double x;         /* force alignment of blocks */
};


void* alloc(struct allocator* allocator, int nbytes, void (*F)(void*))
{
    int nunits = (nbytes + sizeof(union header) - 1) / sizeof(union header) + 1;
    union header* p = malloc(nunits * sizeof(union header));
    if (p)
    {
        p->s.prev = NULL;
        p->s.next = allocator->head;
        p->s.DTOR = F;
        if (allocator->head != NULL)
        {
            allocator->head->s.prev = p;
        }
        allocator->head = p;


        return (void*)(p + 1);
    }
    return NULL;
}

void allocator_free(struct allocator* allocator, void* p)
{
    union header* del = ((union header*)p) - 1;

    if (allocator->head == del)
        allocator->head = del->s.next;

    if (del->s.next != NULL)
        del->s.next->s.prev = del->s.prev;

    if (del->s.prev != NULL)
        del->s.prev->s.next = del->s.next;

    if (del->s.DTOR)
    {
        void* pobj = (void*)((union header*)p + 1);
        del->s.DTOR(pobj);
    }

    free(del);
    return;

}

Ainda não estou usando este allocator, mas  a motivação
são memórias que tem exatamente o mesmo tempo de vida
e que por algum motivo querem este  gerenciamento  
genérico.




Reply all
Reply to author
Forward
0 new messages