fix a thread leak in sink_screen
we were not joining the bg render thread when it exited instead, keep the thread around with a semaphore for it to wait on
This commit is contained in:
parent
3fde3cf4ba
commit
c9b87ce4f5
@ -2517,30 +2517,39 @@ vips_image_write_gen( VipsRegion *or,
|
||||
int
|
||||
vips_image_write( VipsImage *image, VipsImage *out )
|
||||
{
|
||||
/* image needs to stay alive for this call. It can be unreffed during
|
||||
* the generate.
|
||||
*/
|
||||
g_object_ref( image );
|
||||
|
||||
if( vips_image_pio_input( image ) ||
|
||||
vips_image_pipelinev( out,
|
||||
VIPS_DEMAND_STYLE_THINSTRIP, image, NULL ) )
|
||||
VIPS_DEMAND_STYLE_THINSTRIP, image, NULL ) ) {
|
||||
g_object_unref( image );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( vips_image_generate( out,
|
||||
vips_start_one, vips_image_write_gen, vips_stop_one,
|
||||
image, NULL ) )
|
||||
image, NULL ) ) {
|
||||
g_object_unref( image );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* If @out is a partial image, we need to make sure that @image stays
|
||||
* alive as long as @out is alive.
|
||||
/* If @out is a partial image, we need to unref @image when out is
|
||||
* unreffed.
|
||||
*
|
||||
* If it's not partial, perhaps a file we write to or a memory image,
|
||||
* we need to break any links between @image and @out created by
|
||||
* vips_image_pipelinev().
|
||||
*/
|
||||
if( vips_image_ispartial( out ) ) {
|
||||
g_object_ref( image );
|
||||
vips_object_local( out, image );
|
||||
}
|
||||
else {
|
||||
vips__reorder_clear( image );
|
||||
vips__link_break_all( out );
|
||||
g_object_unref( image );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
|
@ -162,7 +162,8 @@ typedef struct _RenderThreadStateClass {
|
||||
|
||||
G_DEFINE_TYPE( RenderThreadState, render_thread_state, VIPS_TYPE_THREAD_STATE );
|
||||
|
||||
/* The BG thread which sits waiting to do some calculations.
|
||||
/* The BG thread which sits waiting to do some calculations, and the semaphore
|
||||
* it waits on holding the number of renders with dirty tiles.
|
||||
*/
|
||||
static GThread *render_thread = NULL;
|
||||
|
||||
@ -170,10 +171,12 @@ static GThread *render_thread = NULL;
|
||||
*/
|
||||
static gboolean render_kill = FALSE;
|
||||
|
||||
/* All the renders with dirty tiles.
|
||||
/* All the renders with dirty tiles, and a semaphore that the bg render thread
|
||||
* waits on.
|
||||
*/
|
||||
static GMutex *render_dirty_lock = NULL;
|
||||
static GSList *render_dirty_all = NULL;
|
||||
static VipsSemaphore n_render_dirty_sem;
|
||||
|
||||
/* Set this to make the bg thread stop and reschedule.
|
||||
*/
|
||||
@ -221,8 +224,14 @@ render_free( Render *render )
|
||||
g_assert( render->ref_count == 0 );
|
||||
|
||||
g_mutex_lock( render_dirty_lock );
|
||||
if( g_slist_find( render_dirty_all, render ) )
|
||||
if( g_slist_find( render_dirty_all, render ) ) {
|
||||
render_dirty_all = g_slist_remove( render_dirty_all, render );
|
||||
|
||||
/* We don't need to adjust the semaphore: if it's too high,
|
||||
* the render thread will just loop and decrement next time
|
||||
* render_dirty_all is NULL.
|
||||
*/
|
||||
}
|
||||
g_mutex_unlock( render_dirty_lock );
|
||||
|
||||
vips_g_mutex_free( render->ref_count_lock );
|
||||
@ -275,34 +284,6 @@ render_unref( Render *render )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Get the first render with dirty tiles.
|
||||
*/
|
||||
static Render *
|
||||
render_dirty_get( void )
|
||||
{
|
||||
Render *render;
|
||||
|
||||
g_mutex_lock( render_dirty_lock );
|
||||
|
||||
/* Just take the head of the jobs list ... we sort when we add.
|
||||
*/
|
||||
render = NULL;
|
||||
if( render_dirty_all ) {
|
||||
render = (Render *) render_dirty_all->data;
|
||||
|
||||
/* Ref the render to make sure it can't die while we're
|
||||
* working on it.
|
||||
*/
|
||||
render_ref( render );
|
||||
|
||||
render_dirty_all = g_slist_remove( render_dirty_all, render );
|
||||
}
|
||||
|
||||
g_mutex_unlock( render_dirty_lock );
|
||||
|
||||
return( render );
|
||||
}
|
||||
|
||||
/* Get the next tile to paint off the dirty list.
|
||||
*/
|
||||
static Tile *
|
||||
@ -444,49 +425,6 @@ render_work( VipsThreadState *state, void *a )
|
||||
|
||||
static void render_dirty_put( Render *render );
|
||||
|
||||
/* Main loop for RenderThreads.
|
||||
*/
|
||||
static void *
|
||||
render_thread_main( void *client )
|
||||
{
|
||||
Render *render;
|
||||
|
||||
while( (render = render_dirty_get()) &&
|
||||
!render_kill ) {
|
||||
VIPS_DEBUG_MSG_GREEN( "render_thread_main: "
|
||||
"threadpool start\n" );
|
||||
|
||||
render_reschedule = FALSE;
|
||||
if( vips_threadpool_run( render->in,
|
||||
render_thread_state_new,
|
||||
render_allocate,
|
||||
render_work,
|
||||
NULL,
|
||||
render ) )
|
||||
VIPS_DEBUG_MSG_RED( "render_thread_main: "
|
||||
"threadpool_run failed\n" );
|
||||
|
||||
VIPS_DEBUG_MSG_GREEN( "render_thread_main: "
|
||||
"threadpool return\n" );
|
||||
|
||||
/* Add back to the jobs list, if we need to.
|
||||
*/
|
||||
render_dirty_put( render );
|
||||
|
||||
/* _get() does a ref to make sure we keep the render
|
||||
* alive during processing ... unref before we loop.
|
||||
* This can kill off the render.
|
||||
*/
|
||||
render_unref( render );
|
||||
}
|
||||
|
||||
/* We are exiting, so render_thread must now be NULL.
|
||||
*/
|
||||
render_thread = NULL;
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Called from vips_shutdown().
|
||||
*/
|
||||
void
|
||||
@ -494,24 +432,25 @@ vips__render_shutdown( void )
|
||||
{
|
||||
/* We may come here without having inited.
|
||||
*/
|
||||
if( !render_dirty_lock )
|
||||
return;
|
||||
if( render_dirty_lock ) {
|
||||
g_mutex_lock( render_dirty_lock );
|
||||
|
||||
g_mutex_lock( render_dirty_lock );
|
||||
if( render_thread ) {
|
||||
GThread *thread;
|
||||
|
||||
if( render_thread ) {
|
||||
GThread *thread;
|
||||
thread = render_thread;
|
||||
render_reschedule = TRUE;
|
||||
render_kill = TRUE;
|
||||
|
||||
thread = render_thread;
|
||||
g_mutex_unlock( render_dirty_lock );
|
||||
|
||||
g_mutex_unlock( render_dirty_lock );
|
||||
vips_semaphore_up( &n_render_dirty_sem );
|
||||
|
||||
render_reschedule = TRUE;
|
||||
render_kill = TRUE;
|
||||
(void) vips_g_thread_join( thread );
|
||||
(void) vips_g_thread_join( thread );
|
||||
}
|
||||
else
|
||||
g_mutex_unlock( render_dirty_lock );
|
||||
}
|
||||
else
|
||||
g_mutex_unlock( render_dirty_lock );
|
||||
}
|
||||
|
||||
static int
|
||||
@ -534,17 +473,10 @@ render_dirty_put( Render *render )
|
||||
render_dirty_all = g_slist_sort( render_dirty_all,
|
||||
(GCompareFunc) render_dirty_sort );
|
||||
|
||||
/* Make sure there is a bg render thread, and get it to
|
||||
* reschedule.
|
||||
/* Tell the bg render thread we have one more dirty
|
||||
* render on there.
|
||||
*/
|
||||
if( !render_thread ) {
|
||||
render_thread = vips_g_thread_new( "sink_screen",
|
||||
render_thread_main, NULL );
|
||||
g_assert( render_thread );
|
||||
}
|
||||
VIPS_DEBUG_MSG_GREEN( "render_dirty_put: "
|
||||
"reschedule\n" );
|
||||
render_reschedule = TRUE;
|
||||
vips_semaphore_up( &n_render_dirty_sem );
|
||||
}
|
||||
}
|
||||
|
||||
@ -1020,10 +952,96 @@ mask_fill( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Get the first render with dirty tiles.
|
||||
*/
|
||||
static Render *
|
||||
render_dirty_get( void )
|
||||
{
|
||||
Render *render;
|
||||
|
||||
/* Wait for a render with dirty tiles.
|
||||
*/
|
||||
vips_semaphore_down( &n_render_dirty_sem );
|
||||
|
||||
g_mutex_lock( render_dirty_lock );
|
||||
|
||||
/* Just take the head of the jobs list ... we sort when we add.
|
||||
*/
|
||||
render = NULL;
|
||||
if( render_dirty_all ) {
|
||||
render = (Render *) render_dirty_all->data;
|
||||
|
||||
/* Ref the render to make sure it can't die while we're
|
||||
* working on it.
|
||||
*/
|
||||
render_ref( render );
|
||||
|
||||
render_dirty_all = g_slist_remove( render_dirty_all, render );
|
||||
}
|
||||
|
||||
g_mutex_unlock( render_dirty_lock );
|
||||
|
||||
return( render );
|
||||
}
|
||||
|
||||
/* Loop for the background render mananger thread.
|
||||
*/
|
||||
static void *
|
||||
render_thread_main( void *client )
|
||||
{
|
||||
Render *render;
|
||||
|
||||
while( !render_kill ) {
|
||||
VIPS_DEBUG_MSG_GREEN( "render_thread_main: "
|
||||
"threadpool start\n" );
|
||||
|
||||
render_reschedule = FALSE;
|
||||
|
||||
if( (render = render_dirty_get()) ) {
|
||||
if( vips_threadpool_run( render->in,
|
||||
render_thread_state_new,
|
||||
render_allocate,
|
||||
render_work,
|
||||
NULL,
|
||||
render ) )
|
||||
VIPS_DEBUG_MSG_RED( "render_thread_main: "
|
||||
"threadpool_run failed\n" );
|
||||
|
||||
VIPS_DEBUG_MSG_GREEN( "render_thread_main: "
|
||||
"threadpool return\n" );
|
||||
|
||||
/* Add back to the jobs list, if we need to.
|
||||
*/
|
||||
render_dirty_put( render );
|
||||
|
||||
/* _get() does a ref to make sure we keep the render
|
||||
* alive during processing ... unref before we loop.
|
||||
* This can kill off the render.
|
||||
*/
|
||||
render_unref( render );
|
||||
}
|
||||
}
|
||||
|
||||
/* We are exiting, so render_thread must now be NULL.
|
||||
*/
|
||||
render_thread = NULL;
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_sink_screen_init( void )
|
||||
{
|
||||
g_assert( !render_thread );
|
||||
g_assert( !render_dirty_lock );
|
||||
|
||||
render_dirty_lock = vips_g_mutex_new();
|
||||
render_thread = vips_g_thread_new( "sink_screen",
|
||||
render_thread_main, NULL );
|
||||
vips_semaphore_init( &n_render_dirty_sem, 0, "n_render_dirty" );
|
||||
|
||||
g_assert( render_thread );
|
||||
g_assert( render_dirty_lock );
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user