mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 16:32:18 +01:00 
			
		
		
		
	notify: Add tests for threadsafe object notifies
https://bugzilla.gnome.org/show_bug.cgi?id=166020
This commit is contained in:
		
				
					committed by
					
						 Benjamin Otte
						Benjamin Otte
					
				
			
			
				
	
			
			
			
						parent
						
							0483ef000a
						
					
				
				
					commit
					f6d3e224df
				
			
							
								
								
									
										2
									
								
								tests/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								tests/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -36,6 +36,8 @@ onceinit | ||||
| patterntest | ||||
| properties | ||||
| properties2 | ||||
| properties3 | ||||
| properties4 | ||||
| qsort-test | ||||
| queue-test | ||||
| regex-test | ||||
|   | ||||
| @@ -19,6 +19,8 @@ test_programs = 				\ | ||||
| 	objects2				\ | ||||
| 	properties				\ | ||||
| 	properties2				\ | ||||
| 	properties3				\ | ||||
| 	properties4				\ | ||||
| 	signal1					\ | ||||
| 	signal2					\ | ||||
| 	signal3 | ||||
|   | ||||
							
								
								
									
										204
									
								
								tests/refcount/properties3.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								tests/refcount/properties3.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,204 @@ | ||||
| #include <glib.h> | ||||
| #include <glib-object.h> | ||||
|  | ||||
| #define G_TYPE_TEST                (my_test_get_type ()) | ||||
| #define MY_TEST(test)              (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest)) | ||||
| #define MY_IS_TEST(test)           (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST)) | ||||
| #define MY_TEST_CLASS(tclass)      (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass)) | ||||
| #define MY_IS_TEST_CLASS(tclass)   (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST)) | ||||
| #define MY_TEST_GET_CLASS(test)    (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass)) | ||||
|  | ||||
| enum { | ||||
|   PROP_0, | ||||
|   PROP_DUMMY | ||||
| }; | ||||
|  | ||||
| typedef struct _GTest GTest; | ||||
| typedef struct _GTestClass GTestClass; | ||||
|  | ||||
| struct _GTest | ||||
| { | ||||
|   GObject object; | ||||
|   gint id; | ||||
|   gint dummy; | ||||
|  | ||||
|   gint count; | ||||
|   gint setcount; | ||||
| }; | ||||
|  | ||||
| struct _GTestClass | ||||
| { | ||||
|   GObjectClass parent_class; | ||||
| }; | ||||
|  | ||||
| G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT); | ||||
|  | ||||
| static volatile gboolean stopping; | ||||
|  | ||||
| static void my_test_get_property (GObject    *object, | ||||
| 				  guint       prop_id, | ||||
| 				  GValue     *value, | ||||
| 				  GParamSpec *pspec); | ||||
| static void my_test_set_property (GObject      *object, | ||||
| 				  guint         prop_id, | ||||
| 				  const GValue *value, | ||||
| 				  GParamSpec   *pspec); | ||||
|  | ||||
| static void | ||||
| my_test_class_init (GTestClass * klass) | ||||
| { | ||||
|   GObjectClass *gobject_class; | ||||
|  | ||||
|   gobject_class = (GObjectClass *) klass; | ||||
|  | ||||
|   gobject_class->get_property = my_test_get_property; | ||||
|   gobject_class->set_property = my_test_set_property; | ||||
|  | ||||
|   g_object_class_install_property (gobject_class, | ||||
| 				   PROP_DUMMY, | ||||
| 				   g_param_spec_int ("dummy", | ||||
| 						     NULL, | ||||
| 						     NULL, | ||||
| 						     0, G_MAXINT, 0, | ||||
| 						     G_PARAM_READWRITE)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_test_init (GTest * test) | ||||
| { | ||||
|   static guint static_id = 1; | ||||
|   test->id = static_id++; | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_test_get_property (GObject    *object, | ||||
| 		      guint       prop_id, | ||||
| 		      GValue     *value, | ||||
| 		      GParamSpec *pspec) | ||||
| { | ||||
|   GTest *test; | ||||
|  | ||||
|   test = MY_TEST (object); | ||||
|  | ||||
|   switch (prop_id) | ||||
|     { | ||||
|     case PROP_DUMMY: | ||||
|       g_value_set_int (value, g_atomic_int_get (&test->dummy)); | ||||
|       break; | ||||
|     default: | ||||
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_test_set_property (GObject      *object, | ||||
| 		      guint         prop_id, | ||||
| 		      const GValue *value, | ||||
| 		      GParamSpec   *pspec) | ||||
| { | ||||
|   GTest *test; | ||||
|  | ||||
|   test = MY_TEST (object); | ||||
|  | ||||
|   switch (prop_id) | ||||
|     { | ||||
|     case PROP_DUMMY: | ||||
|       g_atomic_int_set (&test->dummy, g_value_get_int (value)); | ||||
|       break; | ||||
|     default: | ||||
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| dummy_notify (GObject    *object, | ||||
| 	      GParamSpec *pspec) | ||||
| { | ||||
|   GTest *test; | ||||
|  | ||||
|   test = MY_TEST (object); | ||||
|  | ||||
|   g_atomic_int_inc (&test->count); | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_test_do_property (GTest * test) | ||||
| { | ||||
|   gint dummy; | ||||
|  | ||||
|   g_atomic_int_inc (&test->setcount); | ||||
|  | ||||
|   g_object_get (test, "dummy", &dummy, NULL); | ||||
|   g_object_set (test, "dummy", dummy + 1, NULL); | ||||
| } | ||||
|  | ||||
| static gpointer | ||||
| run_thread (GTest * test) | ||||
| { | ||||
|   gint i = 1; | ||||
|  | ||||
|   while (!stopping) { | ||||
|     my_test_do_property (test); | ||||
|     if ((i++ % 10000) == 0) | ||||
|       { | ||||
| 	g_print (".%c", 'a' + test->id); | ||||
| 	g_thread_yield(); /* force context switch */ | ||||
|       } | ||||
|   } | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| int | ||||
| main (int argc, char **argv) | ||||
| { | ||||
|   gint i; | ||||
|   GTest *test; | ||||
|   GArray *test_threads; | ||||
|   const gint n_threads = 5; | ||||
|  | ||||
|   g_thread_init (NULL); | ||||
|  | ||||
|   g_print ("START: %s\n", argv[0]); | ||||
|   g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK)); | ||||
|   g_type_init (); | ||||
|  | ||||
|   test = g_object_new (G_TYPE_TEST, NULL); | ||||
|  | ||||
|   g_assert (test->count == test->dummy); | ||||
|   g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL); | ||||
|  | ||||
|   test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *)); | ||||
|  | ||||
|   stopping = FALSE; | ||||
|  | ||||
|   for (i = 0; i < n_threads; i++) { | ||||
|     GThread *thread; | ||||
|  | ||||
|     thread = g_thread_create ((GThreadFunc) run_thread, test, TRUE, NULL); | ||||
|     g_array_append_val (test_threads, thread); | ||||
|   } | ||||
|   g_usleep (30000000); | ||||
|  | ||||
|   stopping = TRUE; | ||||
|   g_print ("\nstopping\n"); | ||||
|  | ||||
|   /* join all threads */ | ||||
|   for (i = 0; i < n_threads; i++) { | ||||
|     GThread *thread; | ||||
|  | ||||
|     thread = g_array_index (test_threads, GThread *, i); | ||||
|     g_thread_join (thread); | ||||
|   } | ||||
|  | ||||
|   g_print ("stopped\n"); | ||||
|  | ||||
|   g_print ("%d %d\n", test->setcount, test->count); | ||||
|  | ||||
|   g_array_free (test_threads, TRUE); | ||||
|   g_object_unref (test); | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
							
								
								
									
										169
									
								
								tests/refcount/properties4.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								tests/refcount/properties4.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| #include <glib.h> | ||||
| #include <glib-object.h> | ||||
|  | ||||
| #define MY_TYPE_BADGER              (my_badger_get_type ()) | ||||
| #define MY_BADGER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_BADGER, MyBadger)) | ||||
| #define MY_IS_BADGER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_BADGER)) | ||||
| #define MY_BADGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), MY_TYPE_BADGER, MyBadgerClass)) | ||||
| #define MY_IS_BADGER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_TYPE_BADGER)) | ||||
| #define MY_BADGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), MY_TYPE_BADGER, MyBadgerClass)) | ||||
|  | ||||
| enum { | ||||
|   PROP_0, | ||||
|   PROP_MAMA | ||||
| }; | ||||
|  | ||||
| typedef struct _MyBadger MyBadger; | ||||
| typedef struct _MyBadgerClass MyBadgerClass; | ||||
|  | ||||
| struct _MyBadger | ||||
| { | ||||
|   GObject parent_instance; | ||||
|  | ||||
|   MyBadger * mama; | ||||
|   guint mama_notify_count; | ||||
| }; | ||||
|  | ||||
| struct _MyBadgerClass | ||||
| { | ||||
|   GObjectClass parent_class; | ||||
| }; | ||||
|  | ||||
| G_DEFINE_TYPE (MyBadger, my_badger, G_TYPE_OBJECT); | ||||
|  | ||||
| static void my_badger_dispose (GObject * object); | ||||
|  | ||||
| static void my_badger_get_property (GObject    *object, | ||||
| 				    guint       prop_id, | ||||
| 				    GValue     *value, | ||||
| 				    GParamSpec *pspec); | ||||
| static void my_badger_set_property (GObject      *object, | ||||
| 				    guint         prop_id, | ||||
| 				    const GValue *value, | ||||
| 				    GParamSpec   *pspec); | ||||
|  | ||||
| static void my_badger_mama_notify (GObject    *object, | ||||
| 				   GParamSpec *pspec); | ||||
|  | ||||
| static void | ||||
| my_badger_class_init (MyBadgerClass * klass) | ||||
| { | ||||
|   GObjectClass *gobject_class; | ||||
|  | ||||
|   gobject_class = (GObjectClass *) klass; | ||||
|  | ||||
|   gobject_class->dispose = my_badger_dispose; | ||||
|  | ||||
|   gobject_class->get_property = my_badger_get_property; | ||||
|   gobject_class->set_property = my_badger_set_property; | ||||
|  | ||||
|   g_object_class_install_property (gobject_class, | ||||
| 				   PROP_MAMA, | ||||
| 				   g_param_spec_object ("mama", | ||||
| 							NULL, | ||||
| 							NULL, | ||||
| 							MY_TYPE_BADGER, | ||||
| 							G_PARAM_READWRITE)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_badger_init (MyBadger * self) | ||||
| { | ||||
|   g_signal_connect (self, "notify::mama", G_CALLBACK (my_badger_mama_notify), | ||||
|       NULL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_badger_dispose (GObject * object) | ||||
| { | ||||
|   MyBadger * self; | ||||
|  | ||||
|   self = MY_BADGER (object); | ||||
|  | ||||
|   if (self->mama != NULL) | ||||
|     { | ||||
|       g_object_unref (self->mama); | ||||
|       self->mama = NULL; | ||||
|     } | ||||
|  | ||||
|   G_OBJECT_CLASS (my_badger_parent_class)->dispose (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_badger_get_property (GObject    *object, | ||||
| 			guint        prop_id, | ||||
| 			GValue     *value, | ||||
| 			GParamSpec *pspec) | ||||
| { | ||||
|   MyBadger *self; | ||||
|  | ||||
|   self = MY_BADGER (object); | ||||
|  | ||||
|   switch (prop_id) | ||||
|     { | ||||
|     case PROP_MAMA: | ||||
|       g_value_set_object (value, self->mama); | ||||
|       break; | ||||
|     default: | ||||
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_badger_set_property (GObject      *object, | ||||
| 			guint         prop_id, | ||||
| 			const GValue *value, | ||||
| 			GParamSpec   *pspec) | ||||
| { | ||||
|   MyBadger *self; | ||||
|  | ||||
|   self = MY_BADGER (object); | ||||
|  | ||||
|   switch (prop_id) | ||||
|     { | ||||
|     case PROP_MAMA: | ||||
|       if (self->mama) | ||||
| 	g_object_unref (self->mama); | ||||
|       self->mama = g_value_dup_object (value); | ||||
|       if (self->mama) | ||||
| 	g_object_set (self->mama, "mama", NULL, NULL); /* another notify */ | ||||
|       break; | ||||
|     default: | ||||
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||||
|       break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| my_badger_mama_notify (GObject    *object, | ||||
|                        GParamSpec *pspec) | ||||
| { | ||||
|   MyBadger *self; | ||||
|  | ||||
|   self = MY_BADGER (object); | ||||
|  | ||||
|   self->mama_notify_count++; | ||||
| } | ||||
|  | ||||
| int | ||||
| main (int argc, char **argv) | ||||
| { | ||||
|   MyBadger * badger1, * badger2; | ||||
|  | ||||
|   g_print ("START: %s\n", argv[0]); | ||||
|   g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK)); | ||||
|   g_type_init (); | ||||
|  | ||||
|   badger1 = g_object_new (MY_TYPE_BADGER, NULL); | ||||
|   badger2 = g_object_new (MY_TYPE_BADGER, NULL); | ||||
|  | ||||
|   g_object_set (badger1, "mama", badger2, NULL); | ||||
|   g_assert_cmpuint (badger1->mama_notify_count, ==, 1); | ||||
|   g_assert_cmpuint (badger2->mama_notify_count, ==, 1); | ||||
|  | ||||
|   g_object_unref (badger1); | ||||
|   g_object_unref (badger2); | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user