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 - remove im_rightshift_size(), just a convenience function now
- vipsthumbnail no longer removes profiles by default - vipsthumbnail no longer removes profiles by default
- much more gentle sharpening in thumbnails - much more gentle sharpening in thumbnails
- added "minimise" signal, used by tilecache to drop
18/6/12 started 7.28.9 18/6/12 started 7.28.9
- slightly more memory debugging output - 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 blocking bugs
============= =============

View File

@ -116,16 +116,21 @@ tile_destroy( Tile *tile )
} }
static void static void
vips_tile_cache_dispose( GObject *gobject ) vips_tile_cache_drop_all( VipsTileCache *cache )
{ {
VipsTileCache *cache = (VipsTileCache *) gobject;
while( cache->tiles ) { while( cache->tiles ) {
Tile *tile = (Tile *) cache->tiles->data; Tile *tile = (Tile *) cache->tiles->data;
tile_destroy( tile ); 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 ); VIPS_FREEF( g_mutex_free, cache->lock );
G_OBJECT_CLASS( vips_tile_cache_parent_class )->dispose( gobject ); G_OBJECT_CLASS( vips_tile_cache_parent_class )->dispose( gobject );
@ -375,6 +380,14 @@ vips_tile_cache_gen( VipsRegion *or,
return( 0 ); 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 static int
vips_tile_cache_build( VipsObject *object ) vips_tile_cache_build( VipsObject *object )
{ {
@ -394,6 +407,9 @@ vips_tile_cache_build( VipsObject *object )
vips_demand_hint( conversion->out, vips_demand_hint( conversion->out,
VIPS_DEMAND_STYLE_SMALLTILE, cache->in, NULL ); 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, if( vips_image_generate( conversion->out,
vips_start_one, vips_tile_cache_gen, vips_stop_one, vips_start_one, vips_tile_cache_gen, vips_stop_one,
cache->in, cache ) ) cache->in, cache ) )

View File

@ -419,6 +419,16 @@ typedef struct _VipsImageClass {
*/ */
void (*invalidate)( VipsImage *image ); 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; } VipsImageClass;
GType vips_image_get_type( void ); 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_invalidate_all( VipsImage *image );
void vips_image_minimise_all( VipsImage *image );
void vips_image_preeval( VipsImage *image ); void vips_image_preeval( VipsImage *image );
void vips_image_eval( VipsImage *image, guint64 processed ); void vips_image_eval( VipsImage *image, guint64 processed );
void vips_image_posteval( VipsImage *image ); 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 ); int vips_image_open_output( VipsImage *image );
void vips__link_break_all( VipsImage *im ); 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 ); char *vips__b64_encode( const unsigned char *data, size_t data_length );
unsigned char *vips__b64_decode( const char *buffer, 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 ); g_assert( !image->downstream );
} }
typedef struct _LinkMap {
gboolean upstream;
int *serial;
VipsSListMap2Fn fn;
void *a;
void *b;
} LinkMap;
static void * static void *
vips__link_mapp( VipsImage *image, vips__link_mapp( VipsImage *image, LinkMap *map )
VipsSListMap2Fn fn, int *serial, void *a, void *b )
{ {
void *res; void *res;
/* Loop? /* Loop?
*/ */
if( image->serial == *serial ) if( image->serial == *map->serial )
return( NULL ); 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( res );
return( vips_slist_map4( image->downstream, return( vips_slist_map2( map->upstream ?
(VipsSListMap4Fn) vips__link_mapp, fn, serial, a, b ) ); image->upstream : image->downstream,
(VipsSListMap2Fn) vips__link_mapp, map, NULL ) );
} }
static void * static void *
@ -208,27 +216,37 @@ vips__link_map_cb( VipsImage *image, GSList **images )
return( NULL ); 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 * 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; static int serial = 0;
LinkMap map;
GSList *images; GSList *images;
GSList *p; GSList *p;
void *result; void *result;
serial += 1;
images = NULL;
/* The function might do anything, including removing images /* The function might do anything, including removing images
* or invalidating other images, so we can't trigger them from within * or invalidating other images, so we can't trigger them from within
* the image loop. Instead we collect a list of images, ref them, * the image loop. Instead we collect a list of images, ref them,
* run the functions, and unref. * run the functions, and unref.
*/ */
serial += 1; map.upstream = upstream;
images = NULL; map.serial = &serial;
vips__link_mapp( image, map.fn = (VipsSListMap2Fn) vips__link_map_cb;
(VipsSListMap2Fn) vips__link_map_cb, &serial, &images, NULL ); map.a = (void *) &images;
map.b = NULL;
vips__link_mapp( image, &map );
for( p = images; p; p = p->next ) for( p = images; p; p = p->next )
g_object_ref( p->data ); g_object_ref( p->data );

View File

@ -158,6 +158,7 @@ enum {
SIG_POSTEVAL, SIG_POSTEVAL,
SIG_WRITTEN, SIG_WRITTEN,
SIG_INVALIDATE, SIG_INVALIDATE,
SIG_MINIMISE,
SIG_LAST SIG_LAST
}; };
@ -793,6 +794,12 @@ vips_image_real_invalidate( VipsImage *image )
g_mutex_unlock( image->sslock ); 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 static void
vips_image_class_init( VipsImageClass *class ) vips_image_class_init( VipsImageClass *class )
{ {
@ -829,6 +836,8 @@ vips_image_class_init( VipsImageClass *class )
class->invalidate = vips_image_real_invalidate; class->invalidate = vips_image_real_invalidate;
class->minimise = vips_image_real_minimise;
/* Create properties. /* Create properties.
*/ */
@ -998,6 +1007,15 @@ vips_image_class_init( VipsImageClass *class )
NULL, NULL, NULL, NULL,
g_cclosure_marshal_VOID__VOID, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0 ); 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 static void
@ -1053,16 +1071,52 @@ vips_image_invalidate_all_cb( VipsImage *image )
* vips_image_invalidate_all: * vips_image_invalidate_all:
* @image: #VipsImage to invalidate * @image: #VipsImage to invalidate
* *
* Invalidate all pixel caches on an @image and any derived images. The * Invalidate all pixel caches on an @image and any downstream images, that
* "invalidate" callback is triggered for all invalidated images. * is, images which depend on this image.
*
* The "invalidate" callback is triggered for all invalidated images.
*/ */
void void
vips_image_invalidate_all( VipsImage *image ) 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 ); (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. /* Attach a new time struct, if necessary, and reset it.
*/ */
static int static int

View File

@ -913,6 +913,10 @@ vips_threadpool_run( VipsImage *im,
vips_threadpool_free( pool ); 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 ); return( result );
} }