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