On Tue, 27 Mar 2018 23:11:27 -0300 James Almer <jamr...@gmail.com> wrote:
> Signed-off-by: James Almer <jamr...@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 */ _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel