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#include "sanitizer.h"
25
26#include "avassert.h"
27#include "error.h"
28#include "macros.h"
29#include "mem.h"
30#include "mem_internal.h"
31#include "thread.h"
32
33#ifndef REFSTRUCT_CHECKED
34#ifndef ASSERT_LEVEL
35#define ASSERT_LEVEL 0
36#endif
37#define REFSTRUCT_CHECKED (ASSERT_LEVEL >= 1)
38#endif
39
40#if REFSTRUCT_CHECKED
41#define ff_assert(cond) av_assert0(cond)
42#else
43#define ff_assert(cond) ((void)0)
44#endif
45
46#define REFSTRUCT_COOKIE AV_NE((uint64_t)MKBETAG('R', 'e', 'f', 'S') << 32 | MKBETAG('t', 'r', 'u', 'c'), \
47 MKTAG('R', 'e', 'f', 'S') | (uint64_t)MKTAG('t', 'r', 'u', 'c') << 32)
48
49#ifndef _MSC_VER
50#define REFCOUNT_OFFSET FFALIGN(sizeof(RefCount), FFMAX(ALIGN_64, _Alignof(max_align_t)))
51#else
52#define REFCOUNT_OFFSET FFALIGN(sizeof(RefCount), ALIGN_64)
53#endif
54
55typedef struct RefCount {
56 /**
57 * An uintptr_t is big enough to hold the address of every reference,
58 * so no overflow can happen when incrementing the refcount as long as
59 * the user does not throw away references.
60 */
64 void (*free)(void *ref);
65
66#if REFSTRUCT_CHECKED
67 uint64_t cookie;
68#endif
69} RefCount;
70
72{
73 RefCount *ref = (RefCount*)((char*)obj - REFCOUNT_OFFSET);
74 ff_assert(ref->cookie == REFSTRUCT_COOKIE);
75 return ref;
76}
77
78static const RefCount *cget_refcount(const void *obj)
79{
80 const RefCount *ref = (const RefCount*)((const char*)obj - REFCOUNT_OFFSET);
81 ff_assert(ref->cookie == REFSTRUCT_COOKIE);
82 return ref;
83}
84
85static void *get_userdata(void *buf)
86{
87 return (char*)buf + REFCOUNT_OFFSET;
88}
89
91 void (*free_cb)(AVRefStructOpaque opaque, void *obj))
92{
93 atomic_init(&ref->refcount, 1);
94 ref->opaque = opaque;
95 ref->free_cb = free_cb;
96 ref->free = av_free;
97
98#if REFSTRUCT_CHECKED
99 ref->cookie = REFSTRUCT_COOKIE;
100#endif
101}
102
104 void (*free_cb)(AVRefStructOpaque opaque, void *obj))
105{
106 void *buf, *obj;
107
108 if (size > SIZE_MAX - REFCOUNT_OFFSET)
109 return NULL;
111 if (!buf)
112 return NULL;
113 refcount_init(buf, opaque, free_cb);
114 obj = get_userdata(buf);
116 memset(obj, 0, size);
117
118 return obj;
119}
120
121void av_refstruct_unref(void *objp)
122{
123 void *obj;
124 RefCount *ref;
125
126 memcpy(&obj, objp, sizeof(obj));
127 if (!obj)
128 return;
129 memcpy(objp, &(void *){ NULL }, sizeof(obj));
130
132 if (atomic_fetch_sub_explicit(&ref->refcount, 1, memory_order_acq_rel) == 1) {
133 if (ref->free_cb)
134 ref->free_cb(ref->opaque, obj);
135 ref->free(ref);
136 }
137
138 return;
139}
140
142{
144
146
147 return obj;
148}
149
150const void *av_refstruct_ref_c(const void *obj)
151{
152 /* Casting const away here is fine, as it is only supposed
153 * to apply to the user's data and not our bookkeeping data. */
154 RefCount *ref = get_refcount((void*)obj);
155
157
158 return obj;
159}
160
161void av_refstruct_replace(void *dstp, const void *src)
162{
163 const void *dst;
164 memcpy(&dst, dstp, sizeof(dst));
165
166 if (src == dst)
167 return;
168 av_refstruct_unref(dstp);
169 if (src) {
171 memcpy(dstp, &dst, sizeof(dst));
172 }
173}
174
176{
177 const RefCount *ref = cget_refcount(obj);
178 /* Casting const away here is safe, because it is a load.
179 * It is necessary because atomic_load_explicit() does not
180 * accept const atomics in C11 (see also N1807). */
182}
183
185 size_t size;
191
193 unsigned entry_flags;
194 unsigned pool_flags;
195
196 /** The number of outstanding entries not in available_entries. */
198 /**
199 * This is a linked list of available entries;
200 * the RefCount's opaque pointer is used as next pointer
201 * for available entries.
202 * While the entries are in use, the opaque is a pointer
203 * to the corresponding AVRefStructPool.
204 */
207};
208
209static void pool_free(AVRefStructPool *pool)
210{
211 ff_mutex_destroy(&pool->mutex);
212 if (pool->free_cb)
213 pool->free_cb(pool->opaque);
214 av_free(get_refcount(pool));
215}
216
218{
219 if (!pool->free_entry_cb)
221 if (pool->free_entry_cb)
222 pool->free_entry_cb(pool->opaque, get_userdata(ref));
223 av_free(ref);
224}
225
226static void pool_return_entry(void *ref_)
227{
228 RefCount *ref = ref_;
229 AVRefStructPool *pool = ref->opaque.nc;
230
231 ff_mutex_lock(&pool->mutex);
232 if (!pool->uninited) {
233 /* An entry with an entry free callback owns allocations while it
234 * rests in the pool. LeakSanitizer does not follow pointers stored
235 * in poisoned memory, so such entries stay addressable. */
236 if (!pool->free_entry_cb)
238 ref->opaque.nc = pool->available_entries;
239 pool->available_entries = ref;
240 ref = NULL;
241 }
242 ff_mutex_unlock(&pool->mutex);
243
244 if (ref)
245 pool_free_entry(pool, ref);
246
248 pool_free(pool);
249}
250
251static void pool_reset_entry(AVRefStructOpaque opaque, void *entry)
252{
253 AVRefStructPool *pool = opaque.nc;
254
255 pool->reset_cb(pool->opaque, entry);
256}
257
258static int refstruct_pool_get_ext(void *datap, AVRefStructPool *pool)
259{
260 void *ret = NULL;
261
262 memcpy(datap, &(void *){ NULL }, sizeof(void*));
263
264 ff_mutex_lock(&pool->mutex);
265 ff_assert(!pool->uninited);
266 if (pool->available_entries) {
268 ret = get_userdata(ref);
269 if (!pool->free_entry_cb)
270 FF_ASAN_UNPOISON(ret, pool->size);
271 /* Entries are zeroed once at allocation and a user may rely on never
272 * written parts staying zero, so only a pool that never zeroes and
273 * keeps no state through callbacks gets its reused entries marked
274 * undefined. */
277 !pool->init_cb && !pool->reset_cb && !pool->free_entry_cb)
278 FF_MEM_UNDEFINED(ret, pool->size);
279 pool->available_entries = ref->opaque.nc;
280 ref->opaque.nc = pool;
281 atomic_init(&ref->refcount, 1);
282 }
283 ff_mutex_unlock(&pool->mutex);
284
285 if (!ret) {
286 RefCount *ref;
287 ret = av_refstruct_alloc_ext(pool->size, pool->entry_flags, pool,
288 pool->reset_cb ? pool_reset_entry : NULL);
289 if (!ret)
290 return AVERROR(ENOMEM);
291 ref = get_refcount(ret);
292 ref->free = pool_return_entry;
293 if (pool->init_cb) {
294 int err = pool->init_cb(pool->opaque, ret);
295 if (err < 0) {
297 pool->reset_cb(pool->opaque, ret);
299 pool->free_entry_cb(pool->opaque, ret);
300 av_free(ref);
301 return err;
302 }
303 }
304 }
306
308 memset(ret, 0, pool->size);
309
310 memcpy(datap, &ret, sizeof(ret));
311
312 return 0;
313}
314
316{
317 void *ret;
318 refstruct_pool_get_ext(&ret, pool);
319 return ret;
320}
321
322/**
323 * Hint: The content of pool_unref() and refstruct_pool_uninit()
324 * could currently be merged; they are only separate functions
325 * in case we would ever introduce weak references.
326 */
327static void pool_unref(void *ref)
328{
331 pool_free(pool);
332}
333
335{
336 AVRefStructPool *pool = obj;
338
339 ff_mutex_lock(&pool->mutex);
340 ff_assert(!pool->uninited);
341 pool->uninited = 1;
342 entry = pool->available_entries;
343 pool->available_entries = NULL;
344 ff_mutex_unlock(&pool->mutex);
345
346 while (entry) {
347 void *next = entry->opaque.nc;
348 pool_free_entry(pool, entry);
349 entry = next;
350 }
351}
352
357
359 AVRefStructOpaque opaque,
360 int (*init_cb)(AVRefStructOpaque opaque, void *obj),
361 void (*reset_cb)(AVRefStructOpaque opaque, void *obj),
362 void (*free_entry_cb)(AVRefStructOpaque opaque, void *obj),
363 void (*free_cb)(AVRefStructOpaque opaque))
364{
365 AVRefStructPool *pool = av_refstruct_alloc_ext(sizeof(*pool), 0, NULL,
367 int err;
368
369 if (!pool)
370 return NULL;
372
373 pool->size = size;
374 pool->opaque = opaque;
375 pool->init_cb = init_cb;
376 pool->reset_cb = reset_cb;
377 pool->free_entry_cb = free_entry_cb;
378 pool->free_cb = free_cb;
379#define COMMON_FLAGS AV_REFSTRUCT_POOL_FLAG_NO_ZEROING
381 // Filter out nonsense combinations to avoid checks later.
382 if (!pool->reset_cb)
384 if (!pool->free_entry_cb)
386 pool->pool_flags = flags;
387
389 // We will zero the buffer before every use, so zeroing
390 // upon allocating the buffer is unnecessary.
392 }
393
394 atomic_init(&pool->refcount, 1);
395
396 err = ff_mutex_init(&pool->mutex, NULL);
397 if (err) {
398 // Don't call av_refstruct_uninit() on pool, as it hasn't been properly
399 // set up and is just a POD right now.
400 av_free(get_refcount(pool));
401 return NULL;
402 }
403 return pool;
404}
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
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.
static const char * obj
Definition mscl.c:57
#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:353
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:121
void av_refstruct_replace(void *dstp, const void *src)
Ensure *dstp refers to the same object as src.
Definition refstruct.c:161
static RefCount * get_refcount(void *obj)
Definition refstruct.c:71
const void * av_refstruct_ref_c(const void *obj)
Analog of av_refstruct_ref(), but for constant objects.
Definition refstruct.c:150
static void pool_free_entry(AVRefStructPool *pool, RefCount *ref)
Definition refstruct.c:217
static void refcount_init(RefCount *ref, AVRefStructOpaque opaque, void(*free_cb)(AVRefStructOpaque opaque, void *obj))
Definition refstruct.c:90
static void * get_userdata(void *buf)
Definition refstruct.c:85
static const RefCount * cget_refcount(const void *obj)
Definition refstruct.c:78
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:315
#define REFCOUNT_OFFSET
Definition refstruct.c:50
#define REFSTRUCT_COOKIE
Definition refstruct.c:46
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:103
#define ff_assert(cond)
Definition refstruct.c:43
int av_refstruct_exclusive(const void *obj)
Check whether the reference count of an object managed via this API is 1.
Definition refstruct.c:175
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition refstruct.c:141
#define COMMON_FLAGS
static void pool_free(AVRefStructPool *pool)
Definition refstruct.c:209
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:358
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:327
static void pool_return_entry(void *ref_)
Definition refstruct.c:226
static void pool_reset_entry(AVRefStructOpaque opaque, void *entry)
Definition refstruct.c:251
static int refstruct_pool_get_ext(void *datap, AVRefStructPool *pool)
Definition refstruct.c:258
static void refstruct_pool_uninit(AVRefStructOpaque unused, void *obj)
Definition refstruct.c:334
#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
#define FF_ASAN_POISON(ptr, size)
Definition sanitizer.h:55
#define FF_MEM_UNDEFINED(ptr, size)
Definition sanitizer.h:67
#define FF_ASAN_UNPOISON(ptr, size)
Definition sanitizer.h:56
@ memory_order_relaxed
Definition stdatomic.h:29
@ memory_order_acq_rel
Definition stdatomic.h:33
@ memory_order_acquire
Definition stdatomic.h:31
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:297
#define atomic_load_explicit(object, order)
Definition stdatomic.h:247
uintptr_t atomic_uintptr_t
Definition stdatomic.h:90
#define atomic_init(obj, value)
Definition stdatomic.h:119
#define atomic_fetch_sub_explicit(object, operand, order)
Definition stdatomic.h:300
AVRefStructPool is an API for a thread-safe pool of objects managed via the RefStruct API.
Definition refstruct.c:184
unsigned pool_flags
Definition refstruct.c:194
int(* init_cb)(AVRefStructOpaque opaque, void *obj)
Definition refstruct.c:187
void(* reset_cb)(AVRefStructOpaque opaque, void *obj)
Definition refstruct.c:188
void(* free_cb)(AVRefStructOpaque opaque)
Definition refstruct.c:190
AVRefStructOpaque opaque
Definition refstruct.c:186
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:205
void(* free_entry_cb)(AVRefStructOpaque opaque, void *obj)
Definition refstruct.c:189
unsigned entry_flags
Definition refstruct.c:193
atomic_uintptr_t refcount
The number of outstanding entries not in available_entries.
Definition refstruct.c:197
void(* free)(void *ref)
Definition refstruct.c:64
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:61
void(* free_cb)(AVRefStructOpaque opaque, void *obj)
Definition refstruct.c:63
AVRefStructOpaque opaque
Definition refstruct.c:62
#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