|
1 /* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 #undef G_DISABLE_ASSERT |
|
18 #undef G_LOG_DOMAIN |
|
19 |
|
20 |
|
21 #include <errno.h> |
|
22 #include <glib.h> |
|
23 |
|
24 #ifdef G_OS_UNIX |
|
25 #include <unistd.h> |
|
26 #endif |
|
27 #include <stdio.h> |
|
28 #include <stdlib.h> |
|
29 |
|
30 #ifdef G_OS_WIN32 |
|
31 #include <fcntl.h> /* For _O_BINARY used by pipe() macro */ |
|
32 #include <io.h> /* for _pipe() */ |
|
33 #endif |
|
34 |
|
35 |
|
36 |
|
37 #ifdef SYMBIAN |
|
38 #include <glib_global.h> |
|
39 #include "mrt2_glib2_test.h" |
|
40 #endif /*SYMBIAN*/ |
|
41 |
|
42 |
|
43 #define ITERS 10 |
|
44 #define INCREMENT 10 |
|
45 #define NTHREADS 4 |
|
46 #define NCRAWLERS 4 |
|
47 #define CRAWLER_TIMEOUT_RANGE 40 |
|
48 #define RECURSER_TIMEOUT 50 |
|
49 |
|
50 /* The partial ordering between the context array mutex and |
|
51 * crawler array mutex is that the crawler array mutex cannot |
|
52 * be locked while the context array mutex is locked |
|
53 */ |
|
54 GPtrArray *context_array; |
|
55 GMutex *context_array_mutex; |
|
56 GCond *context_array_cond; |
|
57 |
|
58 GMainLoop *main_loop; |
|
59 |
|
60 G_LOCK_DEFINE_STATIC (crawler_array_lock); |
|
61 GPtrArray *crawler_array; |
|
62 |
|
63 typedef struct _AddrData AddrData; |
|
64 typedef struct _TestData TestData; |
|
65 |
|
66 struct _AddrData |
|
67 { |
|
68 GMainLoop *loop; |
|
69 GIOChannel *dest; |
|
70 gint count; |
|
71 }; |
|
72 |
|
73 struct _TestData |
|
74 { |
|
75 gint current_val; |
|
76 gint iters; |
|
77 GIOChannel *in; |
|
78 }; |
|
79 |
|
80 static void cleanup_crawlers (GMainContext *context); |
|
81 |
|
82 gboolean |
|
83 read_all (GIOChannel *channel, char *buf, gsize len) |
|
84 { |
|
85 gsize bytes_read = 0; |
|
86 gsize count; |
|
87 GIOError err; |
|
88 |
|
89 while (bytes_read < len) |
|
90 { |
|
91 err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count); |
|
92 if (err) |
|
93 { |
|
94 if (err != G_IO_ERROR_AGAIN) |
|
95 g_assert(FALSE && "mainloop-test failed"); |
|
96 return FALSE; |
|
97 } |
|
98 else if (count == 0) |
|
99 return FALSE; |
|
100 |
|
101 bytes_read += count; |
|
102 } |
|
103 |
|
104 return TRUE; |
|
105 } |
|
106 |
|
107 gboolean |
|
108 write_all (GIOChannel *channel, char *buf, gsize len) |
|
109 { |
|
110 gsize bytes_written = 0; |
|
111 gsize count; |
|
112 GIOError err; |
|
113 |
|
114 while (bytes_written < len) |
|
115 { |
|
116 err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count); |
|
117 if (err && err != G_IO_ERROR_AGAIN) |
|
118 return FALSE; |
|
119 |
|
120 bytes_written += count; |
|
121 } |
|
122 |
|
123 return TRUE; |
|
124 } |
|
125 |
|
126 gboolean |
|
127 adder_callback (GIOChannel *source, |
|
128 GIOCondition condition, |
|
129 gpointer data) |
|
130 { |
|
131 char buf1[32]; |
|
132 char buf2[32]; |
|
133 |
|
134 char result[32]; |
|
135 |
|
136 AddrData *addr_data = data; |
|
137 |
|
138 if (!read_all (source, buf1, 32) || |
|
139 !read_all (source, buf2, 32)) |
|
140 { |
|
141 g_main_loop_quit (addr_data->loop); |
|
142 return FALSE; |
|
143 } |
|
144 |
|
145 sprintf (result, "%d", atoi(buf1) + atoi(buf2)); |
|
146 write_all (addr_data->dest, result, 32); |
|
147 |
|
148 return TRUE; |
|
149 } |
|
150 |
|
151 gboolean |
|
152 timeout_callback (gpointer data) |
|
153 { |
|
154 AddrData *addr_data = data; |
|
155 |
|
156 addr_data->count++; |
|
157 |
|
158 return TRUE; |
|
159 } |
|
160 |
|
161 gpointer |
|
162 adder_thread (gpointer data) |
|
163 { |
|
164 GMainContext *context; |
|
165 GSource *adder_source; |
|
166 GSource *timeout_source; |
|
167 |
|
168 GIOChannel **channels = data; |
|
169 AddrData addr_data; |
|
170 |
|
171 context = g_main_context_new (); |
|
172 |
|
173 g_assert(context != NULL); |
|
174 |
|
175 g_mutex_lock (context_array_mutex); |
|
176 |
|
177 g_ptr_array_add (context_array, context); |
|
178 |
|
179 if (context_array->len == NTHREADS) |
|
180 g_cond_broadcast (context_array_cond); |
|
181 |
|
182 g_mutex_unlock (context_array_mutex); |
|
183 |
|
184 addr_data.dest = channels[1]; |
|
185 addr_data.loop = g_main_loop_new (context, FALSE); |
|
186 addr_data.count = 0; |
|
187 |
|
188 adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP); |
|
189 |
|
190 g_assert(adder_source != NULL); |
|
191 |
|
192 g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL); |
|
193 g_source_attach (adder_source, context); |
|
194 g_source_unref (adder_source); |
|
195 |
|
196 timeout_source = g_timeout_source_new (10); |
|
197 |
|
198 g_assert(timeout_source != NULL); |
|
199 |
|
200 g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL); |
|
201 g_source_set_priority (timeout_source, G_PRIORITY_HIGH); |
|
202 g_source_attach (timeout_source, context); |
|
203 g_source_unref (timeout_source); |
|
204 |
|
205 g_main_loop_run (addr_data.loop); |
|
206 |
|
207 g_io_channel_unref (channels[0]); |
|
208 g_io_channel_unref (channels[1]); |
|
209 |
|
210 g_free (channels); |
|
211 |
|
212 g_main_loop_unref (addr_data.loop); |
|
213 |
|
214 #ifdef VERBOSE |
|
215 g_print ("Timeout run %d times\n", addr_data.count); |
|
216 #endif |
|
217 |
|
218 g_mutex_lock (context_array_mutex); |
|
219 g_ptr_array_remove (context_array, context); |
|
220 if (context_array->len == 0) |
|
221 g_main_loop_quit (main_loop); |
|
222 g_mutex_unlock (context_array_mutex); |
|
223 |
|
224 cleanup_crawlers (context); |
|
225 |
|
226 return NULL; |
|
227 } |
|
228 |
|
229 |
|
230 void |
|
231 io_pipe (GIOChannel **channels) |
|
232 { |
|
233 gint fds[2]; |
|
234 |
|
235 if (pipe(fds) < 0) |
|
236 { |
|
237 g_warning ("Cannot create pipe %s\n", g_strerror (errno)); |
|
238 |
|
239 g_assert(FALSE && "mainloop-test failed"); |
|
240 |
|
241 exit (1); |
|
242 } |
|
243 |
|
244 channels[0] = g_io_channel_unix_new (fds[0]); |
|
245 channels[1] = g_io_channel_unix_new (fds[1]); |
|
246 |
|
247 g_io_channel_set_close_on_unref (channels[0], TRUE); |
|
248 g_io_channel_set_close_on_unref (channels[1], TRUE); |
|
249 } |
|
250 |
|
251 void |
|
252 do_add (GIOChannel *in, gint a, gint b) |
|
253 { |
|
254 char buf1[32]; |
|
255 char buf2[32]; |
|
256 |
|
257 sprintf (buf1, "%d", a); |
|
258 sprintf (buf2, "%d", b); |
|
259 |
|
260 write_all (in, buf1, 32); |
|
261 write_all (in, buf2, 32); |
|
262 } |
|
263 |
|
264 gboolean |
|
265 adder_response (GIOChannel *source, |
|
266 GIOCondition condition, |
|
267 gpointer data) |
|
268 { |
|
269 char result[32]; |
|
270 TestData *test_data = data; |
|
271 |
|
272 if (!read_all (source, result, 32)) |
|
273 return FALSE; |
|
274 |
|
275 test_data->current_val = atoi (result); |
|
276 test_data->iters--; |
|
277 |
|
278 if (test_data->iters == 0) |
|
279 { |
|
280 if (test_data->current_val != ITERS * INCREMENT) |
|
281 { |
|
282 g_print ("Addition failed: %d != %d\n", |
|
283 test_data->current_val, ITERS * INCREMENT); |
|
284 |
|
285 g_assert(FALSE && "mainloop-test failed"); |
|
286 |
|
287 exit (1); |
|
288 } |
|
289 |
|
290 g_io_channel_unref (source); |
|
291 g_io_channel_unref (test_data->in); |
|
292 |
|
293 g_free (test_data); |
|
294 |
|
295 return FALSE; |
|
296 } |
|
297 |
|
298 do_add (test_data->in, test_data->current_val, INCREMENT); |
|
299 |
|
300 return TRUE; |
|
301 } |
|
302 |
|
303 void |
|
304 create_adder_thread (void) |
|
305 { |
|
306 GError *err = NULL; |
|
307 TestData *test_data; |
|
308 GThread *thread; |
|
309 |
|
310 GIOChannel *in_channels[2]; |
|
311 GIOChannel *out_channels[2]; |
|
312 |
|
313 GIOChannel **sub_channels; |
|
314 |
|
315 sub_channels = g_new (GIOChannel *, 2); |
|
316 |
|
317 io_pipe (in_channels); |
|
318 io_pipe (out_channels); |
|
319 |
|
320 sub_channels[0] = in_channels[0]; |
|
321 sub_channels[1] = out_channels[1]; |
|
322 |
|
323 g_thread_create (adder_thread, sub_channels, FALSE, &err); |
|
324 |
|
325 if (err) |
|
326 { |
|
327 g_warning ("Cannot create thread: %s", err->message); |
|
328 |
|
329 g_assert(FALSE && "mainloop-test failed"); |
|
330 |
|
331 exit (1); |
|
332 } |
|
333 |
|
334 test_data = g_new (TestData, 1); |
|
335 test_data->in = in_channels[1]; |
|
336 test_data->current_val = 0; |
|
337 test_data->iters = ITERS; |
|
338 |
|
339 g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP, |
|
340 adder_response, test_data); |
|
341 |
|
342 do_add (test_data->in, test_data->current_val, INCREMENT); |
|
343 } |
|
344 |
|
345 static void create_crawler (void); |
|
346 |
|
347 static void |
|
348 remove_crawler (void) |
|
349 { |
|
350 GSource *other_source; |
|
351 |
|
352 if (crawler_array->len > 0) |
|
353 { |
|
354 other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)]; |
|
355 g_source_destroy (other_source); |
|
356 g_assert (g_ptr_array_remove_fast (crawler_array, other_source)); |
|
357 } |
|
358 } |
|
359 |
|
360 static gint |
|
361 crawler_callback (gpointer data) |
|
362 { |
|
363 GSource *source = data; |
|
364 |
|
365 G_LOCK (crawler_array_lock); |
|
366 |
|
367 if (!g_ptr_array_remove_fast (crawler_array, source)) |
|
368 remove_crawler(); |
|
369 |
|
370 remove_crawler(); |
|
371 G_UNLOCK (crawler_array_lock); |
|
372 |
|
373 create_crawler(); |
|
374 //create_crawler(); |
|
375 |
|
376 return FALSE; |
|
377 } |
|
378 |
|
379 static void |
|
380 create_crawler (void) |
|
381 { |
|
382 GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE)); |
|
383 |
|
384 g_assert(source != NULL); |
|
385 |
|
386 g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL); |
|
387 |
|
388 G_LOCK (crawler_array_lock); |
|
389 g_ptr_array_add (crawler_array, source); |
|
390 |
|
391 g_mutex_lock (context_array_mutex); |
|
392 if(context_array->len == 0) |
|
393 g_source_attach (source, context_array->pdata[0]); |
|
394 else |
|
395 g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]); |
|
396 g_source_unref (source); |
|
397 g_mutex_unlock (context_array_mutex); |
|
398 |
|
399 G_UNLOCK (crawler_array_lock); |
|
400 } |
|
401 |
|
402 static void |
|
403 cleanup_crawlers (GMainContext *context) |
|
404 { |
|
405 gint i; |
|
406 |
|
407 G_LOCK (crawler_array_lock); |
|
408 for (i=0; i < crawler_array->len; i++) |
|
409 { |
|
410 if (g_source_get_context (crawler_array->pdata[i]) == context) |
|
411 { |
|
412 g_source_destroy (g_ptr_array_remove_index (crawler_array, i)); |
|
413 i--; |
|
414 } |
|
415 } |
|
416 G_UNLOCK (crawler_array_lock); |
|
417 } |
|
418 |
|
419 static gboolean |
|
420 recurser_idle (gpointer data) |
|
421 { |
|
422 GMainContext *context = data; |
|
423 gint i; |
|
424 |
|
425 for (i = 0; i < 10; i++) |
|
426 g_main_context_iteration (context, FALSE); |
|
427 |
|
428 return FALSE; |
|
429 } |
|
430 |
|
431 static gboolean |
|
432 recurser_start (gpointer data) |
|
433 { |
|
434 GMainContext *context; |
|
435 GSource *source; |
|
436 |
|
437 g_mutex_lock (context_array_mutex); |
|
438 |
|
439 if(context_array->len == 0) |
|
440 context = context_array->pdata[0]; |
|
441 else |
|
442 context = context_array->pdata[g_random_int_range (0, context_array->len)]; |
|
443 source = g_idle_source_new (); |
|
444 g_source_set_callback (source, recurser_idle, context, NULL); |
|
445 g_source_attach (source, context); |
|
446 g_source_unref (source); |
|
447 g_mutex_unlock (context_array_mutex); |
|
448 |
|
449 return TRUE; |
|
450 } |
|
451 |
|
452 int |
|
453 main (int argc, |
|
454 char *argv[]) |
|
455 { |
|
456 /* Only run the test, if threads are enabled and a default thread |
|
457 implementation is available */ |
|
458 #if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE) |
|
459 gint i; |
|
460 |
|
461 #ifdef SYMBIAN |
|
462 g_log_set_handler (NULL, G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG, &mrtLogHandler, NULL); |
|
463 g_set_print_handler(mrtPrintHandler); |
|
464 #endif /*SYMBIAN*/ |
|
465 #if 0 |
|
466 g_thread_init (NULL); |
|
467 |
|
468 context_array = g_ptr_array_new (); |
|
469 |
|
470 g_assert(context_array != NULL); |
|
471 |
|
472 context_array_mutex = g_mutex_new (); |
|
473 |
|
474 g_assert(context_array_mutex != NULL); |
|
475 |
|
476 context_array_cond = g_cond_new (); |
|
477 |
|
478 g_assert(context_array_cond != NULL); |
|
479 |
|
480 crawler_array = g_ptr_array_new (); |
|
481 |
|
482 g_assert(crawler_array != NULL); |
|
483 |
|
484 main_loop = g_main_loop_new (NULL, FALSE); |
|
485 |
|
486 g_assert(main_loop != NULL); |
|
487 |
|
488 for (i = 0; i < NTHREADS; i++) |
|
489 create_adder_thread (); |
|
490 |
|
491 /* Wait for all threads to start |
|
492 */ |
|
493 g_mutex_lock (context_array_mutex); |
|
494 |
|
495 if (context_array->len < NTHREADS) |
|
496 g_cond_wait (context_array_cond, context_array_mutex); |
|
497 |
|
498 g_mutex_unlock (context_array_mutex); |
|
499 |
|
500 for (i = 0; i < NCRAWLERS; i++) |
|
501 create_crawler (); |
|
502 |
|
503 g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL); |
|
504 |
|
505 g_main_loop_run (main_loop); |
|
506 g_main_loop_unref (main_loop); |
|
507 #endif |
|
508 #endif |
|
509 |
|
510 #ifdef SYMBIAN |
|
511 testResultXml("mainloop-test"); |
|
512 #endif /* EMULATOR */ |
|
513 |
|
514 return 0; |
|
515 } |