seems to work?

needs stress testing still

see https://github.com/jcupitt/libvips/issues/535
This commit is contained in:
John Cupitt 2016-10-10 15:12:12 +01:00
parent 524c395f01
commit ccfe4cb215
5 changed files with 37 additions and 37 deletions

View File

@ -1,6 +1,7 @@
27/9/16 started 8.4.2 27/9/16 started 8.4.2
- small doc improvements - small doc improvements
- fix error message for metadata fetch type mismatch - fix error message for metadata fetch type mismatch
- resolve a race condition in thread shutdown, thanks Lovell
1/5/16 started 8.4 1/5/16 started 8.4
- many more wepsave options [Felix Bünemann] - many more wepsave options [Felix Bünemann]

View File

@ -101,17 +101,8 @@ typedef struct _VipsBufferCache {
VipsBufferThread *buffer_thread; VipsBufferThread *buffer_thread;
GSList *reserve; /* VipsBuffer kept in reserve */ GSList *reserve; /* VipsBuffer kept in reserve */
int n_reserve; /* Number in reserve */ int n_reserve; /* Number in reserve */
struct _VipsBufferCacheProxy *proxy;
} VipsBufferCache; } VipsBufferCache;
/* A proxy object between the image and the cache. This has the lifetime of the
* image and keeps a nullable pointer to the buffer cache.
*/
typedef struct _VipsBufferCacheProxy {
VipsBufferCache *cache;
struct _VipsImage *im;
} VipsBufferCacheProxy;
/* What we track for each pixel buffer. These can move between caches and /* What we track for each pixel buffer. These can move between caches and
* between threads, but not between images. * between threads, but not between images.
*/ */

View File

@ -51,6 +51,8 @@ void vips_g_cond_free( GCond * );
*/ */
GThread *vips_g_thread_new( const char *, GThreadFunc, gpointer ); GThread *vips_g_thread_new( const char *, GThreadFunc, gpointer );
gboolean vips_thread_isworker( void );
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /*__cplusplus*/ #endif /*__cplusplus*/

View File

@ -184,8 +184,8 @@ buffer_thread_free( VipsBufferThread *buffer_thread )
* *
* - on thread shutdown, the enclosing hash is destroyed, and that will * - on thread shutdown, the enclosing hash is destroyed, and that will
* trigger this via GDestroyNotify. * trigger this via GDestroyNotify.
* - if the image is closed, buffer_cache_image_postclose() fires and will call * - if the BufferCache has been allocated by the main thread, this will be
* this * triggered from postclose on the image
* *
* These can happen in either order. * These can happen in either order.
*/ */
@ -223,35 +223,26 @@ buffer_cache_free( VipsBufferCache *cache )
} }
VIPS_FREEF( g_slist_free, cache->reserve ); VIPS_FREEF( g_slist_free, cache->reserve );
cache->proxy->cache = NULL;
cache->proxy = NULL;
g_free( cache ); g_free( cache );
} }
static void static void
buffer_cache_proxy_image_postclose( VipsImage *im, VipsBufferCacheProxy *proxy ) buffer_cache_image_postclose( VipsImage *im, VipsBufferCache *cache )
{ {
g_assert( proxy->im == im ); VipsBufferThread *buffer_thread = cache->buffer_thread;
if( proxy->cache ) { /* Runs to clean up main thread buffers on image close.
VipsBufferCache *cache = proxy->cache; */
VipsBufferThread *buffer_thread = cache->buffer_thread; g_assert( cache->im == im );
VipsImage *im = cache->im; g_assert( !vips_thread_isworker() );
g_assert( cache->im == im ); g_hash_table_remove( buffer_thread->hash, im );
g_hash_table_remove( buffer_thread->hash, im );
}
g_free( proxy );
} }
static VipsBufferCache * static VipsBufferCache *
buffer_cache_new( VipsBufferThread *buffer_thread, VipsImage *im ) buffer_cache_new( VipsBufferThread *buffer_thread, VipsImage *im )
{ {
VipsBufferCache *cache; VipsBufferCache *cache;
VipsBufferCacheProxy *proxy;
cache = g_new( VipsBufferCache, 1 ); cache = g_new( VipsBufferCache, 1 );
cache->buffers = NULL; cache->buffers = NULL;
@ -261,17 +252,14 @@ buffer_cache_new( VipsBufferThread *buffer_thread, VipsImage *im )
cache->reserve = NULL; cache->reserve = NULL;
cache->n_reserve = 0; cache->n_reserve = 0;
proxy = g_new( VipsBufferCacheProxy, 1 ); /* VipsBufferCache allocated from worker threads will be freed when
proxy->im = im; * workers shut down. This won't happen for VipsBufferCache allocated
proxy->cache = cache; * from the main thread, since (obviously) thread shutdown will never
cache->proxy = proxy; * happen. In this case, we need to free resources on image close.
/* Free memory on image close. We can't do this on thread exit since
* some buffers will be made from the main thread and that won't exit
* until program termination.
*/ */
g_signal_connect( im, "postclose", if( !vips_thread_isworker() )
G_CALLBACK( buffer_cache_proxy_image_postclose ), proxy ); g_signal_connect( im, "postclose",
G_CALLBACK( buffer_cache_image_postclose ), cache );
#ifdef DEBUG_CREATE #ifdef DEBUG_CREATE
g_mutex_lock( vips__global_lock ); g_mutex_lock( vips__global_lock );

View File

@ -99,6 +99,10 @@ int vips__thinstrip_height = VIPS__THINSTRIP_HEIGHT;
*/ */
int vips__concurrency = 0; int vips__concurrency = 0;
/* Set this GPrivate to indicate that this is a vips worker.
*/
static GPrivate vips_threadpool_is_worker_private;
/* Glib 2.32 revised the thread API. We need some compat functions. /* Glib 2.32 revised the thread API. We need some compat functions.
*/ */
@ -154,6 +158,16 @@ vips_g_cond_free( GCond *cond )
#endif #endif
} }
/* TRUE if we are a vips worker thread. We sometimes manage resource allocation
* differently for vips workers since we can cheaply free stuff on thread
* termination.
*/
gboolean
vips_thread_isworker( void )
{
return( g_private_get( &vips_threadpool_is_worker_private ) != NULL );
}
typedef struct { typedef struct {
const char *domain; const char *domain;
GThreadFunc func; GThreadFunc func;
@ -170,6 +184,10 @@ vips_thread_run( gpointer data )
if( vips__thread_profile ) if( vips__thread_profile )
vips__thread_profile_attach( info->domain ); vips__thread_profile_attach( info->domain );
/* Set this to something (anything) to tag this thread as a vips worker.
*/
g_private_set( &vips_threadpool_is_worker_private, data );
result = info->func( info->data ); result = info->func( info->data );
g_free( info ); g_free( info );