FFmpeg
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 "libavutil/avassert.h"
26 #include "libavutil/error.h"
27 #include "libavutil/macros.h"
28 #include "libavutil/mem.h"
29 #include "libavutil/mem_internal.h"
30 #include "libavutil/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 #if __STDC_VERSION__ >= 201112L && !defined(_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 
54 typedef 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)(FFRefStructOpaque opaque, void *obj);
63  void (*free)(void *ref);
64 
65 #if REFSTRUCT_CHECKED
66  uint64_t cookie;
67 #endif
68 } RefCount;
69 
70 static 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 
77 static 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 
84 static void *get_userdata(void *buf)
85 {
86  return (char*)buf + REFCOUNT_OFFSET;
87 }
88 
90  void (*free_cb)(FFRefStructOpaque 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 
102 void *ff_refstruct_alloc_ext_c(size_t size, unsigned flags, FFRefStructOpaque opaque,
103  void (*free_cb)(FFRefStructOpaque opaque, void *obj))
104 {
105  void *buf, *obj;
106 
107  if (size > SIZE_MAX - REFCOUNT_OFFSET)
108  return NULL;
109  buf = av_malloc(size + REFCOUNT_OFFSET);
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 
120 void ff_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 
140 void *ff_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 
149 const void *ff_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 
160 void ff_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  ff_refstruct_unref(dstp);
168  if (src) {
169  dst = ff_refstruct_ref_c(src);
170  memcpy(dstp, &dst, sizeof(dst));
171  }
172 }
173 
174 int ff_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;
187  void (*reset_cb)(FFRefStructOpaque opaque, void *obj);
190 
191  int uninited;
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 FFRefStructPool.
203  */
206 };
207 
208 static void pool_free(FFRefStructPool *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 
223 static void pool_return_entry(void *ref_)
224 {
225  RefCount *ref = ref_;
226  FFRefStructPool *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 
243 static void pool_reset_entry(FFRefStructOpaque opaque, void *entry)
244 {
245  FFRefStructPool *pool = opaque.nc;
246 
247  pool->reset_cb(pool->opaque, entry);
248 }
249 
250 static int refstruct_pool_get_ext(void *datap, FFRefStructPool *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) {
259  RefCount *ref = 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 = ff_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  */
309 static 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 
316 static void refstruct_pool_uninit(FFRefStructOpaque unused, void *obj)
317 {
318  FFRefStructPool *pool = obj;
319  RefCount *entry;
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 
336 {
338 }
339 
341  FFRefStructOpaque opaque,
342  int (*init_cb)(FFRefStructOpaque opaque, void *obj),
343  void (*reset_cb)(FFRefStructOpaque opaque, void *obj),
344  void (*free_entry_cb)(FFRefStructOpaque opaque, void *obj),
345  void (*free_cb)(FFRefStructOpaque opaque))
346 {
347  FFRefStructPool *pool = ff_refstruct_alloc_ext(sizeof(*pool), 0, NULL,
349  int err;
350 
351  if (!pool)
352  return NULL;
353  get_refcount(pool)->free = pool_unref;
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 FF_REFSTRUCT_POOL_FLAG_NO_ZEROING
362  pool->entry_flags = flags & COMMON_FLAGS;
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 ff_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 }
FFRefStructPool::entry_flags
unsigned entry_flags
Definition: refstruct.c:192
entry
#define entry
Definition: aom_film_grain_template.c:66
ff_mutex_init
static int ff_mutex_init(AVMutex *mutex, const void *attr)
Definition: thread.h:187
ff_refstruct_ref
void * ff_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition: refstruct.c:140
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
REFCOUNT_OFFSET
#define REFCOUNT_OFFSET
Definition: refstruct.c:51
mem_internal.h
ff_refstruct_pool_alloc
FFRefStructPool * ff_refstruct_pool_alloc(size_t size, unsigned flags)
Equivalent to ff_refstruct_pool_alloc(size, flags, NULL, NULL, NULL, NULL, NULL)
Definition: refstruct.c:335
RefCount::free
void(* free)(void *ref)
Definition: refstruct.c:63
RefCount::free_cb
void(* free_cb)(FFRefStructOpaque opaque, void *obj)
Definition: refstruct.c:62
thread.h
ff_refstruct_alloc_ext
static void * ff_refstruct_alloc_ext(size_t size, unsigned flags, void *opaque, void(*free_cb)(FFRefStructOpaque opaque, void *obj))
A wrapper around ff_refstruct_alloc_ext_c() for the common case of a non-const qualified opaque.
Definition: refstruct.h:94
FFRefStructPool::uninited
int uninited
Definition: refstruct.c:191
FFRefStructPool::free_entry_cb
void(* free_entry_cb)(FFRefStructOpaque opaque, void *obj)
Definition: refstruct.c:188
pool_free
static void pool_free(FFRefStructPool *pool)
Definition: refstruct.c:208
ff_refstruct_alloc_ext_c
void * ff_refstruct_alloc_ext_c(size_t size, unsigned flags, FFRefStructOpaque opaque, void(*free_cb)(FFRefStructOpaque opaque, void *obj))
Allocate a refcounted object of usable size size managed via the RefStruct API.
Definition: refstruct.c:102
FFRefStructOpaque
RefStruct is an API for creating reference-counted objects with minimal overhead.
Definition: refstruct.h:58
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:30
ff_mutex_unlock
static int ff_mutex_unlock(AVMutex *mutex)
Definition: thread.h:189
atomic_uintptr_t
intptr_t atomic_uintptr_t
Definition: stdatomic.h:79
FFRefStructPool::reset_cb
void(* reset_cb)(FFRefStructOpaque opaque, void *obj)
Definition: refstruct.c:187
macros.h
RefCount::refcount
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
refstruct.h
FFRefStructPool::mutex
AVMutex mutex
Definition: refstruct.c:205
ff_refstruct_exclusive
int ff_refstruct_exclusive(const void *obj)
Check whether the reference count of an object managed via this API is 1.
Definition: refstruct.c:174
FF_REFSTRUCT_POOL_FLAG_FREE_ON_INIT_ERROR
#define FF_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
FFRefStructPool::free_cb
void(* free_cb)(FFRefStructOpaque opaque)
Definition: refstruct.c:189
avassert.h
pool_unref
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
ff_refstruct_ref_c
const void * ff_refstruct_ref_c(const void *obj)
Analog of ff_refstruct_ref(), but for constant objects.
Definition: refstruct.c:149
AVMutex
#define AVMutex
Definition: thread.h:184
FFRefStructPool
FFRefStructPool is an API for a thread-safe pool of objects managed via the RefStruct API.
Definition: refstruct.c:183
FFRefStructPool::size
size_t size
Definition: refstruct.c:184
RefCount::opaque
FFRefStructOpaque opaque
Definition: refstruct.c:61
NULL
#define NULL
Definition: coverity.c:32
FFRefStructPool::refcount
atomic_uintptr_t refcount
The number of outstanding entries not in available_entries.
Definition: refstruct.c:196
ff_refstruct_pool_alloc_ext_c
FFRefStructPool * ff_refstruct_pool_alloc_ext_c(size_t size, unsigned flags, FFRefStructOpaque opaque, int(*init_cb)(FFRefStructOpaque opaque, void *obj), void(*reset_cb)(FFRefStructOpaque opaque, void *obj), void(*free_entry_cb)(FFRefStructOpaque opaque, void *obj), void(*free_cb)(FFRefStructOpaque opaque))
Allocate an FFRefStructPool, potentially using complex callbacks.
Definition: refstruct.c:340
ff_assert
#define ff_assert(cond)
Definition: refstruct.c:42
FFRefStructPool::opaque
FFRefStructOpaque opaque
Definition: refstruct.c:185
pool_reset_entry
static void pool_reset_entry(FFRefStructOpaque opaque, void *entry)
Definition: refstruct.c:243
atomic_fetch_sub_explicit
#define atomic_fetch_sub_explicit(object, operand, order)
Definition: stdatomic.h:152
atomic_load_explicit
#define atomic_load_explicit(object, order)
Definition: stdatomic.h:96
error.h
ff_mutex_destroy
static int ff_mutex_destroy(AVMutex *mutex)
Definition: thread.h:190
COMMON_FLAGS
#define COMMON_FLAGS
FFRefStructPool::available_entries
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
refstruct_pool_get_ext
static int refstruct_pool_get_ext(void *datap, FFRefStructPool *pool)
Definition: refstruct.c:250
size
int size
Definition: twinvq_data.h:10344
atomic_fetch_add_explicit
#define atomic_fetch_add_explicit(object, operand, order)
Definition: stdatomic.h:149
FFRefStructOpaque::nc
void * nc
Definition: refstruct.h:59
pool_free_entry
static void pool_free_entry(FFRefStructPool *pool, RefCount *ref)
Definition: refstruct.c:216
ff_mutex_lock
static int ff_mutex_lock(AVMutex *mutex)
Definition: thread.h:188
get_userdata
static void * get_userdata(void *buf)
Definition: refstruct.c:84
FF_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME
#define FF_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
FFRefStructPool::pool_flags
unsigned pool_flags
Definition: refstruct.c:193
ret
ret
Definition: filter_design.txt:187
ff_refstruct_replace
void ff_refstruct_replace(void *dstp, const void *src)
Ensure *dstp refers to the same object as src.
Definition: refstruct.c:160
ff_refstruct_pool_alloc_ext
static FFRefStructPool * ff_refstruct_pool_alloc_ext(size_t size, unsigned flags, void *opaque, int(*init_cb)(FFRefStructOpaque opaque, void *obj), void(*reset_cb)(FFRefStructOpaque opaque, void *obj), void(*free_entry_cb)(FFRefStructOpaque opaque, void *obj), void(*free_cb)(FFRefStructOpaque opaque))
A wrapper around ff_refstruct_pool_alloc_ext_c() for the common case of a non-const qualified opaque.
Definition: refstruct.h:258
get_refcount
static RefCount * get_refcount(void *obj)
Definition: refstruct.c:70
ref
static int ref[MAX_W *MAX_W]
Definition: jpeg2000dwt.c:112
cget_refcount
static const RefCount * cget_refcount(const void *obj)
Definition: refstruct.c:77
mem.h
REFSTRUCT_COOKIE
#define REFSTRUCT_COOKIE
Definition: refstruct.c:45
FF_REFSTRUCT_FLAG_NO_ZEROING
#define FF_REFSTRUCT_FLAG_NO_ZEROING
If this flag is set in ff_refstruct_alloc_ext_c(), the object will not be initially zeroed.
Definition: refstruct.h:67
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
FFRefStructPool::init_cb
int(* init_cb)(FFRefStructOpaque opaque, void *obj)
Definition: refstruct.c:186
src
INIT_CLIP pixel * src
Definition: h264pred_template.c:418
pool_return_entry
static void pool_return_entry(void *ref_)
Definition: refstruct.c:223
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:474
RefCount
Definition: refstruct.c:54
atomic_init
#define atomic_init(obj, value)
Definition: stdatomic.h:33
refcount_init
static void refcount_init(RefCount *ref, FFRefStructOpaque opaque, void(*free_cb)(FFRefStructOpaque opaque, void *obj))
Definition: refstruct.c:89
int
int
Definition: ffmpeg_filter.c:424
ff_refstruct_unref
void ff_refstruct_unref(void *objp)
Decrement the reference count of the underlying object and automatically free the object if there are...
Definition: refstruct.c:120
ff_refstruct_pool_get
void * ff_refstruct_pool_get(FFRefStructPool *pool)
Get an object from the pool, reusing an old one from the pool when available.
Definition: refstruct.c:297
refstruct_pool_uninit
static void refstruct_pool_uninit(FFRefStructOpaque unused, void *obj)
Definition: refstruct.c:316
FF_REFSTRUCT_POOL_FLAG_RESET_ON_INIT_ERROR
#define FF_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