move $enable_debug down below checks for GCC to avoid setting CFLAGS
[platform/upstream/glib.git] / tests / mainloop-test.c
1 #include <errno.h>
2 #include <glib.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #define ITERS 10000
8 #define INCREMENT 10
9 #define NTHREADS 4
10 #define NCRAWLERS 4
11 #define CRAWLER_TIMEOUT_RANGE 40
12 #define RECURSER_TIMEOUT 50
13
14 G_LOCK_DEFINE_STATIC (context_array_lock);
15 GPtrArray *context_array;
16 GMainLoop *main_loop;
17
18 G_LOCK_DEFINE_STATIC (crawler_array_lock);
19 GPtrArray *crawler_array;
20
21 typedef struct _AddrData AddrData;
22 typedef struct _TestData TestData;
23
24 struct _AddrData
25 {
26   GMainLoop *loop;
27   GIOChannel *dest;
28   gint count;
29 };
30
31 struct _TestData
32 {
33   gint current_val;
34   gint iters;
35   GIOChannel *in;
36 };
37
38 static void cleanup_crawlers (GMainContext *context);
39
40 gboolean
41 read_all (GIOChannel *channel, char *buf, int len)
42 {
43   int bytes_read = 0;
44   int count;
45   GIOError err;
46
47   while (bytes_read < len)
48     {
49       err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
50       if (err)
51         {
52           if (err != G_IO_ERROR_AGAIN)
53             return FALSE;
54         }
55       else if (count == 0)
56         return FALSE;
57
58       bytes_read += count;
59     }
60
61   return TRUE;
62 }
63
64 gboolean
65 write_all (GIOChannel *channel, char *buf, int len)
66 {
67   int bytes_written = 0;
68   int count;
69   GIOError err;
70
71   while (bytes_written < len)
72     {
73       err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
74       if (err && err != G_IO_ERROR_AGAIN)
75         return FALSE;
76
77       bytes_written += count;
78     }
79
80   return TRUE;
81 }
82
83 gboolean
84 adder_callback (GIOChannel   *source,
85                 GIOCondition  condition,
86                 gpointer      data)
87 {
88   char buf1[32];
89   char buf2[32];
90
91   char result[32];
92
93   AddrData *addr_data = data;
94
95   if (!read_all (source, buf1, 32) ||
96       !read_all (source, buf2, 32))
97     {
98       g_main_loop_quit (addr_data->loop);
99       return FALSE;
100     }
101
102   sprintf (result, "%d", atoi(buf1) + atoi(buf2));
103   write_all (addr_data->dest, result, 32);
104   
105   return TRUE;
106 }
107
108 gboolean
109 timeout_callback (gpointer data)
110 {
111   AddrData *addr_data = data;
112
113   addr_data->count++;
114   
115   return TRUE;
116 }
117
118 void
119 adder_thread (gpointer data)
120 {
121   GMainContext *context;
122   GSource *adder_source;
123   GSource *timeout_source;
124
125   GIOChannel **channels = data;
126   AddrData addr_data;
127
128   context = g_main_context_get (g_thread_self());
129
130   G_LOCK (context_array_lock);
131   g_ptr_array_add (context_array, context);
132   G_UNLOCK (context_array_lock);
133
134   addr_data.dest = channels[1];
135   addr_data.loop = g_main_loop_new (context, FALSE);
136   addr_data.count = 0;
137   
138   adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
139   g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
140   g_source_attach (adder_source, context);
141   g_source_unref (adder_source);
142
143   timeout_source = g_timeout_source_new (10);
144   g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
145   g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
146   g_source_attach (timeout_source, context);
147   g_source_unref (timeout_source);
148
149   g_main_run (addr_data.loop);
150
151   g_io_channel_close (channels[0]);
152   g_io_channel_close (channels[1]);
153   g_io_channel_unref (channels[0]);
154   g_io_channel_unref (channels[1]);
155
156   g_free (channels);
157   
158   g_main_loop_unref (addr_data.loop);
159
160   g_print ("Timeout run %d times\n", addr_data.count);
161
162   G_LOCK (context_array_lock);
163   g_ptr_array_remove (context_array, context);
164   if (context_array->len == 0)
165     g_main_loop_quit (main_loop);
166   G_UNLOCK (context_array_lock);
167
168   cleanup_crawlers (context);
169 }
170
171 void
172 io_pipe (GIOChannel **channels)
173 {
174   gint fds[2];
175
176   if (pipe(fds) < 0)
177     {
178       g_warning ("Cannot create pipe %s\n", g_strerror (errno));
179       exit (1);
180     }
181
182   channels[0] = g_io_channel_unix_new (fds[0]);
183   channels[1] = g_io_channel_unix_new (fds[1]);
184 }
185
186 void
187 do_add (GIOChannel *in, gint a, gint b)
188 {
189   char buf1[32];
190   char buf2[32];
191
192   sprintf (buf1, "%d", a);
193   sprintf (buf2, "%d", b);
194
195   write_all (in, buf1, 32);
196   write_all (in, buf2, 32);
197 }
198
199 gboolean
200 adder_response (GIOChannel   *source,
201                 GIOCondition  condition,
202                 gpointer      data)
203 {
204   char result[32];
205   TestData *test_data = data;
206   
207   if (!read_all (source, result, 32))
208     return FALSE;
209
210   test_data->current_val = atoi (result);
211   test_data->iters--;
212
213   if (test_data->iters == 0)
214     {
215       if (test_data->current_val != ITERS * INCREMENT)
216         {
217           g_print ("Addition failed: %d != %d\n",
218                    test_data->current_val, ITERS * INCREMENT);
219           exit (1);
220         }
221
222       g_io_channel_close (source);
223       g_io_channel_close (test_data->in);
224
225       g_io_channel_unref (source);
226       g_io_channel_unref (test_data->in);
227       
228       return FALSE;
229     }
230   
231   do_add (test_data->in, test_data->current_val, INCREMENT);
232
233   return TRUE;
234 }
235
236 void
237 create_adder_thread (void)
238 {
239   GError *err = NULL;
240   TestData *test_data;
241   
242   GIOChannel *in_channels[2];
243   GIOChannel *out_channels[2];
244
245   GIOChannel **sub_channels;
246
247   sub_channels = g_new (GIOChannel *, 2);
248
249   io_pipe (in_channels);
250   io_pipe (out_channels);
251
252   sub_channels[0] = in_channels[0];
253   sub_channels[1] = out_channels[1];
254
255   g_thread_create (adder_thread, sub_channels, 0,
256                    FALSE, TRUE, G_THREAD_PRIORITY_NORMAL, &err);
257
258   if (err)
259     {
260       g_warning ("Cannot create thread: %s", err->message);
261       exit (1);
262     }
263
264   test_data = g_new (TestData, 1);
265   test_data->in = in_channels[1];
266   test_data->current_val = 0;
267   test_data->iters = ITERS;
268
269   g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
270                   adder_response, test_data);
271   
272   do_add (test_data->in, test_data->current_val, INCREMENT);
273 }
274
275 static void create_crawler (void);
276
277 static void
278 remove_crawler (void)
279 {
280   GSource *other_source;
281
282   if (crawler_array->len > 0)
283     {
284       other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)];
285       g_source_destroy (other_source);
286       g_assert (g_ptr_array_remove_fast (crawler_array, other_source));
287     }
288 }
289
290 static gint
291 crawler_callback (gpointer data)
292 {
293   GSource *source = data;
294
295   G_LOCK (crawler_array_lock);
296   
297   if (!g_ptr_array_remove_fast (crawler_array, source))
298     remove_crawler();
299
300   remove_crawler();
301   G_UNLOCK (crawler_array_lock);
302             
303   create_crawler();
304   create_crawler();
305
306   return FALSE;
307 }
308
309 static void
310 create_crawler (void)
311 {
312   GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
313   g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);
314
315   G_LOCK (context_array_lock);
316   g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
317   g_source_unref (source);
318   G_UNLOCK (context_array_lock);
319
320   G_LOCK (crawler_array_lock);
321   g_ptr_array_add (crawler_array, source);
322   G_UNLOCK (crawler_array_lock);
323 }
324
325 static void
326 cleanup_crawlers (GMainContext *context)
327 {
328   gint i;
329   
330   G_LOCK (crawler_array_lock);
331   for (i=0; i < crawler_array->len; i++)
332     {
333       if (g_source_get_context (crawler_array->pdata[i]) == context)
334         {
335           g_source_destroy (g_ptr_array_remove_index (crawler_array, i));
336           i--;
337         }
338     }
339   G_UNLOCK (crawler_array_lock);
340 }
341
342 static gboolean
343 recurser_idle (gpointer data)
344 {
345   GMainContext *context = data;
346   gint i;
347
348   for (i = 0; i < 10; i++)
349     g_main_context_iteration (context, TRUE);
350
351   return FALSE;
352 }
353
354 static gboolean
355 recurser_start (gpointer data)
356 {
357   GMainContext *context;
358   GSource *source;
359   
360   G_LOCK (context_array_lock);
361   context = context_array->pdata[g_random_int_range (0, context_array->len)];
362   source = g_idle_source_new ();
363   g_source_set_callback (source, recurser_idle, context, NULL);
364   g_source_attach (source, context);
365   g_source_unref (source);
366   G_UNLOCK (context_array_lock);
367
368   return TRUE;
369 }
370
371 int 
372 main (int   argc,
373       char *argv[])
374 {
375   /* Only run the test, if threads are enabled and a default thread
376      implementation is available */
377 #if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE)
378   gint i;
379
380   g_thread_init (NULL);
381
382   context_array = g_ptr_array_new ();
383   crawler_array = g_ptr_array_new ();
384
385   main_loop = g_main_loop_new (NULL, FALSE);
386
387   for (i = 0; i < NTHREADS; i++)
388     create_adder_thread ();
389
390   for (i = 0; i < NCRAWLERS; i++)
391     create_crawler ();
392
393   g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL);
394
395   g_main_loop_run (main_loop);
396   g_main_loop_unref (main_loop);
397
398 #endif
399   return 0;
400 }