Skip to content

thread safety #3

@zleihao

Description

@zleihao

Hello,

First of all, thanks for this small and clean pool allocator — it has been very
useful in my project.

I am currently using libpool in a multithreaded environment and noticed that the
current implementation is not thread-safe (e.g. concurrent pool_alloc /
pool_free on the same Pool instance).

I would like to ask whether you would consider adding an optional thread-safe
mode in the future, for example by protecting the free list with a mutex inside
struct Pool.

One possible approach could be:

struct Pool {
    void* free_chunk;
    ArrayStart* array_starts;
    size_t chunk_sz;
#ifdef LIBPOOL_THREAD_SAFE
    pthread_mutex_t lock;
#endif
};

Pool* pool_new(size_t pool_sz, size_t chunk_sz) {
    Pool* pool;
    char* arr;
    size_t i;

    if (pool_sz == 0)
        return NULL;

   //.....

#ifdef LIBPOOL_THREAD_SAFE
    pthread_mutex_init(&pool->lock, NULL);
#endif

    return pool;
}

void* pool_alloc(Pool* pool) {
    void* result;

    if (pool == NULL)
        return NULL;
    VALGRIND_MAKE_MEM_DEFINED(pool, sizeof(Pool));

#ifdef LIBPOOL_THREAD_SAFE
    pthread_mutex_lock(&pool->lock);  //Added
#endif

    if (pool->free_chunk == NULL) {
#ifdef LIBPOOL_THREAD_SAFE
        pthread_mutex_unlock(&pool->lock);
#endif
        return NULL;
    }
    VALGRIND_MAKE_MEM_DEFINED(pool->free_chunk, sizeof(void**));

    result           = pool->free_chunk;
    pool->free_chunk = *(void**)pool->free_chunk;

#ifdef LIBPOOL_THREAD_SAFE
    pthread_mutex_unlock(&pool->lock);  //Added
#endif

    VALGRIND_MEMPOOL_ALLOC(pool, result, pool->chunk_sz);
    VALGRIND_MAKE_MEM_NOACCESS(pool->free_chunk, sizeof(void**));
    VALGRIND_MAKE_MEM_NOACCESS(pool, sizeof(Pool));

    return result;
}

void pool_free(Pool* pool, void* ptr) {
    if (pool == NULL || ptr == NULL)
        return;

    VALGRIND_MAKE_MEM_DEFINED(pool, sizeof(Pool));

#ifdef LIBPOOL_THREAD_SAFE
    pthread_mutex_lock(&pool->lock);
#endif
        
    *(void**)ptr     = pool->free_chunk;
    pool->free_chunk = ptr;

#ifdef LIBPOOL_THREAD_SAFE
    pthread_mutex_unlock(&pool->lock);  //Added
#endif

    VALGRIND_MAKE_MEM_NOACCESS(pool, sizeof(Pool));
    VALGRIND_MEMPOOL_FREE(pool, ptr);
}

The same mutex would also guard pool_expand and pool_destroy when enabled.

This would keep the current behavior unchanged for single-threaded users, while
making it easier and safer to use libpool in multithreaded programs.

Thanks again for the library!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions