nsgifload: avoid minimise after mapping (#3189)
* nsgifload: avoid minimise after mapping Not reliable on Windows. * nsgifload: prefer use of `VIPS_FREEF` macro * Improve `test_descriptors.c` * Only build `test_descriptors` when targeting Linux
This commit is contained in:
parent
1040766b04
commit
4611651d90
@ -1027,7 +1027,7 @@ vips_foreign_load_heif_load( VipsForeignLoad *load )
|
|||||||
if( vips_foreign_load_heif_set_header( heif, t[0] ) )
|
if( vips_foreign_load_heif_set_header( heif, t[0] ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* CLose input immediately at end of read.
|
/* Close input immediately at end of read.
|
||||||
*/
|
*/
|
||||||
g_signal_connect( t[0], "minimise",
|
g_signal_connect( t[0], "minimise",
|
||||||
G_CALLBACK( vips_foreign_load_heif_minimise ), heif );
|
G_CALLBACK( vips_foreign_load_heif_minimise ), heif );
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
* 6/10/18
|
* 6/10/18
|
||||||
* - from gifload.c
|
* - from gifload.c
|
||||||
* 3/3/22 tlsa
|
* 3/3/22 tlsa
|
||||||
* - update libnsgif API
|
* - update libnsgif API
|
||||||
*9/5/22
|
* 9/5/22
|
||||||
- attach GIF palette as metadata
|
* - attach GIF palette as metadata
|
||||||
|
* 26/11/22 kleisauke
|
||||||
|
* - avoid minimise after mapping -- not reliable on Win32
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -160,9 +162,7 @@ vips_foreign_load_nsgif_dispose( GObject *gobject )
|
|||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_dispose:\n" );
|
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_dispose:\n" );
|
||||||
|
|
||||||
if( gif->anim ) {
|
VIPS_FREEF( nsgif_destroy, gif->anim );
|
||||||
nsgif_destroy( gif->anim );
|
|
||||||
}
|
|
||||||
VIPS_UNREF( gif->source );
|
VIPS_UNREF( gif->source );
|
||||||
VIPS_FREE( gif->delay );
|
VIPS_FREE( gif->delay );
|
||||||
|
|
||||||
@ -336,8 +336,6 @@ vips_foreign_load_nsgif_set_header( VipsForeignLoadNsgif *gif,
|
|||||||
*
|
*
|
||||||
* Don't flag any errors unless we have to: we want to work for corrupt or
|
* Don't flag any errors unless we have to: we want to work for corrupt or
|
||||||
* malformed GIFs.
|
* malformed GIFs.
|
||||||
*
|
|
||||||
* Close as soon as we can to free up the fd.
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
vips_foreign_load_nsgif_header( VipsForeignLoad *load )
|
vips_foreign_load_nsgif_header( VipsForeignLoad *load )
|
||||||
@ -352,12 +350,10 @@ vips_foreign_load_nsgif_header( VipsForeignLoad *load )
|
|||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_header:\n" );
|
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_header:\n" );
|
||||||
|
|
||||||
/* We map in the image, then minimise to close any underlying file
|
/* Map the whole source into memory.
|
||||||
* object. This won't unmap.
|
|
||||||
*/
|
*/
|
||||||
if( !(data = vips_source_map( gif->source, &size )) )
|
if( !(data = vips_source_map( gif->source, &size )) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
vips_source_minimise( gif->source );
|
|
||||||
|
|
||||||
/* Treat errors from _scan() as warnings. If libnsgif really can't do
|
/* Treat errors from _scan() as warnings. If libnsgif really can't do
|
||||||
* something it'll fail gracefully later when we try to read out
|
* something it'll fail gracefully later when we try to read out
|
||||||
@ -542,7 +538,7 @@ vips_foreign_load_nsgif_load( VipsForeignLoad *load )
|
|||||||
{
|
{
|
||||||
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) load;
|
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) load;
|
||||||
VipsImage **t = (VipsImage **)
|
VipsImage **t = (VipsImage **)
|
||||||
vips_object_local_array( VIPS_OBJECT( load ), 4 );
|
vips_object_local_array( VIPS_OBJECT( load ), 2 );
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_load:\n" );
|
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_load:\n" );
|
||||||
|
|
||||||
|
@ -370,7 +370,7 @@ read_new( VipsImage *out, VipsSource *source, int page, int n, double scale )
|
|||||||
read->frame_no = 0;
|
read->frame_no = 0;
|
||||||
|
|
||||||
/* Everything has to stay open until read has finished, unfortunately,
|
/* Everything has to stay open until read has finished, unfortunately,
|
||||||
* since webp relies on us mapping the whole file.
|
* since webp relies on us mapping the whole source.
|
||||||
*/
|
*/
|
||||||
g_signal_connect( out, "close",
|
g_signal_connect( out, "close",
|
||||||
G_CALLBACK( read_close_cb ), read );
|
G_CALLBACK( read_close_cb ), read );
|
||||||
@ -379,6 +379,8 @@ read_new( VipsImage *out, VipsSource *source, int page, int n, double scale )
|
|||||||
read->config.options.use_threads = 1;
|
read->config.options.use_threads = 1;
|
||||||
read->config.output.is_external_memory = 1;
|
read->config.output.is_external_memory = 1;
|
||||||
|
|
||||||
|
/* Map the whole source into memory.
|
||||||
|
*/
|
||||||
if( !(read->data.bytes =
|
if( !(read->data.bytes =
|
||||||
vips_source_map( source, &read->data.size )) )
|
vips_source_map( source, &read->data.size )) )
|
||||||
return( NULL );
|
return( NULL );
|
||||||
@ -783,8 +785,10 @@ static int
|
|||||||
read_image( Read *read, VipsImage *out )
|
read_image( Read *read, VipsImage *out )
|
||||||
{
|
{
|
||||||
VipsImage **t = (VipsImage **)
|
VipsImage **t = (VipsImage **)
|
||||||
vips_object_local_array( VIPS_OBJECT( out ), 3 );
|
vips_object_local_array( VIPS_OBJECT( out ), 2 );
|
||||||
|
|
||||||
|
/* Make the output pipeline.
|
||||||
|
*/
|
||||||
t[0] = vips_image_new();
|
t[0] = vips_image_new();
|
||||||
if( read_header( read, t[0] ) )
|
if( read_header( read, t[0] ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
#endif /*G_OS_WIN32*/
|
#endif /*G_OS_WIN32*/
|
||||||
|
|
||||||
/* Does this fd support mmap. Pipes won't, for example.
|
/* Does this fd support mmap. Pipes won't, for example.
|
||||||
|
* FIXME unused internal function
|
||||||
*/
|
*/
|
||||||
gboolean
|
gboolean
|
||||||
vips__mmap_supported( int fd )
|
vips__mmap_supported( int fd )
|
||||||
|
@ -43,11 +43,6 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* TODO
|
|
||||||
*
|
|
||||||
* - can we map and then close the fd? how about on Windows?
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#define TEST_SANITY
|
#define TEST_SANITY
|
||||||
#define VIPS_DEBUG
|
#define VIPS_DEBUG
|
||||||
|
@ -75,8 +75,8 @@ endif
|
|||||||
# Keep the autotools convention for shared module suffix because GModule
|
# Keep the autotools convention for shared module suffix because GModule
|
||||||
# depends on it: https://gitlab.gnome.org/GNOME/glib/issues/1413
|
# depends on it: https://gitlab.gnome.org/GNOME/glib/issues/1413
|
||||||
module_suffix = []
|
module_suffix = []
|
||||||
if ['darwin', 'ios'].contains(host_os)
|
if host_os in ['darwin', 'ios']
|
||||||
module_suffix = 'so'
|
module_suffix = 'so'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if magick_module
|
if magick_module
|
||||||
|
@ -90,7 +90,7 @@ if modules_enabled
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# Detect and set symbol visibility
|
# Detect and set symbol visibility
|
||||||
if get_option('default_library') == 'shared' and (host_os == 'windows' or host_os == 'cygwin')
|
if get_option('default_library') == 'shared' and host_os in ['windows', 'cygwin']
|
||||||
cfg_var.set('DLL_EXPORT', true)
|
cfg_var.set('DLL_EXPORT', true)
|
||||||
if cc.get_id() == 'msvc' or cc.get_id() == 'clang-cl'
|
if cc.get_id() == 'msvc' or cc.get_id() == 'clang-cl'
|
||||||
cfg_var.set('_VIPS_PUBLIC', '__declspec(dllexport)')
|
cfg_var.set('_VIPS_PUBLIC', '__declspec(dllexport)')
|
||||||
|
@ -44,19 +44,22 @@ test('connections',
|
|||||||
workdir: meson.current_build_dir(),
|
workdir: meson.current_build_dir(),
|
||||||
)
|
)
|
||||||
|
|
||||||
test_descriptors = executable('test_descriptors',
|
# Uses /proc/self/fd, which is only available on *nix
|
||||||
'test_descriptors.c',
|
if host_os == 'linux'
|
||||||
dependencies: libvips_dep,
|
test_descriptors = executable('test_descriptors',
|
||||||
)
|
'test_descriptors.c',
|
||||||
|
dependencies: libvips_dep,
|
||||||
|
)
|
||||||
|
|
||||||
test_descriptors_sh = configure_file(
|
test_descriptors_sh = configure_file(
|
||||||
input: 'test_descriptors.sh',
|
input: 'test_descriptors.sh',
|
||||||
output: 'test_descriptors.sh',
|
output: 'test_descriptors.sh',
|
||||||
copy: true,
|
copy: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
test('descriptors',
|
test('descriptors',
|
||||||
test_descriptors_sh,
|
test_descriptors_sh,
|
||||||
depends: test_descriptors,
|
depends: test_descriptors,
|
||||||
workdir: meson.current_build_dir(),
|
workdir: meson.current_build_dir(),
|
||||||
)
|
)
|
||||||
|
endif
|
||||||
|
@ -1,31 +1,86 @@
|
|||||||
/* Read an image and check that file handles are being closed on minimise.
|
/* Read an image and check that file handles are being closed on minimise.
|
||||||
*
|
*
|
||||||
* This will only work on linux: we signal success and do nothing if /dev/proc
|
* This will only work on linux: we signal success and do nothing if
|
||||||
* does not exist.
|
* /proc/self/fd does not exist.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <vips/vips.h>
|
#include <vips/vips.h>
|
||||||
|
|
||||||
/* Count the number of files in a directory, -1 for directory not found etc.
|
/**
|
||||||
|
* get_open_files:
|
||||||
|
*
|
||||||
|
* Get a list of open files for this process.
|
||||||
|
*
|
||||||
|
* Returns (transfer full) (nullable): a new #GSList, or %NULL
|
||||||
*/
|
*/
|
||||||
static int
|
static GSList *
|
||||||
count_files( const char *dirname )
|
get_open_files()
|
||||||
{
|
{
|
||||||
|
GSList *list = NULL;
|
||||||
GDir *dir;
|
GDir *dir;
|
||||||
int n;
|
const char *name;
|
||||||
|
|
||||||
if( !(dir = g_dir_open( dirname, 0, NULL )) )
|
if( !(dir = g_dir_open( "/proc/self/fd", 0, NULL )) )
|
||||||
return( -1 );
|
return( NULL );
|
||||||
|
|
||||||
for( n = 0; g_dir_read_name( dir ); n++ )
|
while( (name = g_dir_read_name( dir )) ) {
|
||||||
;
|
char *fullname = g_build_filename( "/proc/self/fd", name, NULL );
|
||||||
|
|
||||||
|
list = g_slist_prepend( list, realpath( fullname, NULL ) );
|
||||||
|
|
||||||
|
g_free( fullname );
|
||||||
|
}
|
||||||
|
|
||||||
g_dir_close( dir );
|
g_dir_close( dir );
|
||||||
|
|
||||||
return( n );
|
return( list );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fd_check:
|
||||||
|
* @stage: the originating stage for the error message
|
||||||
|
* @fds: a #GSList of file descriptors to check against
|
||||||
|
*
|
||||||
|
* Check for a leak by comparing the currently open files for this
|
||||||
|
* process with the file descriptors in @fds. If there's a leak,
|
||||||
|
* print an error message and return %FALSE.
|
||||||
|
*
|
||||||
|
* See also: get_open_files().
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if there are no leaks; %FALSE otherwise
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
fd_check( const char *stage, GSList *fds )
|
||||||
|
{
|
||||||
|
GSList *unique_list = NULL, *list, *iter;
|
||||||
|
|
||||||
|
list = get_open_files();
|
||||||
|
|
||||||
|
for( iter = list; iter; iter = iter->next )
|
||||||
|
if( !g_slist_find_custom( fds, iter->data,
|
||||||
|
(GCompareFunc) g_strcmp0 ) )
|
||||||
|
unique_list = g_slist_prepend( unique_list, iter->data );
|
||||||
|
|
||||||
|
if( unique_list == NULL ) {
|
||||||
|
g_slist_free_full( list, g_free );
|
||||||
|
return( TRUE );
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf( stderr, "%s: file descriptors not closed after %s:\n",
|
||||||
|
vips_get_prgname(), stage );
|
||||||
|
for( iter = unique_list; iter; iter = iter->next )
|
||||||
|
fprintf( stderr, "%s\n", (char *) iter->data );
|
||||||
|
|
||||||
|
g_slist_free( unique_list );
|
||||||
|
g_slist_free_full( list, g_free );
|
||||||
|
|
||||||
|
return( FALSE );
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -33,39 +88,36 @@ main( int argc, char **argv )
|
|||||||
{
|
{
|
||||||
VipsSource *source;
|
VipsSource *source;
|
||||||
VipsImage *image, *x;
|
VipsImage *image, *x;
|
||||||
char fd_dir[256];
|
GSList *list;
|
||||||
int n_files;
|
|
||||||
double average;
|
double average;
|
||||||
|
|
||||||
if( VIPS_INIT( argv[0] ) )
|
if( VIPS_INIT( argv[0] ) )
|
||||||
vips_error_exit( "unable to start" );
|
vips_error_exit( "unable to start" );
|
||||||
|
|
||||||
if( argc != 2 )
|
if( argc != 2 )
|
||||||
vips_error_exit( "usage: %s test-image", argv[0] );
|
vips_error_exit( "usage: %s test-image", argv[0] );
|
||||||
|
|
||||||
vips_snprintf( fd_dir, 256, "/proc/%d/fd", getpid() );
|
list = get_open_files();
|
||||||
n_files = count_files( fd_dir );
|
if( list == NULL )
|
||||||
if( n_files == -1 )
|
|
||||||
/* Probably not linux, silent success.
|
/* Probably not linux, silent success.
|
||||||
*/
|
*/
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
/* This is usually 4. stdout / stdin / stderr plus one more made for
|
/* This is usually a list of 4 files. stdout / stdin / stderr plus one
|
||||||
* us by glib, I think, doing what I don't know.
|
* more made for us by glib, I think, doing what I don't know.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Opening an image should read the header, then close the fd.
|
/* Opening an image should read the header, then close the fd.
|
||||||
*/
|
*/
|
||||||
printf( "** rand open ..\n" );
|
printf( "** rand open ..\n" );
|
||||||
if( !(source = vips_source_new_from_file( argv[1] )) )
|
if( !(source = vips_source_new_from_file( argv[1] )) )
|
||||||
vips_error_exit( NULL );
|
goto error;
|
||||||
if( !(image = vips_image_new_from_source( source, "",
|
if( !(image = vips_image_new_from_source( source, "",
|
||||||
"access", VIPS_ACCESS_RANDOM,
|
"access", VIPS_ACCESS_RANDOM,
|
||||||
NULL )) )
|
NULL )) )
|
||||||
vips_error_exit( NULL );
|
goto error;
|
||||||
if( count_files( fd_dir ) != n_files )
|
if( !fd_check( "header read", list ) )
|
||||||
vips_error_exit( "%s: fd not closed after header read",
|
goto error;
|
||||||
argv[1] );
|
|
||||||
|
|
||||||
/* We should be able to read a chunk near the top, then have the fd
|
/* We should be able to read a chunk near the top, then have the fd
|
||||||
* closed again.
|
* closed again.
|
||||||
@ -73,11 +125,11 @@ main( int argc, char **argv )
|
|||||||
printf( "** crop1, avg ..\n" );
|
printf( "** crop1, avg ..\n" );
|
||||||
if( vips_crop( image, &x, 0, 0, image->Xsize, 10, NULL ) ||
|
if( vips_crop( image, &x, 0, 0, image->Xsize, 10, NULL ) ||
|
||||||
vips_avg( x, &average, NULL ) )
|
vips_avg( x, &average, NULL ) )
|
||||||
vips_error_exit( NULL );
|
goto error;
|
||||||
|
|
||||||
g_object_unref( x );
|
g_object_unref( x );
|
||||||
if( count_files( fd_dir ) != n_files )
|
if( !fd_check( "first read", list ) )
|
||||||
vips_error_exit( "%s: fd not closed after first read",
|
goto error;
|
||||||
argv[1] );
|
|
||||||
|
|
||||||
/* We should be able to read again, a little further down, and have
|
/* We should be able to read again, a little further down, and have
|
||||||
* the input restarted and closed again.
|
* the input restarted and closed again.
|
||||||
@ -85,11 +137,11 @@ main( int argc, char **argv )
|
|||||||
printf( "** crop2, avg ..\n" );
|
printf( "** crop2, avg ..\n" );
|
||||||
if( vips_crop( image, &x, 0, 20, image->Xsize, 10, NULL ) ||
|
if( vips_crop( image, &x, 0, 20, image->Xsize, 10, NULL ) ||
|
||||||
vips_avg( x, &average, NULL ) )
|
vips_avg( x, &average, NULL ) )
|
||||||
vips_error_exit( NULL );
|
goto error;
|
||||||
|
|
||||||
g_object_unref( x );
|
g_object_unref( x );
|
||||||
if( count_files( fd_dir ) != n_files )
|
if( !fd_check( "second read", list ) )
|
||||||
vips_error_exit( "%s: fd not closed after second read",
|
goto error;
|
||||||
argv[1] );
|
|
||||||
|
|
||||||
/* Clean up, and we should still just have three open.
|
/* Clean up, and we should still just have three open.
|
||||||
*/
|
*/
|
||||||
@ -99,9 +151,13 @@ main( int argc, char **argv )
|
|||||||
printf( "** shutdown ..\n" );
|
printf( "** shutdown ..\n" );
|
||||||
vips_shutdown();
|
vips_shutdown();
|
||||||
|
|
||||||
if( count_files( fd_dir ) != n_files )
|
if( !fd_check( "shutdown", list ) )
|
||||||
vips_error_exit( "%s: fd not closed after shutdown",
|
goto error;
|
||||||
argv[1] );
|
|
||||||
|
|
||||||
|
g_slist_free_full( list, g_free );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
|
error:
|
||||||
|
g_slist_free_full( list, g_free );
|
||||||
|
return( 1 );
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# test the various restartable loaders
|
# test the various restartable loaders
|
||||||
|
|
||||||
# webp and ppm use streams, but they mmap the input, so you can't close() the
|
# gif, webp and ppm use streams, but they mmap the input, so you can't close() the
|
||||||
# fd on minimise
|
# fd on minimise
|
||||||
|
|
||||||
# set -x
|
# set -x
|
||||||
@ -29,4 +29,3 @@ fi
|
|||||||
if test_supported svgload_source; then
|
if test_supported svgload_source; then
|
||||||
./test_descriptors $test_images/logo.svg
|
./test_descriptors $test_images/logo.svg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user