rework gthread usage

glib-2.32 deprecated g_mutex_new() ... use a compat function to hide
this change

also remove the --without-threads configure flag, gthread is now
compulsory, so there was no longer any point
This commit is contained in:
John Cupitt 2012-10-22 22:08:53 +01:00
parent ff6c4f63d3
commit fad78f8555
21 changed files with 76 additions and 169 deletions

View File

@ -4,6 +4,8 @@
- openslide2vips gets underlying tile size from openslide
- embed has 'background' option
- dzsave --layout google has a @background option
- use vips_mutex_new()/_free() over g_mutex_new()/_free()
- remove no threads option
2/10/12 started 7.30.4
- remove options from format string in .dzi (thanks Martin)

View File

@ -318,16 +318,10 @@ AC_CHECK_LIB(m,hypot,[AC_DEFINE(HAVE_HYPOT,1,[have hypot() in libm.])])
PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.6 gmodule-2.0 >= 2.4 libxml-2.0 gobject-2.0)
PACKAGES_USED="$PACKAGES_USED glib-2.0 gmodule-2.0 libxml-2.0 gobject-2.0"
# option to eval without threads
AC_ARG_ENABLE(threads,
AS_HELP_STRING([--enable-threads], [evaluate with threads (default: yes)]))
if test x"$enable_threads" != "xno"; then
AC_DEFINE(HAVE_THREADS,1,[threaded evaluation])
PKG_CHECK_MODULES(GTHREAD, gthread-2.0)
PACKAGES_USED="$PACKAGES_USED gthread-2.0"
enable_threads=yes
fi
# after 2.32 g_mutex_new() becomes g_mutex_init(), annoyingly
PKG_CHECK_MODULES(THREADS, glib-2.0 >= 2.32,[
AC_DEFINE(HAVE_MUTEX_INIT,1,[define if your glib has g_mutex_init().])
])
# check for gtk-doc
GTK_DOC_CHECK(1.9)
@ -734,7 +728,6 @@ native win32: $vips_os_win32
native OS X: $vips_os_darwin
open files in binary mode: $vips_binary_open
enable debug: $enable_debug
evaluate with threads: $enable_threads
build C++ components: $enable_cxx
build docs with gtkdoc: $enable_gtk_doc
gobject introspection: $found_introspection

View File

@ -147,7 +147,7 @@ icc_destroy( Icc *icc )
IM_FREEF( cmsDeleteTransform, icc->trans );
IM_FREEF( cmsCloseProfile, icc->in_profile );
IM_FREEF( cmsCloseProfile, icc->out_profile );
IM_FREEF( g_mutex_free, icc->lock );
IM_FREEF( vips_mutex_free, icc->lock );
return( 0 );
}
@ -173,7 +173,7 @@ icc_new( IMAGE *in, IMAGE *out, VipsIntent intent )
icc->in_profile = 0;
icc->out_profile = 0;
icc->trans = 0;
icc->lock = g_mutex_new();
icc->lock = vips_mutex_new();
if( im_add_close_callback( out,
(im_callback_fn) icc_destroy, icc, NULL ) )
@ -945,7 +945,7 @@ icc_destroy( Icc *icc )
IM_FREEF( cmsDeleteTransform, icc->trans );
IM_FREEF( cmsCloseProfile, icc->in_profile );
IM_FREEF( cmsCloseProfile, icc->out_profile );
IM_FREEF( g_mutex_free, icc->lock );
IM_FREEF( vips_mutex_free, icc->lock );
return( 0 );
}
@ -970,7 +970,7 @@ icc_new( IMAGE *in, IMAGE *out, VipsIntent intent )
icc->in_profile = 0;
icc->out_profile = 0;
icc->trans = 0;
icc->lock = g_mutex_new();
icc->lock = vips_mutex_new();
if( im_add_close_callback( out,
(im_callback_fn) icc_destroy, icc, NULL ) )

View File

