1 /* Manage sets of pixel buffers on an image.
2 *
3 * 30/10/06
4 * - from window.c
5 * 2/2/07
6 * - speed up the search, use our own lock (thanks Christian)
7 * 5/2/07
8 * - split to many buffer lists per image
9 * 11/2/07
10 * - split to a buffer hash per thread
11 * - reuse buffer mallocs when we can
12 * 20/2/07
13 * - add VipsBufferCacheList and we can avoid some hash ops on
14 * done/undone
15 * 5/3/10
16 * - move invalid stuff to region
17 * - move link maintenance to im_demand_hint
18 * 21/9/11
19 * - switch to vips_tracked_malloc()
20 * 18/12/13
21 * - keep a few buffers in reserve per image, stops malloc/free
22 * cycling when sharing is repeatedly discovered
23 * 6/6/16
24 * - free buffers on image close as well as thread exit, so main thread
25 * buffers don't clog up the system
26 * 13/10/16
27 * - better solution: don't keep a buffercache for non-workers
28 */
29
30 /*
31
32 This file is part of VIPS.
33
34 VIPS is free software; you can redistribute it and/or modify
35 it under the terms of the GNU Lesser General Public License as published by
36 the Free Software Foundation; either version 2 of the License, or
37 (at your option) any later version.
38
39 This program is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 GNU Lesser General Public License for more details.
43
44 You should have received a copy of the GNU Lesser General Public License
45 along with this program; if not, write to the Free Software
46 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
47 02110-1301 USA
48
49 */
50
51 /*
52
53 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
54
55 */
56
57 /*
58 #define DEBUG_VERBOSE
59 #define DEBUG_CREATE
60 #define DEBUG
61 */
62
63 #ifdef HAVE_CONFIG_H
64 #include <config.h>
65 #endif /*HAVE_CONFIG_H*/
66 #include <vips/intl.h>
67
68 #include <stdio.h>
69 #include <stdlib.h>
70
71 #include <vips/vips.h>
72 #include <vips/internal.h>
73 #include <vips/thread.h>
74
75 #ifdef DEBUG
76 /* Track all buffers here for debugging.
77 */
78 static GSList *vips__buffer_all = NULL;
79 #endif /*DEBUG*/
80
81 #ifdef DEBUG_CREATE
82 static GSList *vips__buffer_cache_all = NULL;
83 #endif /*DEBUG_CREATE*/
84
85 /* The maximum numbers of buffers we hold in reserve per image.
86 */
87 static const int buffer_cache_max_reserve = 2;
88
89 /* Workers have a BufferThread (and BufferCache) in a GPrivate they have
90 * exclusive access to.
91 */
92 static GPrivate *buffer_thread_key = NULL;
93
94 void
vips_buffer_print(VipsBuffer * buffer)95 vips_buffer_print( VipsBuffer *buffer )
96 {
97 printf( "VipsBuffer: %p ref_count = %d, ", buffer, buffer->ref_count );
98 printf( "im = %p, ", buffer->im );
99 printf( "area.left = %d, ", buffer->area.left );
100 printf( "area.top = %d, ", buffer->area.top );
101 printf( "area.width = %d, ", buffer->area.width );
102 printf( "area.height = %d, ", buffer->area.height );
103 printf( "done = %d, ", buffer->done );
104 printf( "cache = %p, ", buffer->cache );
105 printf( "buf = %p, ", buffer->buf );
106 printf( "bsize = %zd\n", buffer->bsize );
107 }
108
109 #ifdef DEBUG
110 static void *
vips_buffer_dump(VipsBuffer * buffer,size_t * reserve,size_t * alive)111 vips_buffer_dump( VipsBuffer *buffer, size_t *reserve, size_t *alive )
112 {
113 vips_buffer_print( buffer );
114
115 g_assert( buffer->im );
116 g_assert( buffer->buf );
117
118 if( !buffer->cache &&
119 !buffer->done ) {
120 /* Global buffer, not linked to any cache.
121 */
122 printf( "global buffer %p, %.3g MB\n",
123 buffer, buffer->bsize / (1024 * 1024.0) );
124 *alive += buffer->bsize;
125 }
126
127 else if( buffer->cache &&
128 buffer->done &&
129 !vips_rect_isempty( &buffer->area ) &&
130 g_slist_find( buffer->cache->buffers, buffer ) ) {
131 /* Published on a thread.
132 */
133 printf( "thread buffer %p, %.3g MB\n",
134 buffer, buffer->bsize / (1024 * 1024.0) );
135 *alive += buffer->bsize;
136 }
137
138 else if( buffer->ref_count == 0 &&
139 buffer->cache &&
140 !buffer->done &&
141 vips_rect_isempty( &buffer->area ) &&
142 g_slist_find( buffer->cache->reserve, buffer ) )
143 /* Held in reserve.
144 */
145 *reserve += buffer->bsize;
146
147 else
148 printf( "buffer craziness!\n" );
149
150 return( NULL );
151 }
152 #endif /*DEBUG*/
153
154 #ifdef DEBUG_CREATE
155 static void *
vips_buffer_cache_dump(VipsBufferCache * cache,void * a,void * b)156 vips_buffer_cache_dump( VipsBufferCache *cache, void *a, void *b )
157 {
158 printf( "VipsBufferCache: %p\n", cache );
159 printf( "\t%d buffers\n", g_slist_length( cache->buffers ) );
160 printf( "\tthread %p\n", cache->thread );
161 printf( "\timage %p\n", cache->im );
162 printf( "\tbuffer_thread %p\n", cache->buffer_thread );
163 printf( "\t%d in reserve\n", g_slist_length( cache->reserve ) );
164
165 return( NULL );
166 }
167 #endif /*DEBUG_CREATE*/
168
169 void
vips_buffer_dump_all(void)170 vips_buffer_dump_all( void )
171 {
172 #ifdef DEBUG
173 if( vips__buffer_all ) {
174 size_t reserve;
175 size_t alive;
176
177 printf( "buffers:\n" );
178
179 reserve = 0;
180 alive = 0;
181 vips_slist_map2( vips__buffer_all,
182 (VipsSListMap2Fn) vips_buffer_dump, &reserve, &alive );
183 printf( "%.3g MB alive\n", alive / (1024 * 1024.0) );
184 printf( "%.3g MB in reserve\n", reserve / (1024 * 1024.0) );
185 }
186
187 #ifdef DEBUG_CREATE
188 if( vips__buffer_cache_all ) {
189 printf( "buffers: %d buffer cache still alive\n",
190 g_slist_length( vips__buffer_cache_all ) );
191 vips_slist_map2( vips__buffer_cache_all,
192 (VipsSListMap2Fn) vips_buffer_cache_dump, NULL, NULL );
193 printf( "g_thread_self() == %p\n", g_thread_self() );
194 }
195 #endif /*DEBUG_CREATE*/
196 #endif /*DEBUG*/
197 }
198
199 static void
vips_buffer_free(VipsBuffer * buffer)200 vips_buffer_free( VipsBuffer *buffer )
201 {
202 VIPS_FREEF( vips_tracked_free, buffer->buf );
203 buffer->bsize = 0;
204 g_free( buffer );
205
206 #ifdef DEBUG
207 g_mutex_lock( vips__global_lock );
208
209 g_assert( g_slist_find( vips__buffer_all, buffer ) );
210 vips__buffer_all = g_slist_remove( vips__buffer_all, buffer );
211
212 g_mutex_unlock( vips__global_lock );
213 #endif /*DEBUG*/
214
215 #ifdef DEBUG_VERBOSE
216 printf( "vips_buffer_free: freeing buffer %p\n", buffer );
217 #endif /*DEBUG_VERBOSE*/
218 }
219
220 static void
buffer_thread_free(VipsBufferThread * buffer_thread)221 buffer_thread_free( VipsBufferThread *buffer_thread )
222 {
223 VIPS_FREEF( g_hash_table_destroy, buffer_thread->hash );
224 VIPS_FREE( buffer_thread );
225 }
226
227 /* Run for GDestroyNotify on the VipsBufferThread hash.
228 */
229 static void
buffer_cache_free(VipsBufferCache * cache)230 buffer_cache_free( VipsBufferCache *cache )
231 {
232 GSList *p;
233
234 #ifdef DEBUG_CREATE
235 g_mutex_lock( vips__global_lock );
236 vips__buffer_cache_all =
237 g_slist_remove( vips__buffer_cache_all, cache );
238 g_mutex_unlock( vips__global_lock );
239
240 printf( "buffer_cache_free: freeing cache %p on thread %p\n",
241 cache, g_thread_self() );
242 printf( "\t(%d caches left)\n",
243 g_slist_length( vips__buffer_cache_all ) );
244 #endif /*DEBUG_CREATE*/
245
246 /* Need to mark undone so we don't try and take them off this cache on
247 * unref.
248 */
249 for( p = cache->buffers; p; p = p->next ) {
250 VipsBuffer *buffer = (VipsBuffer *) p->data;
251
252 g_assert( buffer->done );
253 g_assert( buffer->cache == cache );
254
255 buffer->done = FALSE;
256 buffer->cache = NULL;
257 }
258 VIPS_FREEF( g_slist_free, cache->buffers );
259
260 for( p = cache->reserve; p; p = p->next ) {
261 VipsBuffer *buffer = (VipsBuffer *) p->data;
262
263 vips_buffer_free( buffer );
264 }
265 VIPS_FREEF( g_slist_free, cache->reserve );
266
267 g_free( cache );
268 }
269
270 static VipsBufferCache *
buffer_cache_new(VipsBufferThread * buffer_thread,VipsImage * im)271 buffer_cache_new( VipsBufferThread *buffer_thread, VipsImage *im )
272 {
273 VipsBufferCache *cache;
274
275 cache = g_new( VipsBufferCache, 1 );
276 cache->buffers = NULL;
277 cache->thread = g_thread_self();
278 cache->im = im;
279 cache->buffer_thread = buffer_thread;
280 cache->reserve = NULL;
281 cache->n_reserve = 0;
282
283 #ifdef DEBUG_CREATE
284 g_mutex_lock( vips__global_lock );
285 vips__buffer_cache_all =
286 g_slist_prepend( vips__buffer_cache_all, cache );
287 g_mutex_unlock( vips__global_lock );
288
289 printf( "buffer_cache_new: new cache %p for thread %p on image %p\n",
290 cache, g_thread_self(), im );
291 printf( "\t(%d caches now)\n",
292 g_slist_length( vips__buffer_cache_all ) );
293 #endif /*DEBUG_CREATE*/
294
295 return( cache );
296 }
297
298 static VipsBufferThread *
buffer_thread_new(void)299 buffer_thread_new( void )
300 {
301 VipsBufferThread *buffer_thread;
302
303 buffer_thread = g_new( VipsBufferThread, 1 );
304 buffer_thread->hash = g_hash_table_new_full(
305 g_direct_hash, g_direct_equal,
306 NULL, (GDestroyNotify) buffer_cache_free );
307 buffer_thread->thread = g_thread_self();
308
309 return( buffer_thread );
310 }
311
312 /* Get our private VipsBufferThread. NULL for non-worker threads.
313 */
314 static VipsBufferThread *
buffer_thread_get(void)315 buffer_thread_get( void )
316 {
317 VipsBufferThread *buffer_thread;
318
319 if( vips_thread_isworker() ) {
320 /* Workers get a private set of buffers.
321 */
322 if( !(buffer_thread = g_private_get( buffer_thread_key )) ) {
323 buffer_thread = buffer_thread_new();
324 g_private_set( buffer_thread_key, buffer_thread );
325 }
326
327 g_assert( buffer_thread->thread == g_thread_self() );
328 }
329 else
330 /* Non-workers don't have one.
331 */
332 buffer_thread = NULL;
333
334 return( buffer_thread );
335 }
336
337 /* Get the VipsBufferCache for this image, or NULL for a non-worker.
338 */
339 static VipsBufferCache *
buffer_cache_get(VipsImage * im)340 buffer_cache_get( VipsImage *im )
341 {
342 VipsBufferThread *buffer_thread;
343 VipsBufferCache *cache;
344
345 if( (buffer_thread = buffer_thread_get()) ) {
346 if( !(cache = (VipsBufferCache *)
347 g_hash_table_lookup( buffer_thread->hash, im )) ) {
348 cache = buffer_cache_new( buffer_thread, im );
349 g_hash_table_insert( buffer_thread->hash, im, cache );
350 }
351
352 g_assert( cache->thread == g_thread_self() );
353 }
354 else
355 cache = NULL;
356
357 return( cache );
358 }
359
360 /* Pixels have been calculated: publish for other parts of this thread to see.
361 */
362 void
vips_buffer_done(VipsBuffer * buffer)363 vips_buffer_done( VipsBuffer *buffer )
364 {
365 VipsImage *im = buffer->im;
366 VipsBufferCache *cache;
367
368 if( !buffer->done &&
369 (cache = buffer_cache_get( im )) ) {
370 g_assert( !g_slist_find( cache->buffers, buffer ) );
371 g_assert( !buffer->cache );
372
373 buffer->done = TRUE;
374 buffer->cache = cache;
375
376 cache->buffers = g_slist_prepend( cache->buffers, buffer );
377
378 #ifdef DEBUG_VERBOSE
379 printf( "vips_buffer_done: "
380 "thread %p adding buffer %p to cache %p\n",
381 g_thread_self(), buffer, cache );
382 vips_buffer_print( buffer );
383 #endif /*DEBUG_VERBOSE*/
384 }
385 }
386
387 /* Take off the public 'done' list. Make sure it has no calculated pixels in.
388 */
389 void
vips_buffer_undone(VipsBuffer * buffer)390 vips_buffer_undone( VipsBuffer *buffer )
391 {
392 if( buffer->done ) {
393 VipsBufferCache *cache = buffer->cache;
394
395 #ifdef DEBUG_VERBOSE
396 printf( "vips_buffer_undone: thread %p removing "
397 "buffer %p from cache %p\n",
398 g_thread_self(), buffer, cache );
399 #endif /*DEBUG_VERBOSE*/
400
401 g_assert( cache->thread == g_thread_self() );
402 g_assert( cache->buffer_thread->thread == cache->thread );
403 g_assert( g_slist_find( cache->buffers, buffer ) );
404 g_assert( buffer_thread_get() );
405 g_assert( cache->buffer_thread == buffer_thread_get() );
406
407 cache->buffers = g_slist_remove( cache->buffers, buffer );
408 buffer->done = FALSE;
409
410 #ifdef DEBUG_VERBOSE
411 printf( "vips_buffer_undone: %d buffers left\n",
412 g_slist_length( cache->buffers ) );
413 #endif /*DEBUG_VERBOSE*/
414 }
415
416 buffer->cache = NULL;
417 buffer->area.width = 0;
418 buffer->area.height = 0;
419 }
420
421 void
vips_buffer_unref(VipsBuffer * buffer)422 vips_buffer_unref( VipsBuffer *buffer )
423 {
424 #ifdef DEBUG_VERBOSE
425 printf( "** vips_buffer_unref: left = %d, top = %d, "
426 "width = %d, height = %d (%p)\n",
427 buffer->area.left, buffer->area.top,
428 buffer->area.width, buffer->area.height,
429 buffer );
430 #endif /*DEBUG_VERBOSE*/
431
432 g_assert( buffer->ref_count > 0 );
433
434 buffer->ref_count -= 1;
435
436 if( buffer->ref_count == 0 ) {
437 VipsBufferCache *cache;
438
439 #ifdef DEBUG_VERBOSE
440 if( !buffer->done )
441 printf( "vips_buffer_unref: buffer was not done\n" );
442 #endif /*DEBUG_VERBOSE*/
443
444 vips_buffer_undone( buffer );
445
446 /* Place on this thread's reserve list for reuse.
447 */
448 if( (cache = buffer_cache_get( buffer->im )) &&
449 cache->n_reserve < buffer_cache_max_reserve ) {
450 g_assert( !buffer->cache );
451
452 cache->reserve =
453 g_slist_prepend( cache->reserve, buffer );
454 cache->n_reserve += 1;
455
456 buffer->cache = cache;
457 buffer->area.width = 0;
458 buffer->area.height = 0;
459 }
460 else
461 vips_buffer_free( buffer );
462 }
463 }
464
465 static int
buffer_move(VipsBuffer * buffer,VipsRect * area)466 buffer_move( VipsBuffer *buffer, VipsRect *area )
467 {
468 VipsImage *im = buffer->im;
469 size_t new_bsize;
470
471 g_assert( buffer->ref_count == 1 );
472
473 vips_buffer_undone( buffer );
474 g_assert( !buffer->done );
475
476 buffer->area = *area;
477
478 new_bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL( im ) *
479 area->width * area->height;
480 if( buffer->bsize < new_bsize ||
481 !buffer->buf ) {
482 buffer->bsize = new_bsize;
483 VIPS_FREEF( vips_tracked_free, buffer->buf );
484 if( !(buffer->buf = vips_tracked_malloc( buffer->bsize )) )
485 return( -1 );
486 }
487
488 return( 0 );
489 }
490
491 /* Make a new buffer.
492 */
493 VipsBuffer *
vips_buffer_new(VipsImage * im,VipsRect * area)494 vips_buffer_new( VipsImage *im, VipsRect *area )
495 {
496 VipsBufferCache *cache;
497 VipsBuffer *buffer;
498
499 if( (cache = buffer_cache_get( im )) &&
500 cache->reserve ) {
501 buffer = (VipsBuffer *) cache->reserve->data;
502 cache->reserve = g_slist_remove( cache->reserve, buffer );
503 cache->n_reserve -= 1;
504
505 g_assert( buffer->im == im );
506 g_assert( buffer->done == FALSE );
507 g_assert( buffer->cache );
508
509 buffer->ref_count = 1;
510 buffer->done = FALSE;
511 buffer->cache = NULL;
512 }
513 else {
514 buffer = g_new0( VipsBuffer, 1 );
515 buffer->ref_count = 1;
516 buffer->im = im;
517 buffer->done = FALSE;
518 buffer->cache = NULL;
519 buffer->buf = NULL;
520 buffer->bsize = 0;
521
522 #ifdef DEBUG
523 g_mutex_lock( vips__global_lock );
524 vips__buffer_all =
525 g_slist_prepend( vips__buffer_all, buffer );
526 g_mutex_unlock( vips__global_lock );
527 #endif /*DEBUG*/
528 }
529
530 if( buffer_move( buffer, area ) ) {
531 vips_buffer_free( buffer );
532 return( NULL );
533 }
534
535 return( buffer );
536 }
537
538 /* Find an existing buffer that encloses area and return a ref. Or NULL for no
539 * existing buffer.
540 */
541 static VipsBuffer *
buffer_find(VipsImage * im,VipsRect * r)542 buffer_find( VipsImage *im, VipsRect *r )
543 {
544 VipsBufferCache *cache;
545 VipsBuffer *buffer;
546 GSList *p;
547 VipsRect *area;
548
549 if( !(cache = buffer_cache_get( im )) )
550 return( NULL );
551
552 /* This needs to be quick :-( don't use
553 * vips_slist_map2()/vips_rect_includesrect(), do the search
554 * inline.
555 *
556 * FIXME we return the first enclosing buffer, perhaps we should
557 * search for the largest?
558 */
559 for( p = cache->buffers; p; p = p->next ) {
560 buffer = (VipsBuffer *) p->data;
561 area = &buffer->area;
562
563 if( area->left <= r->left &&
564 area->top <= r->top &&
565 area->left + area->width >= r->left + r->width &&
566 area->top + area->height >= r->top + r->height ) {
567 buffer->ref_count += 1;
568
569 #ifdef DEBUG_VERBOSE
570 printf( "buffer_find: left = %d, top = %d, "
571 "width = %d, height = %d, count = %d (%p)\n",
572 buffer->area.left, buffer->area.top,
573 buffer->area.width, buffer->area.height,
574 buffer->ref_count,
575 buffer );
576 #endif /*DEBUG_VERBOSE*/
577
578 return( buffer );
579 }
580 }
581
582 return( NULL );
583 }
584
585 /* Return a ref to a buffer that encloses area. The buffer we return might be
586 * done.
587 */
588 VipsBuffer *
vips_buffer_ref(VipsImage * im,VipsRect * area)589 vips_buffer_ref( VipsImage *im, VipsRect *area )
590 {
591 VipsBuffer *buffer;
592
593 if( (buffer = buffer_find( im, area )) )
594 return( buffer );
595 else
596 return( vips_buffer_new( im, area ) );
597 }
598
599 /* Unref old, ref new, in a single operation. Reuse stuff if we can. The
600 * buffer we return might or might not be done.
601 */
602 VipsBuffer *
vips_buffer_unref_ref(VipsBuffer * old_buffer,VipsImage * im,VipsRect * area)603 vips_buffer_unref_ref( VipsBuffer *old_buffer, VipsImage *im, VipsRect *area )
604 {
605 VipsBuffer *buffer;
606
607 g_assert( !old_buffer ||
608 old_buffer->im == im );
609
610 /* Is the current buffer OK?
611 */
612 if( old_buffer &&
613 vips_rect_includesrect( &old_buffer->area, area ) )
614 return( old_buffer );
615
616 /* Does the new area already have a buffer?
617 */
618 if( (buffer = buffer_find( im, area )) ) {
619 VIPS_FREEF( vips_buffer_unref, old_buffer );
620 return( buffer );
621 }
622
623 /* Is the current buffer unshared? We can just move it.
624 */
625 if( old_buffer &&
626 old_buffer->ref_count == 1 ) {
627 if( buffer_move( old_buffer, area ) ) {
628 vips_buffer_unref( old_buffer );
629 return( NULL );
630 }
631
632 return( old_buffer );
633 }
634
635 /* Fallback ... unref the old one, make a new one.
636 */
637 VIPS_FREEF( vips_buffer_unref, old_buffer );
638 if( !(buffer = vips_buffer_new( im, area )) )
639 return( NULL );
640
641 return( buffer );
642 }
643
644 static void
buffer_thread_destroy_notify(VipsBufferThread * buffer_thread)645 buffer_thread_destroy_notify( VipsBufferThread *buffer_thread )
646 {
647 /* We only come here if vips_thread_shutdown() was not called for this
648 * thread. Do our best to clean up.
649 *
650 * GPrivate has stopped working by this point in destruction, be
651 * careful not to touch that.
652 */
653 buffer_thread_free( buffer_thread );
654 }
655
656 /* Init the buffer cache system. This is called during vips_init.
657 */
658 void
vips__buffer_init(void)659 vips__buffer_init( void )
660 {
661 static GPrivate private =
662 G_PRIVATE_INIT( (GDestroyNotify) buffer_thread_destroy_notify );
663
664 buffer_thread_key = &private;
665
666 if( buffer_cache_max_reserve < 1 )
667 printf( "vips__buffer_init: buffer reserve disabled\n" );
668
669 #ifdef DEBUG
670 printf( "vips__buffer_init: DEBUG enabled\n" );
671 #endif /*DEBUG*/
672
673 #ifdef DEBUG_CREATE
674 printf( "vips__buffer_init: DEBUG_CREATE enabled\n" );
675 #endif /*DEBUG_CREATE*/
676 }
677
678 void
vips__buffer_shutdown(void)679 vips__buffer_shutdown( void )
680 {
681 VipsBufferThread *buffer_thread;
682
683 if( (buffer_thread = g_private_get( buffer_thread_key )) ) {
684 buffer_thread_free( buffer_thread );
685 g_private_set( buffer_thread_key, NULL );
686 }
687 }
688