diff --git a/ChangeLog b/ChangeLog index f4593823..eec4aa90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/TODO b/TODO index e4e24aca..d9d43420 100644 --- a/TODO +++ b/TODO @@ -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 ============= diff --git a/libvips/conversion/tilecache.c b/libvips/conversion/tilecache.c index b9a0b87c..73e887a5 100644 --- a/libvips/conversion/tilecache.c +++ b/libvips/conversion/tilecache.c @@ -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 ) ) diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index e93d3e3c..fce5e34f 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -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 ); diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index 112dc1ce..118b88b9 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -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 ); diff --git a/libvips/iofuncs/generate.c b/libvips/iofuncs/generate.c index 2a3e1d9e..42f0c9bd 100644 --- a/libvips/iofuncs/generate.c +++ b/libvips/iofuncs/generate.c @@ -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 ); diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 40096468..5858c4bc 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -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 diff --git a/libvips/iofuncs/threadpool.c b/libvips/iofuncs/threadpool.c index 37a3ef30..d5f9098b 100644 --- a/libvips/iofuncs/threadpool.c +++ b/libvips/iofuncs/threadpool.c @@ -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 ); }