From b58f67aa90ae7fa5e357ebf1b29f1bc1619fabbf Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Fri, 21 Jun 2013 09:35:04 +0100
Subject: [PATCH] single-thread class init

See:

  https://github.com/jcupitt/libvips/issues/64

We were seeing races in class init on very-many-core machines. GObject
is supposed to single-thread class init, I think, so this shouldn't
happen. Perhaps class init via g_type_class_ref() is not
single-threaded.
---
 libvips/iofuncs/object.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c
index 7bc474a6..1bb7980a 100644
--- a/libvips/iofuncs/object.c
+++ b/libvips/iofuncs/object.c
@@ -2314,16 +2314,32 @@ test_name( VipsObjectClass *class, const char *nickname )
 VipsObjectClass *
 vips_class_find( const char *basename, const char *nickname )
 {
+	static GOnce vips_class_find_once = G_ONCE_INIT;
+	static GMutex *vips_class_find_lock = NULL; 
+
 	const char *classname = basename ? basename : "VipsObject";
 
 	VipsObjectClass *class;
 	GType base;
 
-	if( !(base = g_type_from_name( classname )) || 
-		!(class = vips_class_map_all( base, 
-			(VipsClassMapFn) test_name, (void *) nickname )) ) 
+	if( !(base = g_type_from_name( classname )) )
 		return( NULL );
 
+	if( !vips_class_find_lock ) 
+		vips_class_find_lock = g_once( &vips_class_find_once, 
+			(GThreadFunc) vips_g_mutex_new, NULL );
+
+	/* We have to single-thread class lookup. The first time this runs on
+	 * a class, the g_type_class_ref() in vips_class_map_all() will
+	 * trigger class build and construct the arg table. We musn't have
+	 * this run more than once.
+	 */
+
+	g_mutex_lock( vips_class_find_lock );
+	class = vips_class_map_all( base, 
+		(VipsClassMapFn) test_name, (void *) nickname );
+	g_mutex_unlock( vips_class_find_lock );
+
 	return( class );
 }