experiment with removing shutdown from atexit (#2439)

remove shutdown from atexit

Because atexit() can be called at almost any point during process termination,
including after worker threads have been force-quit, we can't use it for
cleanup.

New policy: use vips_shutdown() explicitly if you need to clean up,
though it's optional. Only use atexit() for leak checking.
This commit is contained in:
John Cupitt 2021-09-17 19:02:53 +01:00 committed by GitHub
parent b221830b5a
commit af61d375bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 72 deletions

View File

@ -387,6 +387,10 @@ int vips__progress = 0;
*/
char *vips__disc_threshold = NULL;
/* Minimise needs a lock.
*/
static GMutex *vips__minimise_lock = NULL;
static guint vips_image_signals[SIG_LAST] = { 0 };
G_DEFINE_TYPE( VipsImage, vips_image, VIPS_TYPE_OBJECT );
@ -1322,8 +1326,7 @@ vips_image_class_init( VipsImageClass *class )
*
* The ::minimise signal is emitted when an image has been asked to
* minimise memory usage. All non-essential caches are dropped.
* See
* vips_image_minimise_all().
* See vips_image_minimise_all().
*/
vips_image_signals[SIG_MINIMISE] = g_signal_new( "minimise",
G_TYPE_FROM_CLASS( class ),
@ -1333,6 +1336,7 @@ vips_image_class_init( VipsImageClass *class )
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0 );
vips__minimise_lock = vips_g_mutex_new();
}
static void
@ -1446,8 +1450,15 @@ vips_image_minimise_all_cb( VipsImage *image, void *a, void *b )
void
vips_image_minimise_all( VipsImage *image )
{
/* Minimisation will modify things like sources, so we can't run it
* from many threads.
*/
g_mutex_lock( vips__minimise_lock );
(void) vips__link_map( image, TRUE,
(VipsSListMap2Fn) vips_image_minimise_all_cb, NULL, NULL );
g_mutex_unlock( vips__minimise_lock );
}
/**

View File

@ -230,9 +230,6 @@ vips_get_prgname( void )
* + loads any plugins from $libdir/vips-x.y/, where x and y are the
* major and minor version numbers for this VIPS.
*
* + if your platform supports atexit(), VIPS_INIT() will ask for
* vips_shutdown() to be called on program exit
*
* Example:
*
* |[
@ -367,6 +364,74 @@ vips_verbose( void )
}
}
static int
vips_leak( void )
{
char txt[1024];
VipsBuf buf = VIPS_BUF_STATIC( txt );
int n_leaks;
n_leaks = 0;
n_leaks += vips__object_leak();
n_leaks += vips__type_leak();
n_leaks += vips_tracked_get_allocs();
n_leaks += vips_tracked_get_mem();
n_leaks += vips_tracked_get_files();
if( vips_tracked_get_allocs() ||
vips_tracked_get_mem() ||
vips_tracked_get_files() ) {
vips_buf_appendf( &buf, "memory: %d allocations, %zd bytes\n",
vips_tracked_get_allocs(), vips_tracked_get_mem() );
vips_buf_appendf( &buf, "files: %d open\n",
vips_tracked_get_files() );
}
vips_buf_appendf( &buf, "memory: high-water mark " );
vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() );
vips_buf_appends( &buf, "\n" );
if( strlen( vips_error_buffer() ) > 0 ) {
vips_buf_appendf( &buf, "error buffer: %s",
vips_error_buffer() );
n_leaks += strlen( vips_error_buffer() );
}
fprintf( stderr, "%s", vips_buf_all( &buf ) );
n_leaks += vips__print_renders();
#ifdef DEBUG
vips_buffer_dump_all();
#endif /*DEBUG*/
return( n_leaks );
}
/* This is not guaranteed to be called, and might be called after many parts
* of libvips have been freed. Threads can be in an indeterminate state.
* You must be very careful to avoid segvs.
*/
static void
vips__atexit( void )
{
/* In dev releases, always show leaks. But not more than once, it's
* annoying.
*/
#ifndef DEBUG_LEAK
if( vips__leak )
#endif /*DEBUG_LEAK*/
{
static gboolean done = FALSE;
if( !done ) {
done = TRUE;
vips_leak();
}
}
}
/**
* vips_init:
* @argv0: name of application
@ -583,11 +648,8 @@ vips_init( const char *argv0 )
gsf_init();
#endif /*HAVE_GSF*/
/* Register vips_shutdown(). This may well not get called and many
* platforms don't support it anyway.
*/
#ifdef HAVE_ATEXIT
atexit( vips_shutdown );
atexit( vips__atexit );
#endif /*HAVE_ATEXIT*/
#ifdef DEBUG_LEAK
@ -635,51 +697,6 @@ vips_check_init( void )
vips_error_clear();
}
static int
vips_leak( void )
{
char txt[1024];
VipsBuf buf = VIPS_BUF_STATIC( txt );
int n_leaks;
n_leaks = 0;
n_leaks += vips__object_leak();
n_leaks += vips__type_leak();
n_leaks += vips_tracked_get_allocs();
n_leaks += vips_tracked_get_mem();
n_leaks += vips_tracked_get_files();
if( vips_tracked_get_allocs() ||
vips_tracked_get_mem() ||
vips_tracked_get_files() ) {
vips_buf_appendf( &buf, "memory: %d allocations, %zd bytes\n",
vips_tracked_get_allocs(), vips_tracked_get_mem() );
vips_buf_appendf( &buf, "files: %d open\n",
vips_tracked_get_files() );
}
vips_buf_appendf( &buf, "memory: high-water mark " );
vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() );
vips_buf_appends( &buf, "\n" );
if( strlen( vips_error_buffer() ) > 0 ) {
vips_buf_appendf( &buf, "error buffer: %s",
vips_error_buffer() );
n_leaks += strlen( vips_error_buffer() );
}
fprintf( stderr, "%s", vips_buf_all( &buf ) );
n_leaks += vips__print_renders();
#ifdef DEBUG
vips_buffer_dump_all();
#endif /*DEBUG*/
return( n_leaks );
}
/**
* vips_thread_shutdown:
*
@ -748,22 +765,6 @@ vips_shutdown( void )
gsf_shutdown();
#endif /*HAVE_GSF*/
/* In dev releases, always show leaks. But not more than once, it's
* annoying.
*/
#ifndef DEBUG_LEAK
if( vips__leak )
#endif /*DEBUG_LEAK*/
{
static gboolean done = FALSE;
if( !done &&
vips_leak() )
exit( 1 );
done = TRUE;
}
VIPS_FREE( vips__argv0 );
VIPS_FREE( vips__prgname );
VIPS_FREEF( vips_g_mutex_free, vips__global_lock );

View File

@ -1054,8 +1054,7 @@ vips__sink_screen_once( void *data )
vips_semaphore_init( &n_render_dirty_sem, 0, "n_render_dirty" );
/* Don't use vips__thread_execute() since this thread will only be
* ended by _shutdown, and that isn't always called early enough on
* windows from atexit().
* ended by vips_shutdown, and that isn't always called.
*/
render_thread = vips_g_thread_new( "sink_screen",
render_thread_main, NULL );