add "minimise" signal

on end of an image loop, send a "minimise" signal down the pipeline

tilecache listens for this signal on its output and drops the cache

helps reduce ruby memuse
This commit is contained in:
John Cupitt 2012-06-23 11:51:57 +01:00
parent 49fbfdd454
commit daac11b01c
8 changed files with 134 additions and 20 deletions

View File

@ -7,6 +7,7 @@
- remove im_rightshift_size(), just a convenience function now
- vipsthumbnail no longer removes profiles by default
- much more gentle sharpening in thumbnails
- added "minimise" signal, used by tilecache to drop
18/6/12 started 7.28.9
- slightly more memory debugging output

8
TODO
View File

@ -1,3 +1,11 @@
- tile cache doesn't need to copy if request fits within a tile
though doing a copy means that we can safely drop the cache without leaving
dangling pointers
- could jpeg load via memory buffer support minimise too?
blocking bugs
=============

View File

@ -116,16 +116,21 @@ tile_destroy( Tile *tile )
}
static void
vips_tile_cache_dispose( GObject *gobject )
vips_tile_cache_drop_all( VipsTileCache *cache )
{
VipsTileCache *cache = (VipsTileCache *) gobject;
while( cache->tiles ) {
Tile *tile = (Tile *) cache->tiles->data;
tile_destroy( tile );
}
}
static void
vips_tile_cache_dispose( GObject *gobject )
{
VipsTileCache *cache = (VipsTileCache *) gobject;
vips_tile_cache_drop_all( cache );
VIPS_FREEF( g_mutex_free, cache->lock );
G_OBJECT_CLASS( vips_tile_cache_parent_class )->dispose( gobject );
@ -375,6 +380,14 @@ vips_tile_cache_gen( VipsRegion *or,
return( 0 );
}
static void
vips_tile_cache_minimise( VipsImage *image, VipsTileCache *cache )
{
printf( "vips_tile_cache_minimise:\n" );
vips_tile_cache_drop_all( cache );
}
static int
vips_tile_cache_build( VipsObject *object )
{
@ -394,6 +407,9 @@ vips_tile_cache_build( VipsObject *object )
vips_demand_hint( conversion->out,
VIPS_DEMAND_STYLE_SMALLTILE, cache->in, NULL );
g_signal_connect( conversion->out, "minimise",
G_CALLBACK( vips_tile_cache_minimise ), cache );
if( vips_image_generate( conversion->out,
vips_start_one, vips_tile_cache_gen, vips_stop_one,
cache->in, cache ) )

View File

@ -419,6 +419,16 @@ typedef struct _VipsImageClass {
*/
void (*invalidate)( VipsImage *image );
/* Minimise this pipeline.
*
* This is triggered (sometimes) at the end of eval to signal that
* we're probably done and that operations involved should try to
* minimise memory use by, for example, dropping caches.
*
* See vips_tilecache().
*/
void (*minimise)( VipsImage *image );
} VipsImageClass;
GType vips_image_get_type( void );
@ -475,6 +485,8 @@ int vips_image_written( VipsImage *image );
void vips_image_invalidate_all( VipsImage *image );
void vips_image_minimise_all( VipsImage *image );
void vips_image_preeval( VipsImage *image );
void vips_image_eval( VipsImage *image, guint64 processed );
void vips_image_posteval( VipsImage *image );

View File

@ -93,7 +93,8 @@ int vips_image_open_input( VipsImage *image );
int vips_image_open_output( VipsImage *image );
void vips__link_break_all( VipsImage *im );
void *vips__link_map( VipsImage *im, VipsSListMap2Fn fn, void *a, void *b );
void *vips__link_map( VipsImage *image, gboolean upstream,
VipsSListMap2Fn fn, void *a, void *b );
char *vips__b64_encode( const unsigned char *data, size_t data_length );
unsigned char *vips__b64_decode( const char *buffer, size_t *data_length );

View File

@ -181,23 +181,31 @@ vips__link_break_all( VipsImage *image )
g_assert( !image->downstream );
}
typedef struct _LinkMap {
gboolean upstream;
int *serial;
VipsSListMap2Fn fn;
void *a;
void *b;
} LinkMap;
static void *
vips__link_mapp( VipsImage *image,
VipsSListMap2Fn fn, int *serial, void *a, void *b )
vips__link_mapp( VipsImage *image, LinkMap *map )
{
void *res;
/* Loop?
*/
if( image->serial == *serial )
if( image->serial == *map->serial )
return( NULL );
image->serial = *serial;
image->serial = *map->serial;
if( (res = fn( image, a, b )) )
if( (res = map->fn( image, map->a, map->b )) )
return( res );
return( vips_slist_map4( image->downstream,
(VipsSListMap4Fn) vips__link_mapp, fn, serial, a, b ) );
return( vips_slist_map2( map->upstream ?
image->upstream : image->downstream,
(VipsSListMap2Fn) vips__link_mapp, map, NULL ) );
}
static void *
@ -208,27 +216,37 @@ vips__link_map_cb( VipsImage *image, GSList **images )
return( NULL );
}
/* Apply a function to an image and all downstream images, direct and indirect.
/* Apply a function to an image and all upstream or downstream images,
* direct and indirect.
*/
void *
vips__link_map( VipsImage *image, VipsSListMap2Fn fn, void *a, void *b )
vips__link_map( VipsImage *image, gboolean upstream,
VipsSListMap2Fn fn, void *a, void *b )
{
static int serial = 0;
LinkMap map;
GSList *images;
GSList *p;
void *result;
serial += 1;
images = NULL;
/* The function might do anything, including removing images
* or invalidating other images, so we can't trigger them from within
* the image loop. Instead we collect a list of images, ref them,
* run the functions, and unref.
*/
serial += 1;
images = NULL;
vips__link_mapp( image,
(VipsSListMap2Fn) vips__link_map_cb, &serial, &images, NULL );
map.upstream = upstream;
map.serial = &serial;
map.fn = (VipsSListMap2Fn) vips__link_map_cb;
map.a = (void *) &images;
map.b = NULL;
vips__link_mapp( image, &map );
for( p = images; p; p = p->next )
g_object_ref( p->data );

View File

@ -158,6 +158,7 @@ enum {
SIG_POSTEVAL,
SIG_WRITTEN,
SIG_INVALIDATE,
SIG_MINIMISE,
SIG_LAST
};
@ -793,6 +794,12 @@ vips_image_real_invalidate( VipsImage *image )
g_mutex_unlock( image->sslock );
}
static void
vips_image_real_minimise( VipsImage *image )
{
VIPS_DEBUG_MSG( "vips_image_real_minimise: %p\n", image );
}
static void
vips_image_class_init( VipsImageClass *class )
{
@ -829,6 +836,8 @@ vips_image_class_init( VipsImageClass *class )
class->invalidate = vips_image_real_invalidate;
class->minimise = vips_image_real_minimise;
/* Create properties.
*/
@ -998,6 +1007,15 @@ vips_image_class_init( VipsImageClass *class )
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0 );
vips_image_signals[SIG_MINIMISE] = g_signal_new( "minimise",
G_TYPE_FROM_CLASS( class ),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET( VipsImageClass, minimise ),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0 );
}
static void
@ -1053,16 +1071,52 @@ vips_image_invalidate_all_cb( VipsImage *image )
* vips_image_invalidate_all:
* @image: #VipsImage to invalidate
*
* Invalidate all pixel caches on an @image and any derived images. The
* "invalidate" callback is triggered for all invalidated images.
* Invalidate all pixel caches on an @image and any downstream images, that
* is, images which depend on this image.
*
* The "invalidate" callback is triggered for all invalidated images.
*/
void
vips_image_invalidate_all( VipsImage *image )
{
(void) vips__link_map( image,
(void) vips__link_map( image, FALSE,
(VipsSListMap2Fn) vips_image_invalidate_all_cb, NULL, NULL );
}
void
vips_image_minimise( VipsImage *image )
{
VIPS_DEBUG_MSG( "vips_image_minimise: %p\n", image );
g_signal_emit( image, vips_image_signals[SIG_MINIMISE], 0 );
}
static void *
vips_image_minimise_all_cb( VipsImage *image )
{
vips_image_minimise( image );
return( NULL );
}
/**
* vips_image_minimise_all:
* @image: #VipsImage to minimise
*
* Minimise memory use on this image and any upstream images, that is, images
* which this image depends upon.
*
* The "minimise" callback is triggered for all minimised images.
*/
void
vips_image_minimise_all( VipsImage *image )
{
printf( "vips_image_minimise_all:\n" );
(void) vips__link_map( image, TRUE,
(VipsSListMap2Fn) vips_image_minimise_all_cb, NULL, NULL );
}
/* Attach a new time struct, if necessary, and reset it.
*/
static int

View File

@ -913,6 +913,10 @@ vips_threadpool_run( VipsImage *im,
vips_threadpool_free( pool );
/* Ask the pipeline to drop into low-mem-use mode now we're done.
*/
vips_image_minimise_all( im );
return( result );
}