[FFmpeg-devel] [PATCH] avutil/buffer: add a dynamic size buffer pool API

wm4 nfxjfg at googlemail.com
Wed Mar 28 12:59:56 EEST 2018


On Tue, 27 Mar 2018 23:11:27 -0300
James Almer <jamrial at gmail.com> wrote:

> Signed-off-by: James Almer <jamrial at gmail.com>
> ---
> Implemented as a completely separate API as suggested. Missing
> Changelog, APIChanges and version bump as usual.
> 
>  libavutil/buffer.c          | 159 ++++++++++++++++++++++++++++++++++++++++++++
>  libavutil/buffer.h          |  53 +++++++++++++++
>  libavutil/buffer_internal.h |  14 ++++
>  3 files changed, 226 insertions(+)
> 
> diff --git a/libavutil/buffer.c b/libavutil/buffer.c
> index 8d1aa5fa84..c39a14c3c7 100644
> --- a/libavutil/buffer.c
> +++ b/libavutil/buffer.c
> @@ -24,6 +24,7 @@
>  #include "common.h"
>  #include "mem.h"
>  #include "thread.h"
> +#include "tree.h"
>  
>  AVBufferRef *av_buffer_create(uint8_t *data, int size,
>                                void (*free)(void *opaque, uint8_t *data),
> @@ -355,3 +356,161 @@ AVBufferRef *av_buffer_pool_get(AVBufferPool *pool)
>  
>      return ret;
>  }
> +
> +AVBufferDynPool *av_buffer_dyn_pool_init(AVBufferRef* (*alloc)(int size))
> +{
> +    AVBufferDynPool *pool = av_mallocz(sizeof(*pool));
> +    if (!pool)
> +        return NULL;
> +
> +    ff_mutex_init(&pool->mutex, NULL);
> +
> +    pool->alloc = alloc ? alloc : av_buffer_alloc;
> +
> +    atomic_init(&pool->refcount, 1);
> +
> +    return pool;
> +}
> +
> +static int free_node(void *opaque, void *elem)
> +{
> +    BufferPoolEntry *buf = elem;
> +
> +    buf->free(buf->opaque, buf->data);
> +    av_free(buf);
> +
> +    return 0;
> +}
> +
> +static void buffer_dyn_pool_free(AVBufferDynPool *pool)
> +{
> +    av_tree_enumerate(pool->root, NULL, NULL, free_node);
> +    av_tree_destroy(pool->root);
> +
> +    ff_mutex_destroy(&pool->mutex);
> +
> +    av_freep(&pool);
> +}
> +
> +void av_buffer_dyn_pool_uninit(AVBufferDynPool **ppool)
> +{
> +    AVBufferDynPool *pool;
> +
> +    if (!ppool || !*ppool)
> +        return;
> +    pool   = *ppool;
> +    *ppool = NULL;
> +
> +    if (atomic_fetch_add_explicit(&pool->refcount, -1, memory_order_acq_rel) == 1)
> +        buffer_dyn_pool_free(pool);
> +}
> +
> +static int cmp_insert(const void *key, const void *node)
> +{
> +    int ret = ((const BufferPoolEntry *) key)->size - ((const BufferPoolEntry *) node)->size;
> +
> +    if (!ret)
> +        ret = ((const BufferPoolEntry *) key)->data - ((const BufferPoolEntry *) node)->data;
> +    return ret;
> +}
> +
> +static void pool_release_dyn_buffer(void *opaque, uint8_t *data)
> +{
> +    BufferPoolEntry *buf = opaque;
> +    AVBufferDynPool *pool = buf->dynpool;
> +
> +    if(CONFIG_MEMORY_POISONING)
> +        memset(buf->data, FF_MEMORY_POISON, buf->size);
> +
> +    ff_mutex_lock(&pool->mutex);
> +    /* Add the buffer into the pool, using the preallocated
> +     * AVTreeNode stored in buf->node */
> +    av_tree_insert(&pool->root, buf, cmp_insert, &buf->node);
> +    ff_mutex_unlock(&pool->mutex);
> +
> +    if (atomic_fetch_add_explicit(&pool->refcount, -1, memory_order_acq_rel) == 1)
> +        buffer_dyn_pool_free(pool);
> +}
> +
> +static AVBufferRef *pool_alloc_dyn_buffer(AVBufferDynPool *pool, int size)
> +{
> +    BufferPoolEntry *buf;
> +    AVBufferRef     *ret;
> +
> +    ret = pool->alloc(size);
> +    if (!ret)
> +        return NULL;
> +
> +    buf = av_mallocz(sizeof(*buf));
> +    if (!buf) {
> +        av_buffer_unref(&ret);
> +        return NULL;
> +    }
> +
> +    buf->node = av_tree_node_alloc();
> +    if (!buf->node) {
> +        av_free(buf);
> +        av_buffer_unref(&ret);
> +        return NULL;
> +    }
> +
> +    buf->data    = ret->buffer->data;
> +    buf->opaque  = ret->buffer->opaque;
> +    buf->free    = ret->buffer->free;
> +    buf->size    = size;
> +    buf->dynpool = pool;
> +
> +    ret->buffer->opaque = buf;
> +    ret->buffer->free   = pool_release_dyn_buffer;
> +
> +    return ret;
> +}
> +
> +static int cmp_find(const void *key, const void *node)
> +{
> +    return *(const int *)key - ((const BufferPoolEntry *) node)->size;
> +}
> +
> +AVBufferRef *av_buffer_dyn_pool_get(AVBufferDynPool *pool, int size)
> +{
> +    AVBufferRef *ret;
> +    BufferPoolEntry *buf, *next[2] = { NULL, NULL };
> +
> +    ff_mutex_lock(&pool->mutex);
> +    /* Find a big enough buffer in the pool. */
> +    buf = av_tree_find(pool->root, &size, cmp_find, (void **)next);
> +
> +    if (!buf)
> +        /* If none of the requested size exists, use a bigger one. */
> +        buf = next[1];
> +    if (!buf && (buf = next[0])) {
> +        /* If the pool also doesn't have a bigger buffer, but does
> +         * have a smaller one, then replace it with a new buffer of
> +         * the requested size. */
> +        av_tree_insert(&pool->root, buf, cmp_insert, &buf->node);
> +        buf->free(buf->opaque, buf->data);
> +        av_free(buf->node);
> +        av_freep(&buf);
> +    }
> +
> +    if (buf) {
> +        ret = av_buffer_create(buf->data, buf->size, pool_release_dyn_buffer,
> +                               buf, 0);
> +        if (ret) {
> +            /* Remove the buffer from the pool. Zero and store the
> +             * AVTreeNode used for it in buf->node so we can use it
> +             * again once the buffer is put back in the pool. */
> +            av_tree_insert(&pool->root, buf, cmp_insert, &buf->node);
> +            memset(buf->node, 0, av_tree_node_size);
> +            ret->size = size;
> +        }
> +    } else {
> +        ret = pool_alloc_dyn_buffer(pool, size);
> +    }
> +    ff_mutex_unlock(&pool->mutex);
> +
> +    if (ret)
> +        atomic_fetch_add_explicit(&pool->refcount, 1, memory_order_relaxed);
> +
> +    return ret;
> +}
> diff --git a/libavutil/buffer.h b/libavutil/buffer.h
> index 73b6bd0b14..d06b301fe5 100644
> --- a/libavutil/buffer.h
> +++ b/libavutil/buffer.h
> @@ -284,6 +284,59 @@ void av_buffer_pool_uninit(AVBufferPool **pool);
>   */
>  AVBufferRef *av_buffer_pool_get(AVBufferPool *pool);
>  
> +/**
> + * @}
> + */
> +
> +/**
> + * @defgroup lavu_bufferdynpool AVBufferDynPool
> + * @ingroup lavu_data
> + *
> + * @{
> + * AVBufferDynPool is an API for a lock-free thread-safe pool of AVBuffers.
> + *
> + * Unlike AVBufferPool, AVBufferDynPool allows the user to request buffers
> + * of any arbitrary size. It is functionally the same otherwise.
> + */
> +
> +/**
> + * The buffer pool. This structure is opaque and not meant to be accessed
> + * directly. It is allocated with av_buffer_dyn_pool_init() and freed with
> + * av_buffer_dyn_pool_uninit().
> + */
> +typedef struct AVBufferDynPool AVBufferDynPool;
> +
> +/**
> + * Allocate and initialize a buffer pool.
> + *
> + * @param alloc a function that will be used to allocate new buffers when the
> + * pool is empty. May be NULL, then the default allocator will be used
> + * (av_buffer_alloc()).
> + * @return newly created buffer pool on success, NULL on error.
> + */
> +AVBufferDynPool *av_buffer_dyn_pool_init(AVBufferRef* (*alloc)(int size));