@ -100,7 +100,7 @@ vips_sequential_dispose( GObject *gobject )
{
VipsSequential *sequential = (VipsSequential *) gobject;
VIPS_FREEF( g_mutex_free, sequential->lock );
VIPS_FREEF( vips_mutex_free, sequential->lock );
VIPS_FREEF( g_cond_free, sequential->ready );
G_OBJECT_CLASS( vips_sequential_parent_class )->dispose( gobject );
@ -293,7 +293,7 @@ static void
vips_sequential_init( VipsSequential *sequential )
{
sequential->trace = FALSE;
sequential->lock = g_mutex_new();
sequential->lock = vips_mutex_new();
sequential->ready = g_cond_new();
sequential->tile_height = 1;
sequential->error = 0;

View File

@ -144,7 +144,7 @@ vips_block_cache_dispose( GObject *gobject )
VipsBlockCache *cache = (VipsBlockCache *) gobject;
vips_block_cache_drop_all( cache );
VIPS_FREEF( g_mutex_free, cache->lock );
VIPS_FREEF( vips_mutex_free, cache->lock );
VIPS_FREEF( g_cond_free, cache->new_tile );
G_OBJECT_CLASS( vips_block_cache_parent_class )->dispose( gobject );
@ -489,7 +489,7 @@ vips_block_cache_init( VipsBlockCache *cache )
cache->time = 0;
cache->ntiles = 0;
cache->lock = g_mutex_new();
cache->lock = vips_mutex_new();
cache->new_tile = g_cond_new();
cache->tiles = g_hash_table_new_full(
(GHashFunc) vips_rect_hash,

View File

@ -105,7 +105,7 @@ tile_destroy( Tile *tile )
static void
read_destroy( Read *read )
{
IM_FREEF( g_mutex_free, read->lock );
IM_FREEF( vips_mutex_free, read->lock );
while( read->cache ) {
Tile *tile = (Tile *) read->cache->data;
@ -131,7 +131,7 @@ read_new( IMAGE *in, IMAGE *out,
read->max_tiles = max_tiles;
read->time = 0;
read->ntiles = 0;
read->lock = g_mutex_new();
read->lock = vips_mutex_new();
read->cache = NULL;
if( im_add_close_callback( out,

View File

@ -133,7 +133,7 @@ static void
vips_fits_close( VipsFits *fits )
{
VIPS_FREE( fits->filename );
VIPS_FREEF( g_mutex_free, fits->lock );
VIPS_FREEF( vips_mutex_free, fits->lock );
if( fits->fptr ) {
int status;
@ -180,7 +180,7 @@ vips_fits_new_read( const char *filename, VipsImage *out, int band_select )
return( NULL );
}
fits->lock = g_mutex_new();
fits->lock = vips_mutex_new();
return( fits );
}
@ -578,7 +578,7 @@ vips_fits_new_write( VipsImage *in, const char *filename )
return( NULL );
}
fits->lock = g_mutex_new();
fits->lock = vips_mutex_new();
return( fits );
}

View File

@ -131,7 +131,7 @@ read_destroy( VipsImage *im, Read *read )
VIPS_FREEF( DestroyImageInfo, read->image_info );
VIPS_FREE( read->frames );
DestroyExceptionInfo( &read->exception );
VIPS_FREEF( g_mutex_free, read->lock );
VIPS_FREEF( vips_mutex_free, read->lock );
return( 0 );
}
@ -161,7 +161,7 @@ read_new( const char *filename, VipsImage *im )
read->n_frames = 0;
read->frames = NULL;
read->frame_height = 0;
read->lock = g_mutex_new();
read->lock = vips_mutex_new();
g_signal_connect( im, "close", G_CALLBACK( read_destroy ), read );

View File

@ -1075,7 +1075,7 @@ write_tif_tilewise( TiffWrite *tw )
return( -1 );
g_assert( !tw->write_lock );
tw->write_lock = g_mutex_new();
tw->write_lock = vips_mutex_new();
/* Write pyramid too? Only bother if bigger than tile size.
*/
@ -1179,7 +1179,7 @@ free_tiff_write( TiffWrite *tw )
VIPS_FREEF( TIFFClose, tw->tif );
VIPS_FREEF( vips_free, tw->tbuf );
VIPS_FREEF( g_mutex_free, tw->write_lock );
VIPS_FREEF( vips_mutex_free, tw->write_lock );
VIPS_FREEF( free_pyramid, tw->layer );
VIPS_FREEF( vips_free, tw->icc_profile );
}

View File

@ -43,40 +43,11 @@ extern "C" {
*/
#define VIPS__DEFAULT_STACK_SIZE (2 * 1024 * 1024)
#ifndef HAVE_THREADS
#undef g_thread_supported
#define g_thread_supported() (0)
#define g_thread_init vips__g_thread_init
#define g_thread_join vips__g_thread_join
#define g_thread_self vips__g_thread_self
#define g_thread_create_full vips__g_thread_create_full
/* We don't need a shadow imlementation of g_thread_create(), even though we
* use it, because it's just a macro over g_thread_create_full().
/* We need wrappers over g_mutex_new(), it was replaced by g_mutex_init() in
* glib 2.32+
*/
void vips__g_thread_init( GThreadFunctions *vtable );
gpointer vips__g_thread_join( GThread * );
gpointer vips__g_thread_self( void );
GThread *vips__g_thread_create_full( GThreadFunc,
gpointer, gulong, gboolean, gboolean, GThreadPriority, GError ** );
#undef g_mutex_new
#undef g_mutex_free
#undef g_mutex_lock
#undef g_mutex_unlock
#define g_mutex_new vips__g_mutex_new
#define g_mutex_free vips__g_mutex_free
#define g_mutex_lock vips__g_mutex_lock
#define g_mutex_unlock vips__g_mutex_unlock
GMutex *vips__g_mutex_new( void );
void vips__g_mutex_free( GMutex * );
void vips__g_mutex_lock( GMutex * );
void vips__g_mutex_unlock( GMutex * );
#endif /*!HAVE_THREADS*/
GMutex *vips_mutex_new( void );
void vips_mutex_free( GMutex * );
#ifdef __cplusplus
}

View File

@ -72,15 +72,8 @@ static GSList *vips__buffers_all = NULL;
static int buffer_cache_n = 0;
#endif /*DEBUG_CREATE*/
#ifdef HAVE_THREADS
static GPrivate *thread_buffer_cache_key = NULL;
#else /*!HAVE_THREADS*/
static VipsBufferCache *thread_buffer_cache = NULL;
#endif /*HAVE_THREADS*/
/* Only need this if we're threading and need to do a lot of start/stop.
*/
#ifdef HAVE_THREADS
static void
buffer_cache_free( VipsBufferCache *cache )
{
@ -95,7 +88,6 @@ buffer_cache_free( VipsBufferCache *cache )
VIPS_FREEF( g_hash_table_destroy, cache->hash );
VIPS_FREE( cache );
}
#endif /*HAVE_THREADS*/
static void
buffer_cache_list_free( VipsBufferCacheList *cache_list )
@ -163,16 +155,10 @@ buffer_cache_get( void )
{
VipsBufferCache *cache;
#ifdef HAVE_THREADS
if( !(cache = g_private_get( thread_buffer_cache_key )) ) {
cache = buffer_cache_new();
g_private_set( thread_buffer_cache_key, cache );
}
#else /*!HAVE_THREADS*/
if( !thread_buffer_cache )
thread_buffer_cache = buffer_cache_new();
cache = thread_buffer_cache;
#endif /*HAVE_THREADS*/
return( cache );
}
@ -474,9 +460,7 @@ vips_buffer_print( VipsBuffer *buffer )
void
vips__buffer_init( void )
{
#ifdef HAVE_THREADS
if( !thread_buffer_cache_key )
thread_buffer_cache_key = g_private_new(
(GDestroyNotify) buffer_cache_free );
#endif /*HAVE_THREADS*/
}

View File

@ -416,7 +416,7 @@ void
vips__cache_init( void )
{
if( !vips_cache_table ) {
vips_cache_lock = g_mutex_new();
vips_cache_lock = vips_mutex_new();
vips_cache_table = g_hash_table_new(
(GHashFunc) vips_operation_hash,

View File

@ -241,7 +241,7 @@ vips_image_finalize( GObject *gobject )
*/
vips_image_delete( image );
VIPS_FREEF( g_mutex_free, image->sslock );
VIPS_FREEF( vips_mutex_free, image->sslock );
VIPS_FREE( image->Hist );
VIPS_FREEF( vips__gslist_gvalue_free, image->history_list );
@ -1057,7 +1057,7 @@ vips_image_init( VipsImage *image )
image->Yres = 1.0;
image->fd = -1; /* since 0 is stdout */
image->sslock = g_mutex_new();
image->sslock = vips_mutex_new();
image->sizeof_header = VIPS_SIZEOF_HEADER;

View File

@ -201,7 +201,7 @@ vips_init( const char *argv0 )
#endif /*G_THREADS_ENABLED*/
if( !vips__global_lock )
vips__global_lock = g_mutex_new();
vips__global_lock = vips_mutex_new();
VIPS_SETSTR( vips__argv0, argv0 );

View File

@ -240,21 +240,13 @@ vips_tracked_free( void *s )
g_free( s );
}
/* g_mutex_new() is a macro.
*/
static void *
vips_tracked_mutex_new( void *data )
{
return( g_mutex_new() );
}
static void
vips_tracked_init( void )
{
static GOnce vips_tracked_once = G_ONCE_INIT;
vips_tracked_mutex = g_once( &vips_tracked_once,
vips_tracked_mutex_new, NULL );
(GThreadFunc) vips_mutex_new, NULL );
}
/**

View File

@ -1271,7 +1271,7 @@ vips_object_class_init( VipsObjectClass *class )
if( !vips__object_all ) {
vips__object_all = g_hash_table_new(
g_direct_hash, g_direct_equal );
vips__object_all_lock = g_mutex_new();
vips__object_all_lock = vips_mutex_new();
}
gobject_class->dispose = vips_object_dispose;

View File

@ -58,19 +58,15 @@ vips_semaphore_init( VipsSemaphore *s, int v, char *name )
{
s->v = v;
s->name = name;
#ifdef HAVE_THREADS
s->mutex = g_mutex_new();
s->mutex = vips_mutex_new();
s->cond = g_cond_new();
#endif /*HAVE_THREADS*/
}
void
vips_semaphore_destroy( VipsSemaphore *s )
{
#ifdef HAVE_THREADS
VIPS_FREEF( g_mutex_free, s->mutex );
VIPS_FREEF( vips_mutex_free, s->mutex );
VIPS_FREEF( g_cond_free, s->cond );
#endif /*HAVE_THREADS*/
}
/* Add n to the semaphore and signal any threads that are blocked waiting
@ -81,12 +77,10 @@ vips_semaphore_upn( VipsSemaphore *s, int n )
{
int value_after_op;
#ifdef HAVE_THREADS
g_mutex_lock( s->mutex );
#endif /*HAVE_THREADS*/
s->v += n;
value_after_op = s->v;
#ifdef HAVE_THREADS
/* If we are only incrementing by one, we only need to wake a single
* thread. If we are incrementing by a lot, we must wake all threads.
*/
@ -95,7 +89,6 @@ vips_semaphore_upn( VipsSemaphore *s, int n )
else
g_cond_broadcast( s->cond );
g_mutex_unlock( s->mutex );
#endif /*HAVE_THREADS*/
#ifdef DEBUG_IO
printf( "vips_semaphore_upn(\"%s\",%d) = %d\n",
@ -122,16 +115,14 @@ vips_semaphore_downn( VipsSemaphore *s, int n )
{
int value_after_op;
#ifdef HAVE_THREADS
g_mutex_lock( s->mutex );
while( s->v < n )
g_cond_wait( s->cond, s->mutex );
#endif /*HAVE_THREADS*/
s->v -= n;
value_after_op = s->v;
#ifdef HAVE_THREADS
g_mutex_unlock( s->mutex );
#endif /*HAVE_THREADS*/
#ifdef DEBUG_IO
printf( "vips_semaphore_downn(\"%s\",%d): %d\n",

View File

@ -171,7 +171,6 @@ wbuffer_write( WriteBuffer *wbuffer )
&wbuffer->area, write->a );
}
#ifdef HAVE_THREADS
/* Run this as a thread to do a BG write.
*/
static void *
@ -200,7 +199,6 @@ wbuffer_write_thread( void *data )
return( NULL );
}
#endif /*HAVE_THREADS*/
static WriteBuffer *
wbuffer_new( Write *write )
@ -227,7 +225,6 @@ wbuffer_new( Write *write )
*/
vips__region_no_ownership( wbuffer->region );
#ifdef HAVE_THREADS
/* Make this last (picks up parts of wbuffer on startup).
*/
if( !(wbuffer->thread = g_thread_create( wbuffer_write_thread, wbuffer,
@ -237,7 +234,6 @@ wbuffer_new( Write *write )
wbuffer_free( wbuffer );
return( NULL );
}
#endif /*HAVE_THREADS*/
return( wbuffer );
}
@ -266,13 +262,7 @@ wbuffer_flush( Write *write )
/* Set the background writer going for this buffer.
*/
#ifdef HAVE_THREADS
vips_semaphore_up( &write->buf->go );
#else
/* No threads? Write ourselves synchronously.
*/
wbuffer_write( write->buf );
#endif /*HAVE_THREADS*/
return( 0 );
}

View File

@ -66,14 +66,6 @@
#include <vips/thread.h>
#include <vips/debug.h>
/* A have-threads we can test in if().
*/
#ifdef HAVE_THREADS
static const int have_threads = 1;
#else /*!HAVE_THREADS*/
static const int have_threads = 0;
#endif /*HAVE_THREADS*/
#ifdef VIPS_DEBUG_AMBER
static int render_num_renders = 0;
#endif /*VIPS_DEBUG_AMBER*/
@ -228,8 +220,8 @@ render_free( Render *render )
}
g_mutex_unlock( render_dirty_lock );
g_mutex_free( render->ref_count_lock );
g_mutex_free( render->lock );
vips_mutex_free( render->ref_count_lock );
vips_mutex_free( render->lock );
vips_slist_map2( render->all, (VipsSListMap2Fn) tile_free, NULL, NULL );
VIPS_FREEF( g_slist_free, render->all );
@ -520,15 +512,12 @@ render_thread_main( void *client )
static int
render_thread_create( void )
{
if( !have_threads )
return( 0 );
if( !render_dirty_lock ) {
render_dirty_lock = g_mutex_new();
render_dirty_lock = vips_mutex_new();
vips_semaphore_init( &render_dirty_sem, 0, "render_dirty_sem" );
}
if( !render_thread && have_threads ) {
if( !render_thread ) {
if( !(render_thread = g_thread_create_full(
render_thread_main, NULL,
VIPS__DEFAULT_STACK_SIZE, TRUE, FALSE,
@ -595,7 +584,7 @@ render_new( VipsImage *in, VipsImage *out, VipsImage *mask,
return( NULL );
render->ref_count = 1;
render->ref_count_lock = g_mutex_new();
render->ref_count_lock = vips_mutex_new();
render->in = in;
render->out = out;
@ -607,7 +596,7 @@ render_new( VipsImage *in, VipsImage *out, VipsImage *mask,
render->notify = notify;
render->a = a;
render->lock = g_mutex_new();
render->lock = vips_mutex_new();
render->all = NULL;
render->ntiles = 0;
@ -746,7 +735,7 @@ tile_queue( Tile *tile, VipsRegion *reg )
tile->painted = FALSE;
tile_touch( tile );
if( render->notify && have_threads ) {
if( render->notify ) {
/* Add to the list of renders with dirty tiles. The bg
* thread will pick it up and paint it. It can be already on
* the dirty list.
@ -755,7 +744,7 @@ tile_queue( Tile *tile, VipsRegion *reg )
render_dirty_put( render );
}
else {
/* No threads, or no notify ... paint the tile ourselves
/* no notify ... paint the tile ourselves
* sychronously. No need to notify the client since they'll
* never see black tiles.
*/
@ -968,7 +957,6 @@ image_stop( void *seq, void *a, void *b )
static int
mask_fill( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop )
{
#ifdef HAVE_THREADS
Render *render = (Render *) a;
VipsRect *r = &out->valid;
int x, y;
@ -1006,9 +994,6 @@ mask_fill( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop )
}
g_mutex_unlock( render->lock );
#else /*!HAVE_THREADS*/
vips_region_paint( out, &out->valid, 255 );
#endif /*HAVE_THREADS*/
return( 0 );
}

View File

@ -101,25 +101,34 @@ int vips__thinstrip_height = VIPS__THINSTRIP_HEIGHT;
*/
int vips__concurrency = 0;
#ifndef HAVE_THREADS
/* If we're building without gthread, we need stubs for the g_thread_*() and
* g_mutex_*() functions. <vips/thread.h> has #defines which point the g_
* names here.
/* Glib 2.32 revised the thread API. We need some compat functions.
*/
void vips__g_thread_init( GThreadFunctions *vtable ) {}
gpointer vips__g_thread_join( GThread *dummy ) { return( NULL ); }
gpointer vips__g_thread_self( void ) { return( NULL ); }
GThread *vips__g_thread_create_full( GThreadFunc d1,
gpointer d2, gulong d3, gboolean d4, gboolean d5, GThreadPriority d6,
GError **d7 )
{ return( NULL ); }
GMutex *
vips_mutex_new( void )
{
GMutex *mutex;
GMutex *vips__g_mutex_new( void ) { return( NULL ); }
void vips__g_mutex_free( GMutex *d ) {}
void vips__g_mutex_lock( GMutex *d ) {}
void vips__g_mutex_unlock( GMutex *d ) {}
#endif /*!HAVE_THREADS*/
#ifdef HAVE_MUTEX_INIT
mutex = g_new( GMutex, 1 );
g_mutex_init( mutex );
#else
mutex = g_mutex_new();
#endif
return( mutex );
}
void
vips_mutex_free( GMutex *mutex )
{
#ifdef HAVE_MUTEX_INIT
g_mutex_clear( mutex );
g_free( mutex );
#else
g_mutex_free( mutex );
#endif
}
/**
* vips_concurrency_set:
@ -564,7 +573,6 @@ vips_thread_work_unit( VipsThread *thr )
}
}
#ifdef HAVE_THREADS
/* What runs as a thread ... loop, waiting to be told to do stuff.
*/
static void *
@ -592,7 +600,6 @@ vips_thread_main_loop( void *a )
return( NULL );
}
#endif /*HAVE_THREADS*/
/* Attach another thread to a threadpool.
*/
@ -628,7 +635,6 @@ vips_thread_new( VipsThreadpool *pool )
}
#endif /*TIME_THREAD*/
#ifdef HAVE_THREADS
/* Make a worker thread. We have to use g_thread_create_full() because
* we need to insist on a non-tiny stack. Some platforms default to
* very small values (eg. various BSDs).
@ -643,7 +649,6 @@ vips_thread_new( VipsThreadpool *pool )
}
VIPS_DEBUG_MSG_RED( "vips_thread_new: g_thread_create_full()\n" );
#endif /*HAVE_THREADS*/
return( thr );
}
@ -674,7 +679,7 @@ vips_threadpool_free( VipsThreadpool *pool )
pool->im->filename, pool );
vips_threadpool_kill_threads( pool );
VIPS_FREEF( g_mutex_free, pool->allocate_lock );
VIPS_FREEF( vips_mutex_free, pool->allocate_lock );
vips_semaphore_destroy( &pool->finish );
vips_semaphore_destroy( &pool->tick );
@ -699,7 +704,7 @@ vips_threadpool_new( VipsImage *im )
pool->im = im;
pool->allocate = NULL;
pool->work = NULL;
pool->allocate_lock = g_mutex_new();
pool->allocate_lock = vips_mutex_new();
pool->nthr = vips_concurrency_get();
pool->thr = NULL;
vips_semaphore_init( &pool->finish, 0, "finish" );
@ -879,15 +884,9 @@ vips_threadpool_run( VipsImage *im,
}
for(;;) {
#ifdef HAVE_THREADS
/* Wait for a tick from a worker.
*/
vips_semaphore_down( &pool->tick );
#else
/* No threads, do the work ourselves in the main thread.
*/
vips_thread_work_unit( pool->thr[0] );
#endif /*HAVE_THREADS*/
VIPS_DEBUG_MSG( "vips_threadpool_run: tick\n" );

View File

@ -680,7 +680,7 @@ lr_blend_labpack( REGION *or, MergeInfo *inf, Overlapping *ovlap, Rect *oreg )
static void *
lock_free( GMutex *lock )
{
g_mutex_free( lock );
vips_mutex_free( lock );
return( NULL );
}
@ -767,10 +767,10 @@ im__build_mergestate( const char *domain,
for( x = 0; x < ovlap->flsize; x++ )
ovlap->first[x] = -1;
ovlap->fl_lock = g_mutex_new();
ovlap->fl_lock = vips_mutex_new();
if( im_add_close_callback( out,
(im_callback_fn) lock_free, ovlap->fl_lock, NULL ) ) {
g_mutex_free( ovlap->fl_lock );
vips_mutex_free( ovlap->fl_lock );
return( NULL );
}