libvips/libvips/iofuncs/buffer.c

690 lines
16 KiB
C
Raw Normal View History

2009-08-16 17:00:08 +02:00
/* Manage sets of pixel buffers on an image.
*
* 30/10/06
* - from window.c
* 2/2/07
* - speed up the search, use our own lock (thanks Christian)
* 5/2/07
* - split to many buffer lists per image
* 11/2/07
* - split to a buffer hash per thread
* - reuse buffer mallocs when we can
* 20/2/07
* - add VipsBufferCacheList and we can avoid some hash ops on
2009-08-16 17:00:08 +02:00
* done/undone
2010-03-05 15:43:49 +01:00
* 5/3/10
* - move invalid stuff to region
2010-03-05 21:21:06 +01:00
* - move link maintenance to im_demand_hint
* 21/9/11
* - switch to vips_tracked_malloc()
* 18/12/13
* - keep a few buffers in reserve per image, stops malloc/free
* cycling when sharing is repeatedly discovered
* 6/6/16
* - free buffers on image close as well as thread exit, so main thread
* buffers don't clog up the system
2016-10-13 15:57:18 +02:00
* 13/10/16
* - better solution: don't keep a buffercache for non-workers
2009-08-16 17:00:08 +02:00
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
2009-08-16 17:00:08 +02:00
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/*
#define DEBUG_VERBOSE
#define DEBUG_CREATE
#define DEBUG
*/
2009-08-16 17:00:08 +02:00
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib/gi18n-lib.h>
2009-08-16 17:00:08 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/thread.h>
#ifdef DEBUG
/* Track all buffers here for debugging.
2009-08-16 17:00:08 +02:00
*/
static GSList *vips__buffer_all = NULL;
2009-08-16 17:00:08 +02:00
#endif /*DEBUG*/
#ifdef DEBUG_CREATE
static GSList *vips__buffer_cache_all = NULL;
2009-08-16 17:00:08 +02:00
#endif /*DEBUG_CREATE*/
/* The maximum numbers of buffers we hold in reserve per image.
2013-12-16 10:22:05 +01:00
*/
static const int buffer_cache_max_reserve = 2;
2013-12-16 10:22:05 +01:00
2016-10-13 15:57:18 +02:00
/* Workers have a BufferThread (and BufferCache) in a GPrivate they have
* exclusive access to.
*/
static GPrivate *buffer_thread_key = NULL;
2009-08-16 17:00:08 +02:00
2016-10-13 15:57:18 +02:00
void
vips_buffer_print( VipsBuffer *buffer )
{
printf( "VipsBuffer: %p ref_count = %d, ", buffer, buffer->ref_count );
printf( "im = %p, ", buffer->im );
printf( "area.left = %d, ", buffer->area.left );
printf( "area.top = %d, ", buffer->area.top );
printf( "area.width = %d, ", buffer->area.width );
printf( "area.height = %d, ", buffer->area.height );
printf( "done = %d, ", buffer->done );
printf( "cache = %p, ", buffer->cache );
2016-10-13 15:57:18 +02:00
printf( "buf = %p, ", buffer->buf );
printf( "bsize = %zd\n", buffer->bsize );
}
#ifdef DEBUG
static void *
vips_buffer_dump( VipsBuffer *buffer, size_t *reserve, size_t *alive )
{
vips_buffer_print( buffer );
2016-10-13 15:57:18 +02:00
g_assert( buffer->im );
g_assert( buffer->buf );
if( !buffer->cache &&
!buffer->done ) {
/* Global buffer, not linked to any cache.
*/
printf( "global buffer %p, %.3g MB\n",
buffer, buffer->bsize / (1024 * 1024.0) );
*alive += buffer->bsize;
}
2016-10-13 15:57:18 +02:00
else if( buffer->cache &&
buffer->done &&
!vips_rect_isempty( &buffer->area ) &&
g_slist_find( buffer->cache->buffers, buffer ) ) {
/* Published on a thread.
*/
printf( "thread buffer %p, %.3g MB\n",
buffer, buffer->bsize / (1024 * 1024.0) );
*alive += buffer->bsize;
}
else if( buffer->ref_count == 0 &&
buffer->cache &&
!buffer->done &&
vips_rect_isempty( &buffer->area ) &&
g_slist_find( buffer->cache->reserve, buffer ) )
/* Held in reserve.
*/
*reserve += buffer->bsize;
2016-10-13 15:57:18 +02:00
else
printf( "buffer craziness!\n" );
return( NULL );
}
#endif /*DEBUG*/
#ifdef DEBUG_CREATE
static void *
vips_buffer_cache_dump( VipsBufferCache *cache, void *a, void *b )
{
printf( "VipsBufferCache: %p\n", cache );
printf( "\t%d buffers\n", g_slist_length( cache->buffers ) );
printf( "\tthread %p\n", cache->thread );
printf( "\timage %p\n", cache->im );
printf( "\tbuffer_thread %p\n", cache->buffer_thread );
printf( "\t%d in reserve\n", g_slist_length( cache->reserve ) );
return( NULL );
}
#endif /*DEBUG_CREATE*/
void
vips_buffer_dump_all( void )
{
#ifdef DEBUG
if( vips__buffer_all ) {
size_t reserve;
size_t alive;
printf( "buffers:\n" );
reserve = 0;
alive = 0;
vips_slist_map2( vips__buffer_all,
(VipsSListMap2Fn) vips_buffer_dump, &reserve, &alive );
printf( "%.3g MB alive\n", alive / (1024 * 1024.0) );
printf( "%.3g MB in reserve\n", reserve / (1024 * 1024.0) );
}
#ifdef DEBUG_CREATE
if( vips__buffer_cache_all ) {
printf( "buffers: %d buffer cache still alive\n",
g_slist_length( vips__buffer_cache_all ) );
vips_slist_map2( vips__buffer_cache_all,
(VipsSListMap2Fn) vips_buffer_cache_dump, NULL, NULL );
printf( "g_thread_self() == %p\n", g_thread_self() );
}
#endif /*DEBUG_CREATE*/
#endif /*DEBUG*/
}
static void
vips_buffer_free( VipsBuffer *buffer )
{
2016-05-17 20:43:47 +02:00
VIPS_FREEF( vips_tracked_free, buffer->buf );
buffer->bsize = 0;
g_free( buffer );
#ifdef DEBUG
g_mutex_lock( vips__global_lock );
g_assert( g_slist_find( vips__buffer_all, buffer ) );
vips__buffer_all = g_slist_remove( vips__buffer_all, buffer );
g_mutex_unlock( vips__global_lock );
#endif /*DEBUG*/
#ifdef DEBUG_VERBOSE
printf( "vips_buffer_free: freeing buffer %p\n", buffer );
#endif /*DEBUG_VERBOSE*/
}
static void
buffer_thread_free( VipsBufferThread *buffer_thread )
{
VIPS_FREEF( g_hash_table_destroy, buffer_thread->hash );
VIPS_FREE( buffer_thread );
}
2016-10-13 15:57:18 +02:00
/* Run for GDestroyNotify on the VipsBufferThread hash.
*/
2009-08-16 17:00:08 +02:00
static void
buffer_cache_free( VipsBufferCache *cache )
2009-08-16 17:00:08 +02:00
{
GSList *p;
2009-08-16 17:00:08 +02:00
#ifdef DEBUG_CREATE
g_mutex_lock( vips__global_lock );
vips__buffer_cache_all =
g_slist_remove( vips__buffer_cache_all, cache );
g_mutex_unlock( vips__global_lock );
2009-08-16 17:00:08 +02:00
printf( "buffer_cache_free: freeing cache %p on thread %p\n",
2009-08-16 17:00:08 +02:00
cache, g_thread_self() );
printf( "\t(%d caches left)\n",
g_slist_length( vips__buffer_cache_all ) );
2009-08-16 17:00:08 +02:00
#endif /*DEBUG_CREATE*/
/* Need to mark undone so we don't try and take them off this cache on
* unref.
*/
for( p = cache->buffers; p; p = p->next ) {
VipsBuffer *buffer = (VipsBuffer *) p->data;
2016-10-13 15:57:18 +02:00
g_assert( buffer->done );
g_assert( buffer->cache == cache );
buffer->done = FALSE;
2016-10-13 15:57:18 +02:00
buffer->cache = NULL;
}
VIPS_FREEF( g_slist_free, cache->buffers );
for( p = cache->reserve; p; p = p->next ) {
VipsBuffer *buffer = (VipsBuffer *) p->data;
2009-08-16 17:00:08 +02:00
vips_buffer_free( buffer );
}
VIPS_FREEF( g_slist_free, cache->reserve );
2009-08-16 17:00:08 +02:00
g_free( cache );
2009-08-16 17:00:08 +02:00
}
static VipsBufferCache *
buffer_cache_new( VipsBufferThread *buffer_thread, VipsImage *im )
2009-08-16 17:00:08 +02:00
{
VipsBufferCache *cache;
2009-08-16 17:00:08 +02:00
cache = g_new( VipsBufferCache, 1 );
cache->buffers = NULL;
2009-08-16 17:00:08 +02:00
cache->thread = g_thread_self();
cache->im = im;
cache->buffer_thread = buffer_thread;
cache->reserve = NULL;
cache->n_reserve = 0;
2009-08-16 17:00:08 +02:00
#ifdef DEBUG_CREATE
g_mutex_lock( vips__global_lock );
vips__buffer_cache_all =
g_slist_prepend( vips__buffer_cache_all, cache );
g_mutex_unlock( vips__global_lock );
2009-08-16 17:00:08 +02:00
printf( "buffer_cache_new: new cache %p for thread %p on image %p\n",
cache, g_thread_self(), im );
printf( "\t(%d caches now)\n",
g_slist_length( vips__buffer_cache_all ) );
2009-08-16 17:00:08 +02:00
#endif /*DEBUG_CREATE*/
return( cache );
}
static VipsBufferThread *
buffer_thread_new( void )
{
VipsBufferThread *buffer_thread;
buffer_thread = g_new( VipsBufferThread, 1 );
buffer_thread->hash = g_hash_table_new_full(
g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) buffer_cache_free );
buffer_thread->thread = g_thread_self();
return( buffer_thread );
}
2016-10-13 15:57:18 +02:00
/* Get our private VipsBufferThread. NULL for non-worker threads.
*/
static VipsBufferThread *
buffer_thread_get( void )
{
VipsBufferThread *buffer_thread;
Revised threading system (#3105) * reimplement threadpool just a set of threads that get recycled "ninja test" passes and dzsave seems to work, though pytest fails for some reason * clean up threading code a bit move base stuff into thread.c, so we have a simple thread -> threadset -> threadpool layering * start trying to revert g_threadpool based on the original commit * working! nice low systime again still need to repply cnages to threadpool.c since 80e0cc3d1 * reapply fixes from master so threadpool.c is now up to date * rename VipsThread as VipsWorker a bit less confusing * use a semaphore to count workers in a pool * tidy up * formatting * dynamic threadpool sizing based on counting the number of blocked threads in each pool it works, but the improvement is not great :( * add "concurrency" metadata item so operators can hint threadpool size (dzsave especially) * don't use thinstrip for small images * add RGB mode to openslide since flatten was taking 20% of CPU time for dzsave * fix up rgb mode now actually works * make the tile buffer per thread in the new openslideload rgb mode * fix dynamic pool downsize * mild refactoring * fix the buffer system oops, turned it off by mistake * all done! * revise changelog * Update libvips/iofuncs/threadset.c Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl> * Update libvips/iofuncs/threadset.c Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl> * LSan: add libMagickCore to suppression file * Revert "Remove mutex lock for VipsThreadStartFn" This reverts commit 41440491. * add VIPS_MAX_THREADS to set a hard limit on the threadset size * Revert "Revert "Remove mutex lock for VipsThreadStartFn"" This reverts commit 77e8520966ba79194fff3b4e648bbd295cd5c260. * remove sslock from sink.c * move fixed threadpool build to init not first use * add some doc comments * revert test suite threshold change * add a test for MAX_THREADS and move the test tmp/ area into the builddir * limit VIPS_MAX_THREADS to sane values * use tabs rather than spaces Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl>
2022-10-26 16:25:19 +02:00
if( vips_thread_isvips() ) {
/* Our threads get a set of private buffers, since we know we
* will be calling vips_thread_shutdown() on thread
* termination.
*/
if( !(buffer_thread = g_private_get( buffer_thread_key )) ) {
buffer_thread = buffer_thread_new();
g_private_set( buffer_thread_key, buffer_thread );
}
g_assert( buffer_thread->thread == g_thread_self() );
}
2016-10-13 15:57:18 +02:00
else
Revised threading system (#3105) * reimplement threadpool just a set of threads that get recycled "ninja test" passes and dzsave seems to work, though pytest fails for some reason * clean up threading code a bit move base stuff into thread.c, so we have a simple thread -> threadset -> threadpool layering * start trying to revert g_threadpool based on the original commit * working! nice low systime again still need to repply cnages to threadpool.c since 80e0cc3d1 * reapply fixes from master so threadpool.c is now up to date * rename VipsThread as VipsWorker a bit less confusing * use a semaphore to count workers in a pool * tidy up * formatting * dynamic threadpool sizing based on counting the number of blocked threads in each pool it works, but the improvement is not great :( * add "concurrency" metadata item so operators can hint threadpool size (dzsave especially) * don't use thinstrip for small images * add RGB mode to openslide since flatten was taking 20% of CPU time for dzsave * fix up rgb mode now actually works * make the tile buffer per thread in the new openslideload rgb mode * fix dynamic pool downsize * mild refactoring * fix the buffer system oops, turned it off by mistake * all done! * revise changelog * Update libvips/iofuncs/threadset.c Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl> * Update libvips/iofuncs/threadset.c Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl> * LSan: add libMagickCore to suppression file * Revert "Remove mutex lock for VipsThreadStartFn" This reverts commit 41440491. * add VIPS_MAX_THREADS to set a hard limit on the threadset size * Revert "Revert "Remove mutex lock for VipsThreadStartFn"" This reverts commit 77e8520966ba79194fff3b4e648bbd295cd5c260. * remove sslock from sink.c * move fixed threadpool build to init not first use * add some doc comments * revert test suite threshold change * add a test for MAX_THREADS and move the test tmp/ area into the builddir * limit VIPS_MAX_THREADS to sane values * use tabs rather than spaces Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl>
2022-10-26 16:25:19 +02:00
/* Non-vips threads don't have one.
*/
2016-10-13 15:57:18 +02:00
buffer_thread = NULL;
return( buffer_thread );
}
2016-10-13 15:57:18 +02:00
/* Get the VipsBufferCache for this image, or NULL for a non-worker.
*/
static VipsBufferCache *
buffer_cache_get( VipsImage *im )
2009-08-16 17:00:08 +02:00
{
2016-10-13 15:57:18 +02:00
VipsBufferThread *buffer_thread;
VipsBufferCache *cache;
2009-08-16 17:00:08 +02:00
2016-10-13 15:57:18 +02:00
if( (buffer_thread = buffer_thread_get()) ) {
if( !(cache = (VipsBufferCache *)
g_hash_table_lookup( buffer_thread->hash, im )) ) {
cache = buffer_cache_new( buffer_thread, im );
g_hash_table_insert( buffer_thread->hash, im, cache );
}
2016-10-13 15:57:18 +02:00
g_assert( cache->thread == g_thread_self() );
2009-08-16 17:00:08 +02:00
}
2016-10-13 15:57:18 +02:00
else
cache = NULL;
return( cache );
2009-08-16 17:00:08 +02:00
}
/* Pixels have been calculated: publish for other parts of this thread to see.
*/
void
vips_buffer_done( VipsBuffer *buffer )
2009-08-16 17:00:08 +02:00
{
2016-10-13 15:57:18 +02:00
VipsImage *im = buffer->im;
VipsBufferCache *cache;
2009-08-16 17:00:08 +02:00
2016-10-13 15:57:18 +02:00
if( !buffer->done &&
(cache = buffer_cache_get( im )) ) {
g_assert( !g_slist_find( cache->buffers, buffer ) );
g_assert( !buffer->cache );
2009-08-16 17:00:08 +02:00
buffer->done = TRUE;
buffer->cache = cache;
2014-01-07 22:31:00 +01:00
cache->buffers = g_slist_prepend( cache->buffers, buffer );
#ifdef DEBUG_VERBOSE
printf( "vips_buffer_done: "
"thread %p adding buffer %p to cache %p\n",
g_thread_self(), buffer, cache );
vips_buffer_print( buffer );
#endif /*DEBUG_VERBOSE*/
2009-08-16 17:00:08 +02:00
}
}
2013-12-13 15:05:10 +01:00
/* Take off the public 'done' list. Make sure it has no calculated pixels in.
2009-08-16 17:00:08 +02:00
*/
void
vips_buffer_undone( VipsBuffer *buffer )
2009-08-16 17:00:08 +02:00
{
if( buffer->done ) {
VipsBufferCache *cache = buffer->cache;
2009-08-16 17:00:08 +02:00
#ifdef DEBUG_VERBOSE
printf( "vips_buffer_undone: thread %p removing "
2009-08-16 17:00:08 +02:00
"buffer %p from cache %p\n",
g_thread_self(), buffer, cache );
#endif /*DEBUG_VERBOSE*/
2009-08-16 17:00:08 +02:00
2016-10-13 15:57:18 +02:00
g_assert( cache->thread == g_thread_self() );
g_assert( cache->buffer_thread->thread == cache->thread );
g_assert( g_slist_find( cache->buffers, buffer ) );
2016-10-13 15:57:18 +02:00
g_assert( buffer_thread_get() );
2014-01-26 11:54:31 +01:00
g_assert( cache->buffer_thread == buffer_thread_get() );
2009-08-16 17:00:08 +02:00
cache->buffers = g_slist_remove( cache->buffers, buffer );
2009-08-16 17:00:08 +02:00
buffer->done = FALSE;
#ifdef DEBUG_VERBOSE
printf( "vips_buffer_undone: %d buffers left\n",
2016-10-13 15:57:18 +02:00
g_slist_length( cache->buffers ) );
#endif /*DEBUG_VERBOSE*/
2009-08-16 17:00:08 +02:00
}
2013-12-13 15:05:10 +01:00
2014-01-09 17:56:44 +01:00
buffer->cache = NULL;
2013-12-13 15:05:10 +01:00
buffer->area.width = 0;
buffer->area.height = 0;
2009-08-16 17:00:08 +02:00
}
void
vips_buffer_unref( VipsBuffer *buffer )
2009-08-16 17:00:08 +02:00
{
#ifdef DEBUG_VERBOSE
printf( "** vips_buffer_unref: left = %d, top = %d, "
2009-08-16 17:00:08 +02:00
"width = %d, height = %d (%p)\n",
buffer->area.left, buffer->area.top,
buffer->area.width, buffer->area.height,
buffer );
#endif /*DEBUG_VERBOSE*/
2009-08-16 17:00:08 +02:00
2010-03-05 15:43:49 +01:00
g_assert( buffer->ref_count > 0 );
2009-08-16 17:00:08 +02:00
buffer->ref_count -= 1;
if( buffer->ref_count == 0 ) {
2016-10-13 15:57:18 +02:00
VipsBufferCache *cache;
#ifdef DEBUG_VERBOSE
2009-08-16 17:00:08 +02:00
if( !buffer->done )
printf( "vips_buffer_unref: buffer was not done\n" );
#endif /*DEBUG_VERBOSE*/
2009-08-16 17:00:08 +02:00
vips_buffer_undone( buffer );
2009-08-16 17:00:08 +02:00
/* Place on this thread's reserve list for reuse.
*/
2016-10-13 15:57:18 +02:00
if( (cache = buffer_cache_get( buffer->im )) &&
2016-10-12 13:46:27 +02:00
cache->n_reserve < buffer_cache_max_reserve ) {
2014-01-07 22:31:00 +01:00
g_assert( !buffer->cache );
cache->reserve =
g_slist_prepend( cache->reserve, buffer );
cache->n_reserve += 1;
2016-10-13 15:57:18 +02:00
buffer->cache = cache;
buffer->area.width = 0;
buffer->area.height = 0;
}
else
vips_buffer_free( buffer );
2009-08-16 17:00:08 +02:00
}
}
static int
buffer_move( VipsBuffer *buffer, VipsRect *area )
2009-08-16 17:00:08 +02:00
{
VipsImage *im = buffer->im;
2009-08-16 17:00:08 +02:00
size_t new_bsize;
2010-03-05 15:43:49 +01:00
g_assert( buffer->ref_count == 1 );
2009-08-16 17:00:08 +02:00
vips_buffer_undone( buffer );
2010-03-05 15:43:49 +01:00
g_assert( !buffer->done );
2009-08-16 17:00:08 +02:00
2013-12-13 13:20:05 +01:00
buffer->area = *area;
new_bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL( im ) *
2009-08-16 17:00:08 +02:00
area->width * area->height;
if( buffer->bsize < new_bsize ||
!buffer->buf ) {
2009-08-16 17:00:08 +02:00
buffer->bsize = new_bsize;
VIPS_FREEF( vips_tracked_free, buffer->buf );
if( !(buffer->buf = vips_tracked_malloc( buffer->bsize )) )
2009-08-16 17:00:08 +02:00
return( -1 );
}
return( 0 );
}
/* Make a new buffer.
*/
VipsBuffer *
vips_buffer_new( VipsImage *im, VipsRect *area )
{
2016-10-13 15:57:18 +02:00
VipsBufferCache *cache;
VipsBuffer *buffer;
2016-10-13 15:57:18 +02:00
if( (cache = buffer_cache_get( im )) &&
cache->reserve ) {
buffer = (VipsBuffer *) cache->reserve->data;
cache->reserve = g_slist_remove( cache->reserve, buffer );
cache->n_reserve -= 1;
g_assert( buffer->im == im );
g_assert( buffer->done == FALSE );
2016-10-13 15:57:18 +02:00
g_assert( buffer->cache );
buffer->ref_count = 1;
buffer->done = FALSE;
2016-10-13 15:57:18 +02:00
buffer->cache = NULL;
}
else {
buffer = g_new0( VipsBuffer, 1 );
buffer->ref_count = 1;
buffer->im = im;
buffer->done = FALSE;
buffer->cache = NULL;
buffer->buf = NULL;
buffer->bsize = 0;
#ifdef DEBUG
g_mutex_lock( vips__global_lock );
vips__buffer_all =
g_slist_prepend( vips__buffer_all, buffer );
g_mutex_unlock( vips__global_lock );
#endif /*DEBUG*/
}
if( buffer_move( buffer, area ) ) {
vips_buffer_free( buffer );
return( NULL );
}
return( buffer );
}
2016-10-13 15:57:18 +02:00
/* Find an existing buffer that encloses area and return a ref. Or NULL for no
* existing buffer.
2009-08-16 17:00:08 +02:00
*/
static VipsBuffer *
buffer_find( VipsImage *im, VipsRect *r )
2009-08-16 17:00:08 +02:00
{
2016-10-13 15:57:18 +02:00
VipsBufferCache *cache;
VipsBuffer *buffer;
2009-08-16 17:00:08 +02:00
GSList *p;
VipsRect *area;
2009-08-16 17:00:08 +02:00
2016-10-13 15:57:18 +02:00
if( !(cache = buffer_cache_get( im )) )
return( NULL );
2009-08-16 17:00:08 +02:00
/* This needs to be quick :-( don't use
2016-10-13 15:57:18 +02:00
* vips_slist_map2()/vips_rect_includesrect(), do the search
* inline.
2010-03-05 15:43:49 +01:00
*
* FIXME we return the first enclosing buffer, perhaps we should
* search for the largest?
2009-08-16 17:00:08 +02:00
*/
for( p = cache->buffers; p; p = p->next ) {
buffer = (VipsBuffer *) p->data;
2009-08-16 17:00:08 +02:00
area = &buffer->area;
if( area->left <= r->left &&
area->top <= r->top &&
area->left + area->width >= r->left + r->width &&
area->top + area->height >= r->top + r->height ) {
buffer->ref_count += 1;
#ifdef DEBUG_VERBOSE
2016-10-13 15:57:18 +02:00
printf( "buffer_find: left = %d, top = %d, "
2009-08-16 17:00:08 +02:00
"width = %d, height = %d, count = %d (%p)\n",
buffer->area.left, buffer->area.top,
buffer->area.width, buffer->area.height,
buffer->ref_count,
buffer );
#endif /*DEBUG_VERBOSE*/
2009-08-16 17:00:08 +02:00
return( buffer );
2009-08-16 17:00:08 +02:00
}
}
return( NULL );
2009-08-16 17:00:08 +02:00
}
/* Return a ref to a buffer that encloses area. The buffer we return might be
* done.
2009-08-16 17:00:08 +02:00
*/
VipsBuffer *
vips_buffer_ref( VipsImage *im, VipsRect *area )
2009-08-16 17:00:08 +02:00
{
VipsBuffer *buffer;
2009-08-16 17:00:08 +02:00
2016-10-13 15:57:18 +02:00
if( (buffer = buffer_find( im, area )) )
return( buffer );
else
return( vips_buffer_new( im, area ) );
2009-08-16 17:00:08 +02:00
}
2010-03-05 15:43:49 +01:00
/* Unref old, ref new, in a single operation. Reuse stuff if we can. The
* buffer we return might or might not be done.
2009-08-16 17:00:08 +02:00
*/
VipsBuffer *
vips_buffer_unref_ref( VipsBuffer *old_buffer, VipsImage *im, VipsRect *area )
2009-08-16 17:00:08 +02:00
{
VipsBuffer *buffer;
2009-08-16 17:00:08 +02:00
g_assert( !old_buffer ||
old_buffer->im == im );
2009-08-16 17:00:08 +02:00
2010-03-05 15:43:49 +01:00
/* Is the current buffer OK?
*/
2010-03-05 21:21:06 +01:00
if( old_buffer &&
vips_rect_includesrect( &old_buffer->area, area ) )
2010-03-05 15:43:49 +01:00
return( old_buffer );
/* Does the new area already have a buffer?
*/
if( (buffer = buffer_find( im, area )) ) {
VIPS_FREEF( vips_buffer_unref, old_buffer );
2010-03-05 15:43:49 +01:00
return( buffer );
2009-08-16 17:00:08 +02:00
}
2010-03-05 15:43:49 +01:00
/* Is the current buffer unshared? We can just move it.
*/
if( old_buffer &&
old_buffer->ref_count == 1 ) {
2010-03-05 15:43:49 +01:00
if( buffer_move( old_buffer, area ) ) {
vips_buffer_unref( old_buffer );
2009-08-16 17:00:08 +02:00
return( NULL );
}
2010-03-05 15:43:49 +01:00
return( old_buffer );
2009-08-16 17:00:08 +02:00
}
2010-03-05 15:43:49 +01:00
/* Fallback ... unref the old one, make a new one.
*/
VIPS_FREEF( vips_buffer_unref, old_buffer );
if( !(buffer = vips_buffer_new( im, area )) )
2010-03-05 15:43:49 +01:00
return( NULL );
2009-08-16 17:00:08 +02:00
return( buffer );
}
static void
2016-06-06 12:03:31 +02:00
buffer_thread_destroy_notify( VipsBufferThread *buffer_thread )
{
/* We only come here if vips_thread_shutdown() was not called for this
* thread. Do our best to clean up.
2014-08-09 11:25:34 +02:00
*
* GPrivate has stopped working by this point in destruction, be
* careful not to touch that.
2014-08-09 11:25:34 +02:00
*/
buffer_thread_free( buffer_thread );
}
/* Init the buffer cache system. This is called during vips_init.
2009-08-16 17:00:08 +02:00
*/
void
vips__buffer_init( void )
2009-08-16 17:00:08 +02:00
{
2012-10-23 14:57:38 +02:00
static GPrivate private =
2016-06-06 12:03:31 +02:00
G_PRIVATE_INIT( (GDestroyNotify) buffer_thread_destroy_notify );
2012-10-23 14:57:38 +02:00
buffer_thread_key = &private;
2013-12-16 10:22:05 +01:00
if( buffer_cache_max_reserve < 1 )
printf( "vips__buffer_init: buffer reserve disabled\n" );
#ifdef DEBUG
printf( "vips__buffer_init: DEBUG enabled\n" );
#endif /*DEBUG*/
#ifdef DEBUG_CREATE
printf( "vips__buffer_init: DEBUG_CREATE enabled\n" );
#endif /*DEBUG_CREATE*/
2009-08-16 17:00:08 +02:00
}
void
vips__buffer_shutdown( void )
{
VipsBufferThread *buffer_thread;
if( (buffer_thread = g_private_get( buffer_thread_key )) ) {
buffer_thread_free( buffer_thread );
g_private_set( buffer_thread_key, NULL );
}
}