No custom free pool free function like the fixed buffer API? (Not sure if neded.)
> +
> +/**
> + * Mark the pool as being available for freeing. It will actually be freed only
> + * once all the allocated buffers associated with the pool are released. Thus it
> + * is safe to call this function while some of the allocated buffers are still
> + * in use.
> + *
> + * @param pool pointer to the pool to be freed. It will be set to NULL.
> + */
> +void av_buffer_dyn_pool_uninit(AVBufferDynPool **pool);
> +
> +/**
> + * Allocate a new AVBuffer, reusing an old buffer from the pool when available.
> + * This function may be called simultaneously from multiple threads.
> + *
> + * @param pool pointer to an initialized pool.
> + * @param size Required buffer size in bytes.
> + *
> + * @return a reference to the new buffer on success, NULL on error.
> + */
> +AVBufferRef *av_buffer_dyn_pool_get(AVBufferDynPool *pool, int size);
> +
>  /**
>   * @}
>   */
> diff --git a/libavutil/buffer_internal.h b/libavutil/buffer_internal.h
> index 54b67047e5..2c0e9ea063 100644
> --- a/libavutil/buffer_internal.h
> +++ b/libavutil/buffer_internal.h
> @@ -61,6 +61,7 @@ struct AVBuffer {
>  
>  typedef struct BufferPoolEntry {
>      uint8_t *data;
> +    size_t   size;
>  
>      /*
>       * Backups of the original opaque/free of the AVBuffer corresponding to
> @@ -71,6 +72,9 @@ typedef struct BufferPoolEntry {
>  
>      AVBufferPool *pool;
>      struct BufferPoolEntry *next;
> +
> +    AVBufferDynPool *dynpool;
> +    struct AVTreeNode *node;
>  } BufferPoolEntry;
>  
>  struct AVBufferPool {
> @@ -95,4 +99,14 @@ struct AVBufferPool {
>      void         (*pool_free)(void *opaque);
>  };
>  
> +struct AVBufferDynPool {
> +    AVMutex mutex;
> +    struct AVTreeNode *root;
> +
> +    atomic_uint refcount;
> +
> +    int size;
> +    AVBufferRef* (*alloc)(int size);
> +};
> +
>  #endif /* AVUTIL_BUFFER_INTERNAL_H */



More information about the ffmpeg-devel mailing list