Home | History | Annotate | Download | only in refcount
      1 /* Copyright (C) 2005 Imendio AB
      2  *
      3  * This software is provided "as is"; redistribution and modification
      4  * is permitted, provided that the following disclaimer is retained.
      5  *
      6  * This software is distributed in the hope that it will be useful,
      7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      9  * In no event shall the authors or contributors be liable for any
     10  * direct, indirect, incidental, special, exemplary, or consequential
     11  * damages (including, but not limited to, procurement of substitute
     12  * goods or services; loss of use, data, or profits; or business
     13  * interruption) however caused and on any theory of liability, whether
     14  * in contract, strict liability, or tort (including negligence or
     15  * otherwise) arising in any way out of the use of this software, even
     16  * if advised of the possibility of such damage.
     17  */
     18 #include <glib-object.h>
     19 #include <unistd.h>
     20 
     21 #define TEST_POINTER1   ((gpointer) 47)
     22 #define TEST_POINTER2   ((gpointer) 49)
     23 #define TEST_INT1       (-77)
     24 #define TEST_INT2       (78)
     25 
     26 /* --- GTest class --- */
     27 typedef struct {
     28   GObject object;
     29   gint value;
     30   gpointer test_pointer1;
     31   gpointer test_pointer2;
     32 } GTest;
     33 typedef struct {
     34   GObjectClass parent_class;
     35   void (*test_signal1) (GTest * test, gint an_int);
     36   void (*test_signal2) (GTest * test, gint an_int);
     37 } GTestClass;
     38 
     39 #define G_TYPE_TEST                (my_test_get_type ())
     40 #define MY_TEST(test)              (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
     41 #define MY_IS_TEST(test)           (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
     42 #define MY_TEST_CLASS(tclass)      (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
     43 #define MY_IS_TEST_CLASS(tclass)   (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
     44 #define MY_TEST_GET_CLASS(test)    (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
     45 
     46 G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT);
     47 
     48 /* --- variables --- */
     49 static volatile gboolean stopping = FALSE;
     50 static guint             test_signal1 = 0;
     51 static guint             test_signal2 = 0;
     52 static gboolean          seen_signal_handler = FALSE;
     53 static gboolean          seen_cleanup = FALSE;
     54 static gboolean          seen_test_int1 = FALSE;
     55 static gboolean          seen_test_int2 = FALSE;
     56 static gboolean          seen_thread1 = FALSE;
     57 static gboolean          seen_thread2 = FALSE;
     58 
     59 /* --- functions --- */
     60 static void
     61 my_test_init (GTest * test)
     62 {
     63   g_print ("init %p\n", test);
     64 
     65   test->value = 0;
     66   test->test_pointer1 = TEST_POINTER1;
     67   test->test_pointer2 = TEST_POINTER2;
     68 }
     69 
     70 enum {
     71   ARG_0,
     72   ARG_TEST_PROP
     73 };
     74 
     75 static void
     76 my_test_set_property (GObject      *object,
     77                      guint         prop_id,
     78                      const GValue *value,
     79                      GParamSpec   *pspec)
     80 {
     81   GTest *test = MY_TEST (object);
     82   switch (prop_id)
     83     {
     84     case ARG_TEST_PROP:
     85       test->value = g_value_get_int (value);
     86       break;
     87     default:
     88       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     89       break;
     90     }
     91 }
     92 
     93 static void
     94 my_test_get_property (GObject    *object,
     95                      guint       prop_id,
     96                      GValue     *value,
     97                      GParamSpec *pspec)
     98 {
     99   GTest *test = MY_TEST (object);
    100   switch (prop_id)
    101     {
    102     case ARG_TEST_PROP:
    103       g_value_set_int (value, test->value);
    104       break;
    105     default:
    106       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    107       break;
    108     }
    109 }
    110 
    111 static void
    112 my_test_test_signal2 (GTest *test,
    113                       gint   an_int)
    114 {
    115 }
    116 
    117 static void
    118 my_test_emit_test_signal1 (GTest *test,
    119                            gint   vint)
    120 {
    121   g_signal_emit (G_OBJECT (test), test_signal1, 0, vint);
    122 }
    123 
    124 static void
    125 my_test_emit_test_signal2 (GTest *test,
    126                            gint   vint)
    127 {
    128   g_signal_emit (G_OBJECT (test), test_signal2, 0, vint);
    129 }
    130 
    131 static void
    132 my_test_class_init (GTestClass *klass)
    133 {
    134   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    135 
    136   gobject_class->set_property = my_test_set_property;
    137   gobject_class->get_property = my_test_get_property;
    138 
    139   test_signal1 = g_signal_new ("test-signal1", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
    140                                G_STRUCT_OFFSET (GTestClass, test_signal1), NULL, NULL,
    141                                g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
    142   test_signal2 = g_signal_new ("test-signal2", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
    143                                G_STRUCT_OFFSET (GTestClass, test_signal2), NULL, NULL,
    144                                g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
    145 
    146   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TEST_PROP,
    147                                    g_param_spec_int ("test-prop", "Test Prop", "Test property",
    148                                                      0, 1, 0, G_PARAM_READWRITE));
    149   klass->test_signal2 = my_test_test_signal2;
    150 }
    151 
    152 static inline guint32
    153 quick_rand32 (void)
    154 {
    155   static guint32 accu = 2147483563;
    156   accu = 1664525 * accu + 1013904223;
    157   return accu;
    158 }
    159 
    160 static void
    161 test_closure (GClosure *closure)
    162 {
    163   /* try to produce high contention in closure->ref_count */
    164   guint i = 0, n = quick_rand32() % 199;
    165   for (i = 0; i < n; i++)
    166     g_closure_ref (closure);
    167   g_closure_sink (closure); /* NOP */
    168   for (i = 0; i < n; i++)
    169     g_closure_unref (closure);
    170 }
    171 
    172 static gpointer
    173 thread1_main (gpointer data)
    174 {
    175   GClosure *closure = data;
    176   while (!stopping)
    177     {
    178       static guint count = 0;
    179       test_closure (closure);
    180       if (++count % 10000 == 0)
    181         {
    182           g_printerr ("c");
    183           g_thread_yield(); /* force context switch */
    184           seen_thread1 = TRUE;
    185         }
    186     }
    187   return NULL;
    188 }
    189 
    190 static gpointer
    191 thread2_main (gpointer data)
    192 {
    193   GClosure *closure = data;
    194   while (!stopping)
    195     {
    196       static guint count = 0;
    197       test_closure (closure);
    198       if (++count % 10000 == 0)
    199         {
    200           g_printerr ("C");
    201           g_thread_yield(); /* force context switch */
    202           seen_thread2 = TRUE;
    203         }
    204     }
    205   return NULL;
    206 }
    207 
    208 static void
    209 test_signal_handler (GTest   *test,
    210                      gint     vint,
    211                      gpointer data)
    212 {
    213   g_assert (data == TEST_POINTER2);
    214   g_assert (test->test_pointer1 == TEST_POINTER1);
    215   seen_signal_handler = TRUE;
    216   seen_test_int1 |= vint == TEST_INT1;
    217   seen_test_int2 |= vint == TEST_INT2;
    218 }
    219 
    220 static void
    221 destroy_data (gpointer  data,
    222               GClosure *closure)
    223 {
    224   seen_cleanup = data == TEST_POINTER2;
    225   g_assert (closure->ref_count == 0);
    226 }
    227 
    228 static void
    229 test_emissions (GTest *test)
    230 {
    231   my_test_emit_test_signal1 (test, TEST_INT1);
    232   my_test_emit_test_signal2 (test, TEST_INT2);
    233 }
    234 
    235 int
    236 main (int    argc,
    237       char **argv)
    238 {
    239   GThread *thread1, *thread2;
    240   GClosure *closure;
    241   GTest *object;
    242   guint i;
    243 
    244   g_thread_init (NULL);
    245   g_print ("START: %s\n", argv[0]);
    246   g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
    247   g_type_init ();
    248 
    249   object = g_object_new (G_TYPE_TEST, NULL);
    250   closure = g_cclosure_new (G_CALLBACK (test_signal_handler), TEST_POINTER2, destroy_data);
    251 
    252   g_signal_connect_closure (object, "test-signal1", closure, FALSE);
    253   g_signal_connect_closure (object, "test-signal2", closure, FALSE);
    254 
    255   stopping = FALSE;
    256 
    257   thread1 = g_thread_create (thread1_main, closure, TRUE, NULL);
    258   thread2 = g_thread_create (thread2_main, closure, TRUE, NULL);
    259 
    260   for (i = 0; i < 1000000; i++)
    261     {
    262       static guint count = 0;
    263       test_emissions (object);
    264       if (++count % 10000 == 0)
    265         {
    266           g_printerr (".\n");
    267           g_thread_yield(); /* force context switch */
    268         }
    269     }
    270 
    271   stopping = TRUE;
    272   g_print ("\nstopping\n");
    273 
    274   /* wait for thread shutdown */
    275   g_thread_join (thread1);
    276   g_thread_join (thread2);
    277 
    278   /* finalize object, destroy signals, run cleanup code */
    279   g_object_unref (object);
    280 
    281   g_print ("stopped\n");
    282 
    283   g_assert (seen_thread1 != FALSE);
    284   g_assert (seen_thread2 != FALSE);
    285   g_assert (seen_test_int1 != FALSE);
    286   g_assert (seen_test_int2 != FALSE);
    287   g_assert (seen_signal_handler != FALSE);
    288   g_assert (seen_cleanup != FALSE);
    289 
    290   return 0;
    291 }
    292