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 "internal.h"
24 #include "refstruct.h"
25 
26 #include "libavutil/avassert.h"
27 #include "libavutil/macros.h"
28 #include "libavutil/mem.h"
29 
30 #ifndef REFSTRUCT_CHECKED
31 #ifndef ASSERT_LEVEL
32 #define ASSERT_LEVEL 0
33 #endif
34 #define REFSTRUCT_CHECKED (ASSERT_LEVEL >= 1)
35 #endif
36 
37 #if REFSTRUCT_CHECKED
38 #define ff_assert(cond) av_assert0(cond)
39 #else
40 #define ff_assert(cond) ((void)0)
41 #endif
42 
43 #define REFSTRUCT_COOKIE AV_NE((uint64_t)MKBETAG('R', 'e', 'f', 'S') << 32 | MKBETAG('t', 'r', 'u', 'c'), \
44  MKTAG('R', 'e', 'f', 'S') | (uint64_t)MKTAG('t', 'r', 'u', 'c') << 32)
45 
46 #if __STDC_VERSION__ >= 201112L
47 #define REFCOUNT_OFFSET FFALIGN(sizeof(RefCount), FFMAX3(STRIDE_ALIGN, 16, _Alignof(max_align_t)))
48 #else
49 #define REFCOUNT_OFFSET FFALIGN(sizeof(RefCount), FFMAX(STRIDE_ALIGN, 16))
50 #endif
51 
52 typedef struct RefCount {
53  /**
54  * An uintptr_t is big enough to hold the address of every reference,
55  * so no overflow can happen when incrementing the refcount as long as
56  * the user does not throw away references.
57  */
60  void (*free_cb)(FFRefStructOpaque opaque, void *obj);
61 
62 #if REFSTRUCT_CHECKED
63  uint64_t cookie;
64 #endif
65 } RefCount;
66 
67 static RefCount *get_refcount(void *obj)
68 {
69  RefCount *ref = (RefCount*)((char*)obj - REFCOUNT_OFFSET);
70  ff_assert(ref->cookie == REFSTRUCT_COOKIE);
71  return ref;
72 }
73 
74 static const RefCount *cget_refcount(const void *obj)
75 {
76  const RefCount *ref = (const RefCount*)((const char*)obj - REFCOUNT_OFFSET);
77  ff_assert(ref->cookie == REFSTRUCT_COOKIE);
78  return ref;
79 }
80 
81 static void *get_userdata(void *buf)
82 {
83  return (char*)buf + REFCOUNT_OFFSET;
84 }
85 
87  void (*free_cb)(FFRefStructOpaque opaque, void *obj))
88 {
89  atomic_init(&ref->refcount, 1);
90  ref->opaque = opaque;
91  ref->free_cb = free_cb;
92 
93 #if REFSTRUCT_CHECKED
94  ref->cookie = REFSTRUCT_COOKIE;
95 #endif
96 }
97 
98 void *ff_refstruct_alloc_ext_c(size_t size, unsigned flags, FFRefStructOpaque opaque,
99  void (*free_cb)(FFRefStructOpaque opaque, void *obj))
100 {
101  void *buf, *obj;
102 
103  if (size > SIZE_MAX - REFCOUNT_OFFSET)
104  return NULL;
105  buf = av_malloc(size + REFCOUNT_OFFSET);
106  if (!buf)
107  return NULL;
108  refcount_init(buf, opaque, free_cb);
109  obj = get_userdata(buf);
111  memset(obj, 0, size);
112 
113  return obj;
114 }
115 
116 void ff_refstruct_unref(void *objp)
117 {
118  void *obj;
119  RefCount *ref;
120 
121  memcpy(&obj, objp, sizeof(obj));
122  if (!obj)
123  return;
124  memcpy(objp, &(void *){ NULL }, sizeof(obj));
125 
126  ref = get_refcount(obj);
127  if (atomic_fetch_sub_explicit(&ref->refcount, 1, memory_order_acq_rel) == 1) {
128  if (ref->free_cb)
129  ref->free_cb(ref->opaque, obj);
130  av_free(ref);
131  }
132 
133  return;
134 }
135 
136 void *ff_refstruct_ref(void *obj)
137 {
138  RefCount *ref = get_refcount(obj);
139 
140  atomic_fetch_add_explicit(&ref->refcount, 1, memory_order_relaxed);
141 
142  return obj;
143 }
144 
145 const void *ff_refstruct_ref_c(const void *obj)
146 {
147  /* Casting const away here is fine, as it is only supposed
148  * to apply to the user's data and not our bookkeeping data. */
149  RefCount *ref = get_refcount((void*)obj);
150 
151  atomic_fetch_add_explicit(&ref->refcount, 1, memory_order_relaxed);
152 
153  return obj;
154 }
155 
156 void ff_refstruct_replace(void *dstp, const void *src)
157 {
158  const void *dst;
159  memcpy(&dst, dstp, sizeof(dst));
160 
161  if (src == dst)
162  return;
163  ff_refstruct_unref(dstp);
164  if (src) {
165  dst = ff_refstruct_ref_c(src);
166  memcpy(dstp, &dst, sizeof(dst));
167  }
168 }
169 
170 int ff_refstruct_exclusive(const void *obj)
171 {
172  const RefCount *ref = cget_refcount(obj);
173  /* Casting const away here is safe, because it is a load.
174  * It is necessary because atomic_load_explicit() does not
175  * accept const atomics in C11 (see also N1807). */
176  return atomic_load_explicit((atomic_uintptr_t*)&ref->refcount, memory_order_acquire) == 1;
177 }
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:136
REFCOUNT_OFFSET
#define REFCOUNT_OFFSET
Definition: refstruct.c:49
RefCount::free_cb
void(* free_cb)(FFRefStructOpaque opaque, void *obj)
Definition: refstruct.c:60
internal.h
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:98
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
atomic_uintptr_t
intptr_t atomic_uintptr_t
Definition: stdatomic.h:79
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:58
refstruct.h
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:170
avassert.h
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:145
RefCount::opaque
FFRefStructOpaque opaque
Definition: refstruct.c:59
NULL
#define NULL
Definition: coverity.c:32
ff_assert
#define ff_assert(cond)
Definition: refstruct.c:40
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
size
int size
Definition: twinvq_data.h:10344
atomic_fetch_add_explicit
#define atomic_fetch_add_explicit(object, operand, order)
Definition: stdatomic.h:149
get_userdata
static void * get_userdata(void *buf)
Definition: refstruct.c:81
ff_refstruct_replace
void ff_refstruct_replace(void *dstp, const void *src)
Ensure *dstp refers to the same object as src.
Definition: refstruct.c:156
get_refcount
static RefCount * get_refcount(void *obj)
Definition: refstruct.c:67
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:74
mem.h
REFSTRUCT_COOKIE
#define REFSTRUCT_COOKIE
Definition: refstruct.c:43
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
src
INIT_CLIP pixel * src
Definition: h264pred_template.c:418
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:474
RefCount
Definition: refstruct.c:52
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:86
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:116