]> git.sesse.net Git - vlc/blob - src/misc/picture_pool.c
picture_pool_NewFromFormat: improve error handling
[vlc] / src / misc / picture_pool.c
1 /*****************************************************************************
2  * picture_pool.c : picture pool functions
3  *****************************************************************************
4  * Copyright (C) 2009 VLC authors and VideoLAN
5  * Copyright (C) 2009 Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
6  * $Id$
7  *
8  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 #include <assert.h>
33
34 #include <vlc_common.h>
35 #include <vlc_picture_pool.h>
36
37 /*****************************************************************************
38  *
39  *****************************************************************************/
40 struct picture_gc_sys_t {
41     picture_pool_t *pool;
42     /* Saved release */
43     void (*destroy)(picture_t *);
44     void *destroy_sys;
45     /* */
46     atomic_bool zombie;
47     int64_t tick;
48 };
49
50 struct picture_pool_t {
51     /* */
52     picture_pool_t *master;
53     int64_t        tick;
54     /* */
55     unsigned       picture_count;
56     picture_t      **picture;
57     bool           *picture_reserved;
58
59     int       (*pic_lock)(picture_t *);
60     void      (*pic_unlock)(picture_t *);
61     unsigned    refs;
62     vlc_mutex_t lock;
63 };
64
65 static void Release(picture_pool_t *pool)
66 {
67     bool destroy;
68
69     vlc_mutex_lock(&pool->lock);
70     assert(pool->refs > 0);
71     destroy = !--pool->refs;
72     vlc_mutex_unlock(&pool->lock);
73
74     if (!destroy)
75         return;
76
77     vlc_mutex_destroy(&pool->lock);
78     free(pool->picture_reserved);
79     free(pool->picture);
80     free(pool);
81 }
82
83 static void DestroyPicture(picture_t *picture)
84 {
85     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
86     picture_pool_t *pool = gc_sys->pool;
87
88     if (pool->pic_unlock != NULL)
89         pool->pic_unlock(picture);
90
91     if (!atomic_load(&gc_sys->zombie))
92         return;
93
94     /* Picture from an already destroyed pool */
95     picture->gc.pf_destroy = gc_sys->destroy;
96     picture->gc.p_sys      = gc_sys->destroy_sys;
97     free(gc_sys);
98
99     picture->gc.pf_destroy(picture);
100     Release(pool);
101 }
102
103 static picture_pool_t *Create(picture_pool_t *master, int picture_count)
104 {
105     picture_pool_t *pool = calloc(1, sizeof(*pool));
106     if (!pool)
107         return NULL;
108
109     pool->master = master;
110     pool->tick = master ? master->tick : 1;
111     pool->picture_count = picture_count;
112     pool->picture = calloc(pool->picture_count, sizeof(*pool->picture));
113     pool->picture_reserved = calloc(pool->picture_count, sizeof(*pool->picture_reserved));
114     if (!pool->picture || !pool->picture_reserved) {
115         free(pool->picture);
116         free(pool->picture_reserved);
117         free(pool);
118         return NULL;
119     }
120     pool->refs = 1;
121     vlc_mutex_init(&pool->lock);
122     return pool;
123 }
124
125 picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
126 {
127     picture_pool_t *pool = Create(NULL, cfg->picture_count);
128     if (!pool)
129         return NULL;
130
131     pool->pic_lock   = cfg->lock;
132     pool->pic_unlock = cfg->unlock;
133
134     /*
135      * NOTE: When a pooled picture is released, it must be returned to the list
136      * of available pictures from its pool, rather than destroyed.
137      * This requires a dedicated release callback, a pointer to the pool and a
138      * reference count. For simplicity, rather than allocate a whole new
139      * picture_t structure, the pool overrides gc.pf_destroy and gc.p_sys when
140      * created, and restores them when destroyed.
141      * There are some implications to keep in mind:
142      *  - The original creator of the picture (e.g. video output display) must
143      *    not manipulate the gc parameters while the picture is pooled.
144      *  - The picture cannot be pooled more than once, in other words, pools
145      *    cannot be stacked/layered.
146      *  - The picture must be available and its reference count equal to one
147      *    when it gets pooled.
148      *  - Picture plane pointers and sizes must not be mangled in any case.
149      */
150     for (unsigned i = 0; i < cfg->picture_count; i++) {
151         picture_t *picture = cfg->picture[i];
152
153         /* Save the original garbage collector */
154         picture_gc_sys_t *gc_sys = malloc(sizeof(*gc_sys));
155         if (unlikely(gc_sys == NULL))
156             abort();
157         gc_sys->pool        = pool;
158         gc_sys->destroy     = picture->gc.pf_destroy;
159         gc_sys->destroy_sys = picture->gc.p_sys;
160         atomic_init(&gc_sys->zombie, false);
161         gc_sys->tick        = 0;
162
163         /* Override the garbage collector */
164         assert(atomic_load(&picture->gc.refcount) == 1);
165         atomic_init(&picture->gc.refcount, 0);
166         picture->gc.pf_destroy = DestroyPicture;
167         picture->gc.p_sys      = gc_sys;
168
169         /* */
170         pool->picture[i] = picture;
171         pool->picture_reserved[i] = false;
172         pool->refs++;
173     }
174     return pool;
175
176 }
177
178 picture_pool_t *picture_pool_New(unsigned count, picture_t *const *tab)
179 {
180     picture_pool_configuration_t cfg;
181
182     memset(&cfg, 0, sizeof(cfg));
183     cfg.picture_count = count;
184     cfg.picture       = tab;
185
186     return picture_pool_NewExtended(&cfg);
187 }
188
189 picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt,
190                                            unsigned count)
191 {
192     picture_t *picture[count ? count : 1];
193     unsigned i;
194
195     for (i = 0; i < count; i++) {
196         picture[i] = picture_NewFromFormat(fmt);
197         if (picture[i] == NULL)
198             goto error;
199     }
200
201     picture_pool_t *pool = picture_pool_New(count, picture);
202     if (!pool)
203         goto error;
204
205     return pool;
206
207 error:
208     while (i > 0)
209         picture_Release(picture[--i]);
210     return NULL;
211 }
212
213 picture_pool_t *picture_pool_Reserve(picture_pool_t *master, unsigned count)
214 {
215     picture_pool_t *pool = Create(master, count);
216     if (!pool)
217         return NULL;
218
219     pool->pic_lock   = master->pic_lock;
220     pool->pic_unlock = master->pic_unlock;
221
222     unsigned found = 0;
223     for (unsigned i = 0; i < master->picture_count && found < count; i++) {
224         if (master->picture_reserved[i])
225             continue;
226
227         assert(atomic_load(&master->picture[i]->gc.refcount) == 0);
228         master->picture_reserved[i] = true;
229
230         pool->picture[found]          = master->picture[i];
231         pool->picture_reserved[found] = false;
232         found++;
233     }
234     if (found < count) {
235         picture_pool_Delete(pool);
236         return NULL;
237     }
238     return pool;
239 }
240
241 void picture_pool_Delete(picture_pool_t *pool)
242 {
243     for (unsigned i = 0; i < pool->picture_count; i++) {
244         picture_t *picture = pool->picture[i];
245         if (pool->master) {
246             for (unsigned j = 0; j < pool->master->picture_count; j++) {
247                 if (pool->master->picture[j] == picture)
248                     pool->master->picture_reserved[j] = false;
249             }
250         } else {
251             picture_gc_sys_t *gc_sys = picture->gc.p_sys;
252
253             assert(!pool->picture_reserved[i]);
254
255             /* Restore the initial reference that was cloberred in
256              * picture_pool_NewExtended(). */
257             atomic_fetch_add(&picture->gc.refcount, 1);
258             /* The picture might still locked and then the G.C. state cannot be
259              * modified (w/o memory synchronization). */
260             atomic_store(&gc_sys->zombie, true);
261
262             picture_Release(picture);
263         }
264     }
265     Release(pool);
266 }
267
268 picture_t *picture_pool_Get(picture_pool_t *pool)
269 {
270     for (unsigned i = 0; i < pool->picture_count; i++) {
271         if (pool->picture_reserved[i])
272             continue;
273
274         picture_t *picture = pool->picture[i];
275         uintptr_t refs = 0;
276
277         if (!atomic_compare_exchange_strong(&picture->gc.refcount, &refs, 1))
278             continue;
279
280         if (pool->pic_lock != NULL && pool->pic_lock(picture) != 0) {
281             atomic_store(&picture->gc.refcount, 0);
282             continue;
283         }
284
285         /* */
286         picture->p_next = NULL;
287         picture->gc.p_sys->tick = pool->tick++;
288         return picture;
289     }
290     return NULL;
291 }
292
293 void picture_pool_Reset(picture_pool_t *pool)
294 {
295     for (unsigned i = 0; i < pool->picture_count; i++) {
296         if (pool->picture_reserved[i])
297             continue;
298
299         picture_t *picture = pool->picture[i];
300         if (atomic_load(&picture->gc.refcount) > 0) {
301             if (pool->pic_unlock != NULL)
302                 pool->pic_unlock(picture);
303         }
304         atomic_store(&picture->gc.refcount, 0);
305     }
306 }
307
308 void picture_pool_NonEmpty(picture_pool_t *pool)
309 {
310     picture_t *oldest = NULL;
311
312     for (unsigned i = 0; i < pool->picture_count; i++) {
313         if (pool->picture_reserved[i])
314             continue;
315
316         picture_t *picture = pool->picture[i];
317         if (atomic_load(&picture->gc.refcount) == 0)
318             return; /* Nothing to do */
319
320         if (oldest == NULL || picture->gc.p_sys->tick < oldest->gc.p_sys->tick)
321             oldest = picture;
322     }
323
324     if (oldest == NULL)
325         return; /* Cannot fix! */
326
327     if (atomic_load(&oldest->gc.refcount) > 0) {
328         if (pool->pic_unlock != NULL)
329             pool->pic_unlock(oldest);
330     }
331     atomic_store(&oldest->gc.refcount, 0);
332 }
333
334 int picture_pool_GetSize(picture_pool_t *pool)
335 {
336     return pool->picture_count;
337 }