1 #undef G_DISABLE_ASSERT
13 #include <fcntl.h> /* For _O_BINARY used by pipe() macro */
14 #include <io.h> /* for _pipe() */
15 #define pipe(fds) _pipe(fds, 4096, _O_BINARY)
22 #define CRAWLER_TIMEOUT_RANGE 40
23 #define RECURSER_TIMEOUT 50
25 /* The partial ordering between the context array mutex and
26 * crawler array mutex is that the crawler array mutex cannot
27 * be locked while the context array mutex is locked
29 GPtrArray
*context_array
;
30 GMutex
*context_array_mutex
;
31 GCond
*context_array_cond
;
35 G_LOCK_DEFINE_STATIC (crawler_array_lock
);
36 GPtrArray
*crawler_array
;
38 typedef struct _AddrData AddrData
;
39 typedef struct _TestData TestData
;
55 static void cleanup_crawlers (GMainContext
*context
);
58 read_all (GIOChannel
*channel
, char *buf
, gsize len
)
64 while (bytes_read
< len
)
66 err
= g_io_channel_read (channel
, buf
+ bytes_read
, len
- bytes_read
, &count
);
69 if (err
!= G_IO_ERROR_AGAIN
)
82 write_all (GIOChannel
*channel
, char *buf
, gsize len
)
84 gsize bytes_written
= 0;
88 while (bytes_written
< len
)
90 err
= g_io_channel_write (channel
, buf
+ bytes_written
, len
- bytes_written
, &count
);
91 if (err
&& err
!= G_IO_ERROR_AGAIN
)
94 bytes_written
+= count
;
101 adder_callback (GIOChannel
*source
,
102 GIOCondition condition
,
110 AddrData
*addr_data
= data
;
112 if (!read_all (source
, buf1
, 32) ||
113 !read_all (source
, buf2
, 32))
115 g_main_loop_quit (addr_data
->loop
);
119 sprintf (result
, "%d", atoi(buf1
) + atoi(buf2
));
120 write_all (addr_data
->dest
, result
, 32);
126 timeout_callback (gpointer data
)
128 AddrData
*addr_data
= data
;
136 adder_thread (gpointer data
)
138 GMainContext
*context
;
139 GSource
*adder_source
;
140 GSource
*timeout_source
;
142 GIOChannel
**channels
= data
;
145 context
= g_main_context_new ();
147 g_mutex_lock (context_array_mutex
);
149 g_ptr_array_add (context_array
, context
);
151 if (context_array
->len
== NTHREADS
)
152 g_cond_broadcast (context_array_cond
);
154 g_mutex_unlock (context_array_mutex
);
156 addr_data
.dest
= channels
[1];
157 addr_data
.loop
= g_main_loop_new (context
, FALSE
);
160 adder_source
= g_io_create_watch (channels
[0], G_IO_IN
| G_IO_HUP
);
161 g_source_set_callback (adder_source
, (GSourceFunc
)adder_callback
, &addr_data
, NULL
);
162 g_source_attach (adder_source
, context
);
163 g_source_unref (adder_source
);
165 timeout_source
= g_timeout_source_new (10);
166 g_source_set_callback (timeout_source
, (GSourceFunc
)timeout_callback
, &addr_data
, NULL
);
167 g_source_set_priority (timeout_source
, G_PRIORITY_HIGH
);
168 g_source_attach (timeout_source
, context
);
169 g_source_unref (timeout_source
);
171 g_main_loop_run (addr_data
.loop
);
173 g_io_channel_unref (channels
[0]);
174 g_io_channel_unref (channels
[1]);
178 g_main_loop_unref (addr_data
.loop
);
181 g_print ("Timeout run %d times\n", addr_data
.count
);
184 g_mutex_lock (context_array_mutex
);
185 g_ptr_array_remove (context_array
, context
);
186 if (context_array
->len
== 0)
187 g_main_loop_quit (main_loop
);
188 g_mutex_unlock (context_array_mutex
);
190 cleanup_crawlers (context
);
196 io_pipe (GIOChannel
**channels
)
202 g_warning ("Cannot create pipe %s\n", g_strerror (errno
));
206 channels
[0] = g_io_channel_unix_new (fds
[0]);
207 channels
[1] = g_io_channel_unix_new (fds
[1]);
209 g_io_channel_set_close_on_unref (channels
[0], TRUE
);
210 g_io_channel_set_close_on_unref (channels
[1], TRUE
);
214 do_add (GIOChannel
*in
, gint a
, gint b
)
219 sprintf (buf1
, "%d", a
);
220 sprintf (buf2
, "%d", b
);
222 write_all (in
, buf1
, 32);
223 write_all (in
, buf2
, 32);
227 adder_response (GIOChannel
*source
,
228 GIOCondition condition
,
232 TestData
*test_data
= data
;
234 if (!read_all (source
, result
, 32))
237 test_data
->current_val
= atoi (result
);
240 if (test_data
->iters
== 0)
242 if (test_data
->current_val
!= ITERS
* INCREMENT
)
244 g_print ("Addition failed: %d != %d\n",
245 test_data
->current_val
, ITERS
* INCREMENT
);
249 g_io_channel_unref (source
);
250 g_io_channel_unref (test_data
->in
);
257 do_add (test_data
->in
, test_data
->current_val
, INCREMENT
);
263 create_adder_thread (void)
268 GIOChannel
*in_channels
[2];
269 GIOChannel
*out_channels
[2];
271 GIOChannel
**sub_channels
;
273 sub_channels
= g_new (GIOChannel
*, 2);
275 io_pipe (in_channels
);
276 io_pipe (out_channels
);
278 sub_channels
[0] = in_channels
[0];
279 sub_channels
[1] = out_channels
[1];
281 g_thread_create (adder_thread
, sub_channels
, FALSE
, &err
);
285 g_warning ("Cannot create thread: %s", err
->message
);
289 test_data
= g_new (TestData
, 1);
290 test_data
->in
= in_channels
[1];
291 test_data
->current_val
= 0;
292 test_data
->iters
= ITERS
;
294 g_io_add_watch (out_channels
[0], G_IO_IN
| G_IO_HUP
,
295 adder_response
, test_data
);
297 do_add (test_data
->in
, test_data
->current_val
, INCREMENT
);
300 static void create_crawler (void);
303 remove_crawler (void)
305 GSource
*other_source
;
307 if (crawler_array
->len
> 0)
309 other_source
= crawler_array
->pdata
[g_random_int_range (0, crawler_array
->len
)];
310 g_source_destroy (other_source
);
311 g_assert (g_ptr_array_remove_fast (crawler_array
, other_source
));
316 crawler_callback (gpointer data
)
318 GSource
*source
= data
;
320 G_LOCK (crawler_array_lock
);
322 if (!g_ptr_array_remove_fast (crawler_array
, source
))
326 G_UNLOCK (crawler_array_lock
);
335 create_crawler (void)
337 GSource
*source
= g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE
));
338 g_source_set_callback (source
, (GSourceFunc
)crawler_callback
, source
, NULL
);
340 G_LOCK (crawler_array_lock
);
341 g_ptr_array_add (crawler_array
, source
);
343 g_mutex_lock (context_array_mutex
);
344 g_source_attach (source
, context_array
->pdata
[g_random_int_range (0, context_array
->len
)]);
345 g_source_unref (source
);
346 g_mutex_unlock (context_array_mutex
);
348 G_UNLOCK (crawler_array_lock
);
352 cleanup_crawlers (GMainContext
*context
)
356 G_LOCK (crawler_array_lock
);
357 for (i
=0; i
< crawler_array
->len
; i
++)
359 if (g_source_get_context (crawler_array
->pdata
[i
]) == context
)
361 g_source_destroy (g_ptr_array_remove_index (crawler_array
, i
));
365 G_UNLOCK (crawler_array_lock
);
369 recurser_idle (gpointer data
)
371 GMainContext
*context
= data
;
374 for (i
= 0; i
< 10; i
++)
375 g_main_context_iteration (context
, FALSE
);
381 recurser_start (gpointer data
)
383 GMainContext
*context
;
386 g_mutex_lock (context_array_mutex
);
387 context
= context_array
->pdata
[g_random_int_range (0, context_array
->len
)];
388 source
= g_idle_source_new ();
389 g_source_set_callback (source
, recurser_idle
, context
, NULL
);
390 g_source_attach (source
, context
);
391 g_source_unref (source
);
392 g_mutex_unlock (context_array_mutex
);
401 /* Only run the test, if threads are enabled and a default thread
402 implementation is available */
403 #if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE)
406 g_thread_init (NULL
);
408 context_array
= g_ptr_array_new ();
409 context_array_mutex
= g_mutex_new ();
410 context_array_cond
= g_cond_new ();
412 crawler_array
= g_ptr_array_new ();
414 main_loop
= g_main_loop_new (NULL
, FALSE
);
416 for (i
= 0; i
< NTHREADS
; i
++)
417 create_adder_thread ();
419 /* Wait for all threads to start
421 g_mutex_lock (context_array_mutex
);
423 if (context_array
->len
< NTHREADS
)
424 g_cond_wait (context_array_cond
, context_array_mutex
);
426 g_mutex_unlock (context_array_mutex
);
428 for (i
= 0; i
< NCRAWLERS
; i
++)
431 g_timeout_add (RECURSER_TIMEOUT
, recurser_start
, NULL
);
433 g_main_loop_run (main_loop
);
434 g_main_loop_unref (main_loop
);