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