1 /* This file is part of GEGL.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 3 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15 *
16 * Copyright 2006-2009 Øyvind Kolås <pippin@gimp.org>
17 */
18
19 #include "config.h"
20
21 #include "string.h" /* memcpy */
22
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26
27 #include <glib-object.h>
28
29 #include "gegl-buffer.h"
30 #include "gegl-tile.h"
31 #include "gegl-tile-alloc.h"
32 #include "gegl-buffer-private.h"
33 #include "gegl-tile-storage.h"
34
35 /* the offset of the n_clones array, relative to the tile data, when it shares
36 * the same buffer as the data.
37 */
38 #define INLINE_N_CLONES_OFFSET (2 * sizeof (gint))
39
40 enum
41 {
42 CLONE_STATE_UNCLONED,
43 CLONE_STATE_CLONED,
44 CLONE_STATE_UNCLONING
45 };
46
gegl_tile_ref(GeglTile * tile)47 GeglTile *gegl_tile_ref (GeglTile *tile)
48 {
49 g_atomic_int_inc (&tile->ref_count);
50 return tile;
51 }
52
53 static const gint free_data_directly;
54
gegl_tile_unref(GeglTile * tile)55 void gegl_tile_unref (GeglTile *tile)
56 {
57 if (!g_atomic_int_dec_and_test (&tile->ref_count))
58 return;
59
60 /* In the case of a file store for example, we must make sure that
61 * the in-memory tile is written to disk before we free the memory,
62 * otherwise this data will be lost.
63 */
64 gegl_tile_store (tile);
65
66 if (g_atomic_int_dec_and_test (gegl_tile_n_clones (tile)))
67 { /* no clones */
68 if (tile->destroy_notify == (gpointer) &free_data_directly)
69 {
70 /* tile->data and tile->n_clones share the same buffer,
71 * which is freed through tile->data.
72 */
73 gegl_tile_free (tile->data);
74 }
75 else
76 {
77 /* tile->data and tile->n_clones are unrelated, so free them
78 * separately.
79 */
80 if (tile->data && tile->destroy_notify)
81 tile->destroy_notify (tile->destroy_notify_data);
82
83 g_slice_free1 (2 * sizeof (gint), tile->n_clones);
84 }
85 }
86
87 g_slice_free (GeglTile, tile);
88 }
89
90 static inline GeglTile *
gegl_tile_new_bare_internal(void)91 gegl_tile_new_bare_internal (void)
92 {
93 GeglTile *tile = g_slice_new0 (GeglTile);
94 tile->ref_count = 1;
95 tile->tile_storage = NULL;
96 tile->stored_rev = 1;
97 tile->rev = 1;
98 tile->lock_count = 0;
99 tile->read_lock_count = 0;
100 tile->clone_state = CLONE_STATE_UNCLONED;
101 tile->data = NULL;
102
103 return tile;
104 }
105
106 GeglTile *
gegl_tile_new_bare(void)107 gegl_tile_new_bare (void)
108 {
109 GeglTile *tile = gegl_tile_new_bare_internal ();
110
111 tile->n_clones = g_slice_alloc (2 * sizeof (gint));
112 *gegl_tile_n_clones (tile) = 1;
113 *gegl_tile_n_cached_clones (tile) = 0;
114
115 tile->destroy_notify = NULL;
116 tile->destroy_notify_data = NULL;
117
118 return tile;
119 }
120
121 GeglTile *
gegl_tile_dup(GeglTile * src)122 gegl_tile_dup (GeglTile *src)
123 {
124 GeglTile *tile;
125
126 g_warn_if_fail (src->lock_count == 0);
127 g_warn_if_fail (! src->damage);
128
129 if (! src->keep_identity)
130 {
131 src->clone_state = CLONE_STATE_CLONED;
132
133 tile = gegl_tile_new_bare_internal ();
134
135 tile->data = src->data;
136 tile->size = src->size;
137 tile->is_zero_tile = src->is_zero_tile;
138 tile->is_global_tile = src->is_global_tile;
139 tile->clone_state = CLONE_STATE_CLONED;
140 tile->n_clones = src->n_clones;
141
142 tile->destroy_notify = src->destroy_notify;
143 tile->destroy_notify_data = src->destroy_notify_data;
144
145 g_atomic_int_inc (gegl_tile_n_clones (tile));
146 }
147 else
148 {
149 /* we can't clone the source tile if we need to keep its data-pointer
150 * identity, since we have no way of uncloning it without changing its
151 * data pointer.
152 */
153
154 tile = gegl_tile_new (src->size);
155
156 memcpy (tile->data, src->data, src->size);
157 }
158
159 /* mark the tile as dirty, since, even though the in-memory tile data may be
160 * shared with the source tile, the stored tile data is separate.
161 */
162 tile->rev++;
163
164 return tile;
165 }
166
167 GeglTile *
gegl_tile_new(gint size)168 gegl_tile_new (gint size)
169 {
170 GeglTile *tile = gegl_tile_new_bare_internal ();
171
172 tile->data = gegl_tile_alloc (size);
173 tile->size = size;
174
175 /* gegl_tile_alloc() guarantees that there's enough room for the n_clones
176 * array in front of the data buffer.
177 */
178 tile->n_clones = (gint *) (tile->data -
179 INLINE_N_CLONES_OFFSET);
180 *gegl_tile_n_clones (tile) = 1;
181 *gegl_tile_n_cached_clones (tile) = 0;
182
183 tile->destroy_notify = (gpointer) &free_data_directly;
184 tile->destroy_notify_data = NULL;
185
186 return tile;
187 }
188
189 static inline void
gegl_tile_unclone(GeglTile * tile)190 gegl_tile_unclone (GeglTile *tile)
191 {
192 if (*gegl_tile_n_clones (tile) > 1)
193 {
194 GeglTileHandlerCache *notify_cache = NULL;
195 gboolean cached;
196 gboolean global;
197
198 global = tile->is_global_tile;
199 tile->is_global_tile = FALSE;
200
201 if (! global)
202 {
203 while (! g_atomic_int_compare_and_exchange (&tile->read_lock_count,
204 0,
205 -1));
206 }
207
208 cached = tile->tile_storage && tile->tile_storage->cache;
209
210 if (cached)
211 {
212 if (! g_atomic_int_dec_and_test (gegl_tile_n_cached_clones (tile)))
213 notify_cache = tile->tile_storage->cache;
214 }
215
216 /* the tile data is shared with other tiles,
217 * create a local copy
218 */
219
220 if (! ~tile->damage)
221 {
222 /* if the tile is fully damaged, we only need to allocate a new
223 * buffer, but we don't have to copy the old one.
224 */
225
226 tile->is_zero_tile = FALSE;
227
228 if (g_atomic_int_dec_and_test (gegl_tile_n_clones (tile)))
229 {
230 /* someone else uncloned the tile in the meantime, and we're now
231 * the last copy; bail.
232 */
233 *gegl_tile_n_clones (tile) = 1;
234 *gegl_tile_n_cached_clones (tile) = cached;
235
236 goto end;
237 }
238
239 tile->data = gegl_tile_alloc (tile->size);
240 }
241 else if (tile->is_zero_tile)
242 {
243 tile->is_zero_tile = FALSE;
244
245 if (g_atomic_int_dec_and_test (gegl_tile_n_clones (tile)))
246 {
247 /* someone else uncloned the tile in the meantime, and we're now
248 * the last copy; bail.
249 */
250 *gegl_tile_n_clones (tile) = 1;
251 *gegl_tile_n_cached_clones (tile) = cached;
252
253 goto end;
254 }
255
256 tile->data = gegl_tile_alloc0 (tile->size);
257 }
258 else
259 {
260 guchar *buf;
261
262 buf = gegl_tile_alloc (tile->size);
263 memcpy (buf, tile->data, tile->size);
264
265 if (g_atomic_int_dec_and_test (gegl_tile_n_clones (tile)))
266 {
267 /* someone else uncloned the tile in the meantime, and we're now
268 * the last copy; bail.
269 */
270 gegl_tile_free (buf);
271 *gegl_tile_n_clones (tile) = 1;
272 *gegl_tile_n_cached_clones (tile) = cached;
273
274 goto end;
275 }
276
277 tile->data = buf;
278 }
279
280 tile->n_clones = (gint *) (tile->data - INLINE_N_CLONES_OFFSET);
281 *gegl_tile_n_clones (tile) = 1;
282 *gegl_tile_n_cached_clones (tile) = cached;
283
284 tile->destroy_notify = (gpointer) &free_data_directly;
285 tile->destroy_notify_data = NULL;
286
287 end:
288 if (notify_cache)
289 gegl_tile_handler_cache_tile_uncloned (notify_cache, tile);
290
291 if (! global)
292 g_atomic_int_set (&tile->read_lock_count, 0);
293 }
294 }
295
296 void
gegl_tile_lock(GeglTile * tile)297 gegl_tile_lock (GeglTile *tile)
298 {
299 unsigned int count = 0;
300 g_atomic_int_inc (&tile->lock_count);
301
302 while (TRUE)
303 {
304 switch (g_atomic_int_get (&tile->clone_state))
305 {
306 case CLONE_STATE_UNCLONED:
307 return;
308
309 case CLONE_STATE_CLONED:
310 if (g_atomic_int_compare_and_exchange (&tile->clone_state,
311 CLONE_STATE_CLONED,
312 CLONE_STATE_UNCLONING))
313 {
314 gegl_tile_unclone (tile);
315
316 g_atomic_int_set (&tile->clone_state, CLONE_STATE_UNCLONED);
317
318 return;
319 }
320 break;
321
322 case CLONE_STATE_UNCLONING:
323 break;
324 }
325
326 ++count;
327 if (count > 32)
328 g_usleep (1000); /* the smallest sleepable time on win32 */
329 }
330 }
331
332 static inline void
gegl_tile_void_pyramid(GeglTile * tile,guint64 damage)333 gegl_tile_void_pyramid (GeglTile *tile,
334 guint64 damage)
335 {
336 if (tile->tile_storage &&
337 tile->tile_storage->seen_zoom &&
338 tile->z == 0) /* we only accepting voiding the base level */
339 {
340 gegl_tile_handler_damage_tile (GEGL_TILE_HANDLER (tile->tile_storage),
341 tile->x, tile->y, tile->z,
342 damage);
343 }
344 }
345
346 void
gegl_tile_unlock(GeglTile * tile)347 gegl_tile_unlock (GeglTile *tile)
348 {
349 if (g_atomic_int_dec_and_test (&tile->lock_count))
350 {
351 g_atomic_int_inc (&tile->rev);
352 tile->damage = 0;
353
354 if (tile->unlock_notify != NULL)
355 {
356 tile->unlock_notify (tile, tile->unlock_notify_data);
357 }
358
359 if (tile->z == 0)
360 {
361 gegl_tile_void_pyramid (tile, ~(guint64) 0);
362 }
363 }
364 }
365
366 void
gegl_tile_unlock_no_void(GeglTile * tile)367 gegl_tile_unlock_no_void (GeglTile *tile)
368 {
369 if (g_atomic_int_dec_and_test (&tile->lock_count))
370 {
371 g_atomic_int_inc (&tile->rev);
372 tile->damage = 0;
373
374 if (tile->unlock_notify != NULL)
375 {
376 tile->unlock_notify (tile, tile->unlock_notify_data);
377 }
378 }
379 }
380
381 void
gegl_tile_read_lock(GeglTile * tile)382 gegl_tile_read_lock (GeglTile *tile)
383 {
384 while (TRUE)
385 {
386 gint count = g_atomic_int_get (&tile->read_lock_count);
387
388 if (count < 0)
389 {
390 continue;
391 }
392
393 if (g_atomic_int_compare_and_exchange (&tile->read_lock_count,
394 count,
395 count + 1))
396 {
397 break;
398 }
399 }
400 }
401
402 void
gegl_tile_read_unlock(GeglTile * tile)403 gegl_tile_read_unlock (GeglTile *tile)
404 {
405 g_atomic_int_dec_and_test (&tile->read_lock_count);
406 }
407
408 void
gegl_tile_mark_as_stored(GeglTile * tile)409 gegl_tile_mark_as_stored (GeglTile *tile)
410 {
411 tile->stored_rev = tile->rev;
412 }
413
414 gboolean
gegl_tile_is_stored(GeglTile * tile)415 gegl_tile_is_stored (GeglTile *tile)
416 {
417 return tile->stored_rev == tile->rev;
418 }
419
420 gboolean
gegl_tile_needs_store(GeglTile * tile)421 gegl_tile_needs_store (GeglTile *tile)
422 {
423 return tile->tile_storage &&
424 ! gegl_tile_is_stored (tile) &&
425 ! tile->damage;
426 }
427
428 void
gegl_tile_void(GeglTile * tile)429 gegl_tile_void (GeglTile *tile)
430 {
431 gegl_tile_mark_as_stored (tile);
432
433 if (tile->z == 0)
434 gegl_tile_void_pyramid (tile, ~(guint64) 0);
435 }
436
437 gboolean
gegl_tile_damage(GeglTile * tile,guint64 damage)438 gegl_tile_damage (GeglTile *tile,
439 guint64 damage)
440 {
441 tile->damage |= damage;
442
443 if (! ~tile->damage)
444 {
445 gegl_tile_void (tile);
446
447 return TRUE;
448 }
449 else
450 {
451 if (tile->z == 0)
452 gegl_tile_void_pyramid (tile, damage);
453
454 return FALSE;
455 }
456 }
457
gegl_tile_store(GeglTile * tile)458 gboolean gegl_tile_store (GeglTile *tile)
459 {
460 gboolean ret;
461 if (gegl_tile_is_stored (tile))
462 return TRUE;
463 else if (! gegl_tile_needs_store (tile))
464 return FALSE;
465
466 g_rec_mutex_lock (&tile->tile_storage->mutex);
467
468 if (gegl_tile_is_stored (tile))
469 {
470 g_rec_mutex_unlock (&tile->tile_storage->mutex);
471 return TRUE;
472 }
473
474 ret = gegl_tile_source_set_tile (GEGL_TILE_SOURCE (tile->tile_storage),
475 tile->x,
476 tile->y,
477 tile->z,
478 tile);
479
480 g_rec_mutex_unlock (&tile->tile_storage->mutex);
481
482 return ret;
483 }
484
gegl_tile_get_data(GeglTile * tile)485 guchar *gegl_tile_get_data (GeglTile *tile)
486 {
487 return tile->data;
488 }
489
gegl_tile_set_data(GeglTile * tile,gpointer pixel_data,gint pixel_data_size)490 void gegl_tile_set_data (GeglTile *tile,
491 gpointer pixel_data,
492 gint pixel_data_size)
493 {
494 tile->data = pixel_data;
495 tile->size = pixel_data_size;
496 }
497
gegl_tile_set_data_full(GeglTile * tile,gpointer pixel_data,gint pixel_data_size,GDestroyNotify destroy_notify,gpointer destroy_notify_data)498 void gegl_tile_set_data_full (GeglTile *tile,
499 gpointer pixel_data,
500 gint pixel_data_size,
501 GDestroyNotify destroy_notify,
502 gpointer destroy_notify_data)
503 {
504 tile->data = pixel_data;
505 tile->size = pixel_data_size;
506 tile->destroy_notify = destroy_notify;
507 tile->destroy_notify_data = destroy_notify_data;
508 }
509
gegl_tile_set_rev(GeglTile * tile,guint rev)510 void gegl_tile_set_rev (GeglTile *tile,
511 guint rev)
512 {
513 tile->rev = rev;
514 }
515
gegl_tile_get_rev(GeglTile * tile)516 guint gegl_tile_get_rev (GeglTile *tile)
517 {
518 return tile->rev;
519 }
520
gegl_tile_set_unlock_notify(GeglTile * tile,GeglTileCallback unlock_notify,gpointer unlock_notify_data)521 void gegl_tile_set_unlock_notify (GeglTile *tile,
522 GeglTileCallback unlock_notify,
523 gpointer unlock_notify_data)
524 {
525 tile->unlock_notify = unlock_notify;
526 tile->unlock_notify_data = unlock_notify_data;
527 }
528