source: trunk/third/glib2/tests/mainloop-test.c @ 18159

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