FFmpeg
Loading...
Searching...
No Matches
refstruct.c
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include <stdatomic.h>
20#include <stdint.h>
21#include <string.h>
22
23#include "refstruct.h"
24
25#include "avassert.h"
26#include "error.h"
27#include "macros.h"
28#include "mem.h"
29#include "mem_internal.h"
30#include "thread.h"
31
32#ifndef REFSTRUCT_CHECKED
33#ifndef ASSERT_LEVEL
34#define ASSERT_LEVEL 0
35#endif
36#define REFSTRUCT_CHECKED (ASSERT_LEVEL >= 1)
37#endif
38
39#if REFSTRUCT_CHECKED
40#define ff_assert(cond) av_assert0(cond)
41#else
42#define ff_assert(cond) ((void)0)
43#endif
44
45#define REFSTRUCT_COOKIE AV_NE((uint64_t)MKBETAG('R', 'e', 'f', 'S') << 32 | MKBETAG('t', 'r', 'u', 'c'), \
46 MKTAG('R', 'e', 'f', 'S') | (uint64_t)MKTAG('t', 'r', 'u', 'c') << 32)
47
48#ifndef _MSC_VER
49#define REFCOUNT_OFFSET FFALIGN(sizeof(RefCount), FFMAX(ALIGN_64, _Alignof(max_align_t)))
50#else
51#define REFCOUNT_OFFSET FFALIGN(sizeof(RefCount), ALIGN_64)
52#endif
53
54typedef struct RefCount {
55 /**
56 * An uintptr_t is big enough to hold the address of every reference,
57 * so no overflow can happen when incrementing the refcount as long as
58 * the user does not throw away references.
59 */
62 void (*free_cb)(AVRefStructOpaque opaque, void *obj);
63 void (*free)(void *ref);
64
65#if REFSTRUCT_CHECKED
66 uint64_t cookie;
67#endif
68} RefCount;
69
70static RefCount *get_refcount(void *obj)
71{
72 RefCount *ref = (RefCount*)((char*)obj - REFCOUNT_OFFSET);
73 ff_assert(ref->cookie == REFSTRUCT_COOKIE);
74 return ref;
75}
76
77static const RefCount *cget_refcount(const void *obj)
78{
79 const RefCount *ref = (const RefCount*)((const char*)obj - REFCOUNT_OFFSET);
80 ff_assert(ref->cookie == REFSTRUCT_COOKIE);
81 return ref;
82}
83
84static void *get_userdata(void *buf)
85{
86 return (char*)buf + REFCOUNT_OFFSET;
87}
88
90 void (*free_cb)(AVRefStructOpaque opaque, void *obj))
91{
92 atomic_init(&ref->refcount, 1);
93 ref->opaque = opaque;
94 ref->free_cb = free_cb;
95 ref->free = av_free;
96
97#if REFSTRUCT_CHECKED
98 ref->cookie = REFSTRUCT_COOKIE;
99#endif
100}
101
103 void (*free_cb)(AVRefStructOpaque opaque, void *obj))
104{
105 void *buf, *obj;
106
107 if (size > SIZE_MAX - REFCOUNT_OFFSET)
108 return NULL;
110 if (!buf)
111 return NULL;
112 refcount_init(buf, opaque, free_cb);
113 obj = get_userdata(buf);
115 memset(obj, 0, size);
116
117 return obj;
118}
119
120void av_refstruct_unref(void *objp)
121{
122 void *obj;
123 RefCount *ref;
124
125 memcpy(&obj, objp, sizeof(obj));
126 if (!obj)
127 return;
128 memcpy(objp, &(void *){ NULL }, sizeof(obj));
129
130 ref = get_refcount(obj);
131 if (atomic_fetch_sub_explicit(&ref->refcount, 1, memory_order_acq_rel) == 1) {
132 if (ref->free_cb)
133 ref->free_cb(ref->opaque, obj);
134 ref->free(ref);
135 }
136
137 return;
138}
139
140void *av_refstruct_ref(void *obj)
141{
142 RefCount *ref = get_refcount(obj);
143
144 atomic_fetch_add_explicit(&ref->refcount, 1, memory_order_relaxed);
145
146 return obj;
147}
148
149const void *av_refstruct_ref_c(const void *obj)
150{
151 /* Casting const away here is fine, as it is only supposed
152 * to apply to the user's data and not our bookkeeping data. */
153 RefCount *ref = get_refcount((void*)obj);
154
155 atomic_fetch_add_explicit(&ref->refcount, 1, memory_order_relaxed);
156
157 return obj;
158}
159
160void av_refstruct_replace(void *dstp, const void *src)
161{
162 const void *dst;
163 memcpy(&dst, dstp, sizeof(dst));
164
165 if (src == dst)
166 return;
167 av_refstruct_unref(dstp);
168 if (src) {
170 memcpy(dstp, &dst, sizeof(dst));
171 }
172}
173
174int av_refstruct_exclusive(const void *obj)
175{
176 const RefCount *ref = cget_refcount(obj);
177 /* Casting const away here is safe, because it is a load.
178 * It is necessary because atomic_load_explicit() does not
179 * accept const atomics in C11 (see also N1807). */
180 return atomic_load_explicit((atomic_uintptr_t*)&ref->refcount, memory_order_acquire) == 1;
181}
182
184 size_t size;
186 int (*init_cb)(AVRefStructOpaque opaque, void *obj);
187 void (*reset_cb)(AVRefStructOpaque opaque, void *obj);
190
192 unsigned entry_flags;
193 unsigned pool_flags;
194
195 /** The number of outstanding entries not in available_entries. */
197 /**
198 * This is a linked list of available entries;
199 * the RefCount's opaque pointer is used as next pointer
200 * for available entries.
201 * While the entries are in use, the opaque is a pointer
202 * to the corresponding AVRefStructPool.
203 */
206};
207
208static void pool_free(AVRefStructPool *pool)
209{
210 ff_mutex_destroy(&pool->mutex);
211 if (pool->free_cb)
212 pool->free_cb(pool->opaque);
213 av_free(get_refcount(pool));
214}
215
217{
218 if (pool->free_entry_cb)
219 pool->free_entry_cb(pool->opaque, get_userdata(ref));
220 av_free(ref);
221}
222
223static void pool_return_entry(void *ref_)
224{
225 RefCount *ref = ref_;
226 AVRefStructPool *pool = ref->opaque.nc;
227
228 ff_mutex_lock(&pool->mutex);
229 if (!pool->uninited) {
230 ref->opaque.nc = pool->available_entries;
231 pool->available_entries = ref;
232 ref = NULL;
233 }
234 ff_mutex_unlock(&pool->mutex);
235
236 if (ref)
237 pool_free_entry(pool, ref);
238
239 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
240 pool_free(pool);
241}
242
243static void pool_reset_entry(AVRefStructOpaque opaque, void *entry)
244{
245 AVRefStructPool *pool = opaque.nc;
246
247 pool->reset_cb(pool->opaque, entry);
248}
249
250static int refstruct_pool_get_ext(void *datap, AVRefStructPool *pool)
251{
252 void *ret = NULL;
253
254 memcpy(datap, &(void *){ NULL }, sizeof(void*));
255
256 ff_mutex_lock(&pool->mutex);
257 ff_assert(!pool->uninited);
258 if (pool->available_entries) {
260 ret = get_userdata(ref);
261 pool->available_entries = ref->opaque.nc;
262 ref->opaque.nc = pool;
263 atomic_init(&ref->refcount, 1);
264 }
265 ff_mutex_unlock(&pool->mutex);
266
267 if (!ret) {
268 RefCount *ref;
269 ret = av_refstruct_alloc_ext(pool->size, pool->entry_flags, pool,
270 pool->reset_cb ? pool_reset_entry : NULL);
271 if (!ret)
272 return AVERROR(ENOMEM);
273 ref = get_refcount(ret);
274 ref->free = pool_return_entry;
275 if (pool->init_cb) {
276 int err = pool->init_cb(pool->opaque, ret);
277 if (err < 0) {
279 pool->reset_cb(pool->opaque, ret);
281 pool->free_entry_cb(pool->opaque, ret);
282 av_free(ref);
283 return err;
284 }
285 }
286 }
287 atomic_fetch_add_explicit(&pool->refcount, 1, memory_order_relaxed);
288
290 memset(ret, 0, pool->size);
291
292 memcpy(datap, &ret, sizeof(ret));
293
294 return 0;
295}
296
298{
299 void *ret;
300 refstruct_pool_get_ext(&ret, pool);
301 return ret;
302}
303
304/**
305 * Hint: The content of pool_unref() and refstruct_pool_uninit()
306 * could currently be merged; they are only separate functions
307 * in case we would ever introduce weak references.
308 */
309static void pool_unref(void *ref)
310{
312 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1)
313 pool_free(pool);
314}
315
316static void refstruct_pool_uninit(AVRefStructOpaque unused, void *obj)
317{
318 AVRefStructPool *pool = obj;
320
321 ff_mutex_lock(&pool->mutex);
322 ff_assert(!pool->uninited);
323 pool->uninited = 1;
324 entry = pool->available_entries;
325 pool->available_entries = NULL;
326 ff_mutex_unlock(&pool->mutex);
327
328 while (entry) {
329 void *next = entry->opaque.nc;
330 pool_free_entry(pool, entry);
331 entry = next;
332 }
333}
334
339
341 AVRefStructOpaque opaque,
342 int (*init_cb)(AVRefStructOpaque opaque, void *obj),
343 void (*reset_cb)(AVRefStructOpaque opaque, void *obj),
344 void (*free_entry_cb)(AVRefStructOpaque opaque, void *obj),
345 void (*free_cb)(AVRefStructOpaque opaque))
346{
347 AVRefStructPool *pool = av_refstruct_alloc_ext(sizeof(*pool), 0, NULL,
349 int err;
350
351 if (!pool)
352 return NULL;
354
355 pool->size = size;
356 pool->opaque = opaque;
357 pool->init_cb = init_cb;
358 pool->reset_cb = reset_cb;
359 pool->free_entry_cb = free_entry_cb;
360 pool->free_cb = free_cb;
361#define COMMON_FLAGS AV_REFSTRUCT_POOL_FLAG_NO_ZEROING
363 // Filter out nonsense combinations to avoid checks later.
364 if (!pool->reset_cb)
366 if (!pool->free_entry_cb)
368 pool->pool_flags = flags;
369
371 // We will zero the buffer before every use, so zeroing
372 // upon allocating the buffer is unnecessary.
374 }
375
376 atomic_init(&pool->refcount, 1);
377
378 err = ff_mutex_init(&pool->mutex, NULL);
379 if (err) {
380 // Don't call av_refstruct_uninit() on pool, as it hasn't been properly
381 // set up and is just a POD right now.
382 av_free(get_refcount(pool));
383 return NULL;
384 }
385 return pool;
386}
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
#define entry
simple assert() macros that are a bit more flexible than ISO C assert().
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define NULL
Definition coverity.c:32
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:149
#define atomic_load_explicit(object, order)
Definition stdatomic.h:96
intptr_t atomic_uintptr_t
Definition stdatomic.h:79
#define atomic_init(obj, value)
Definition stdatomic.h:33
#define atomic_fetch_sub_explicit(object, operand, order)
Definition stdatomic.h:152
error code definitions
#define AVERROR(e)
Definition error.h:45
static int ff_mutex_unlock(AVMutex *mutex)
Definition thread.h:189
static int ff_mutex_lock(AVMutex *mutex)
Definition thread.h:188
static int ff_mutex_destroy(AVMutex *mutex)
Definition thread.h:190
#define AVMutex
Definition thread.h:184
static int ff_mutex_init(AVMutex *mutex, const void *attr)
Definition thread.h:187
Utility Preprocessor macros.
Memory handling functions.
#define av_malloc(s)
Definition ops_static.c:52
AVRefStructPool * av_refstruct_pool_alloc(size_t size, unsigned flags)
Equivalent to av_refstruct_pool_alloc(size, flags, NULL, NULL, NULL, NULL, NULL)
Definition refstruct.c:335
void av_refstruct_unref(void *objp)
Decrement the reference count of the underlying object and automatically free the object if there are...
Definition refstruct.c:120
void av_refstruct_replace(void *dstp, const void *src)
Ensure *dstp refers to the same object as src.
Definition refstruct.c:160
static RefCount * get_refcount(void *obj)
Definition refstruct.c:70
const void * av_refstruct_ref_c(const void *obj)
Analog of av_refstruct_ref(), but for constant objects.
Definition refstruct.c:149
static void pool_free_entry(AVRefStructPool *pool, RefCount *ref)
Definition refstruct.c:216
static void refcount_init(RefCount *ref, AVRefStructOpaque opaque, void(*free_cb)(AVRefStructOpaque opaque, void *obj))
Definition refstruct.c:89
static void * get_userdata(void *buf)
Definition refstruct.c:84
static const RefCount * cget_refcount(const void *obj)
Definition refstruct.c:77
void * av_refstruct_pool_get(AVRefStructPool *pool)
Get an object from the pool, reusing an old one from the pool when available.
Definition refstruct.c:297
#define REFCOUNT_OFFSET
Definition refstruct.c:49
#define REFSTRUCT_COOKIE
Definition refstruct.c:45
void * av_refstruct_alloc_ext_c(size_t size, unsigned flags, AVRefStructOpaque opaque, void(*free_cb)(AVRefStructOpaque opaque, void *obj))
Allocate a refcounted object of usable size size managed via the RefStruct API.
Definition refstruct.c:102
#define ff_assert(cond)
Definition refstruct.c:42
int av_refstruct_exclusive(const void *obj)
Check whether the reference count of an object managed via this API is 1.
Definition refstruct.c:174
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition refstruct.c:140
#define COMMON_FLAGS
static void pool_free(AVRefStructPool *pool)
Definition refstruct.c:208
AVRefStructPool * av_refstruct_pool_alloc_ext_c(size_t size, unsigned flags, AVRefStructOpaque opaque, int(*init_cb)(AVRefStructOpaque opaque, void *obj), void(*reset_cb)(AVRefStructOpaque opaque, void *obj), void(*free_entry_cb)(AVRefStructOpaque opaque, void *obj), void(*free_cb)(AVRefStructOpaque opaque))
Allocate an AVRefStructPool, potentially using complex callbacks.
Definition refstruct.c:340
static void pool_unref(void *ref)
Hint: The content of pool_unref() and refstruct_pool_uninit() could currently be merged; they are onl...
Definition refstruct.c:309
static void pool_return_entry(void *ref_)
Definition refstruct.c:223
static void pool_reset_entry(AVRefStructOpaque opaque, void *entry)
Definition refstruct.c:243
static int refstruct_pool_get_ext(void *datap, AVRefStructPool *pool)
Definition refstruct.c:250
static void refstruct_pool_uninit(AVRefStructOpaque unused, void *obj)
Definition refstruct.c:316
#define AV_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME
If this flag is set, the entries will be zeroed before being returned to the user (after the init or ...
Definition refstruct.h:221
#define AV_REFSTRUCT_POOL_FLAG_FREE_ON_INIT_ERROR
If this flag is set and both init_cb and free_entry_cb callbacks are provided, then free_cb will be c...
Definition refstruct.h:213
#define AV_REFSTRUCT_POOL_FLAG_RESET_ON_INIT_ERROR
If this flag is set and both init_cb and reset_cb callbacks are provided, then reset_cb will be calle...
Definition refstruct.h:202
static void * av_refstruct_alloc_ext(size_t size, unsigned flags, void *opaque, void(*free_cb)(AVRefStructOpaque opaque, void *obj))
A wrapper around av_refstruct_alloc_ext_c() for the common case of a non-const qualified opaque.
Definition refstruct.h:94
static AVRefStructPool * av_refstruct_pool_alloc_ext(size_t size, unsigned flags, void *opaque, int(*init_cb)(AVRefStructOpaque opaque, void *obj), void(*reset_cb)(AVRefStructOpaque opaque, void *obj), void(*free_entry_cb)(AVRefStructOpaque opaque, void *obj), void(*free_cb)(AVRefStructOpaque opaque))
A wrapper around av_refstruct_pool_alloc_ext_c() for the common case of a non-const qualified opaque.
Definition refstruct.h:258
#define AV_REFSTRUCT_FLAG_NO_ZEROING
If this flag is set in av_refstruct_alloc_ext_c(), the object will not be initially zeroed.
Definition refstruct.h:67
AVRefStructPool is an API for a thread-safe pool of objects managed via the RefStruct API.
Definition refstruct.c:183
unsigned pool_flags
Definition refstruct.c:193
int(* init_cb)(AVRefStructOpaque opaque, void *obj)
Definition refstruct.c:186
void(* reset_cb)(AVRefStructOpaque opaque, void *obj)
Definition refstruct.c:187
void(* free_cb)(AVRefStructOpaque opaque)
Definition refstruct.c:189
AVRefStructOpaque opaque
Definition refstruct.c:185
RefCount * available_entries
This is a linked list of available entries; the RefCount's opaque pointer is used as next pointer for...
Definition refstruct.c:204
void(* free_entry_cb)(AVRefStructOpaque opaque, void *obj)
Definition refstruct.c:188
unsigned entry_flags
Definition refstruct.c:192
atomic_uintptr_t refcount
The number of outstanding entries not in available_entries.
Definition refstruct.c:196
void(* free)(void *ref)
Definition refstruct.c:63
atomic_uintptr_t refcount
An uintptr_t is big enough to hold the address of every reference, so no overflow can happen when inc...
Definition refstruct.c:60
void(* free_cb)(AVRefStructOpaque opaque, void *obj)
Definition refstruct.c:62
AVRefStructOpaque opaque
Definition refstruct.c:61
#define av_free(p)
#define src
Definition vp8dsp.c:248
static int ref[MAX_W *MAX_W]
int size
RefStruct is an API for creating reference-counted objects with minimal overhead.
Definition refstruct.h:58