|
1 /* Portion Copyright © 2008-09 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.*/ |
|
2 #undef G_DISABLE_ASSERT |
|
3 #undef G_LOG_DOMAIN |
|
4 |
|
5 #include "config.h" |
|
6 |
|
7 #include <glib.h> |
|
8 |
|
9 #ifdef __SYMBIAN32__ |
|
10 #include <glib_global.h> |
|
11 #include "mrt2_glib2_test.h" |
|
12 #endif /*__SYMBIAN32__*/ |
|
13 #define DEBUG_MSG(x) |
|
14 /* #define DEBUG_MSG(args) g_printerr args ; g_printerr ("\n"); */ |
|
15 |
|
16 #define WAIT 5 /* seconds */ |
|
17 #define MAX_THREADS 10 |
|
18 |
|
19 /* if > 0 the test will run continously (since the test ends when |
|
20 * thread count is 0), if -1 it means no limit to unused threads |
|
21 * if 0 then no unused threads are possible */ |
|
22 #define MAX_UNUSED_THREADS -1 |
|
23 |
|
24 G_LOCK_DEFINE_STATIC (thread_counter_pools); |
|
25 |
|
26 static gulong abs_thread_counter = 0; |
|
27 static gulong running_thread_counter = 0; |
|
28 static gulong leftover_task_counter = 0; |
|
29 |
|
30 G_LOCK_DEFINE_STATIC (last_thread); |
|
31 |
|
32 static guint last_thread_id = 0; |
|
33 |
|
34 G_LOCK_DEFINE_STATIC (thread_counter_sort); |
|
35 |
|
36 static gulong sort_thread_counter = 0; |
|
37 |
|
38 static GThreadPool *idle_pool = NULL; |
|
39 |
|
40 static GMainLoop *main_loop = NULL; |
|
41 |
|
42 static void |
|
43 test_thread_functions (void) |
|
44 { |
|
45 gint max_unused_threads; |
|
46 guint max_idle_time; |
|
47 |
|
48 /* This function attempts to call functions which don't need a |
|
49 * threadpool to operate to make sure no uninitialised pointers |
|
50 * accessed and no errors occur. |
|
51 */ |
|
52 |
|
53 max_unused_threads = 3; |
|
54 |
|
55 DEBUG_MSG (("[funcs] Setting max unused threads to %d", |
|
56 max_unused_threads)); |
|
57 g_thread_pool_set_max_unused_threads (max_unused_threads); |
|
58 |
|
59 DEBUG_MSG (("[funcs] Getting max unused threads = %d", |
|
60 g_thread_pool_get_max_unused_threads ())); |
|
61 g_assert (g_thread_pool_get_max_unused_threads() == max_unused_threads); |
|
62 |
|
63 DEBUG_MSG (("[funcs] Getting num unused threads = %d", |
|
64 g_thread_pool_get_num_unused_threads ())); |
|
65 g_assert (g_thread_pool_get_num_unused_threads () == 0); |
|
66 |
|
67 DEBUG_MSG (("[funcs] Stopping unused threads")); |
|
68 g_thread_pool_stop_unused_threads (); |
|
69 |
|
70 max_idle_time = 10 * G_USEC_PER_SEC; |
|
71 |
|
72 DEBUG_MSG (("[funcs] Setting max idle time to %d", |
|
73 max_idle_time)); |
|
74 g_thread_pool_set_max_idle_time (max_idle_time); |
|
75 |
|
76 DEBUG_MSG (("[funcs] Getting max idle time = %d", |
|
77 g_thread_pool_get_max_idle_time ())); |
|
78 g_assert (g_thread_pool_get_max_idle_time () == max_idle_time); |
|
79 |
|
80 DEBUG_MSG (("[funcs] Setting max idle time to 0")); |
|
81 g_thread_pool_set_max_idle_time (0); |
|
82 |
|
83 DEBUG_MSG (("[funcs] Getting max idle time = %d", |
|
84 g_thread_pool_get_max_idle_time ())); |
|
85 g_assert (g_thread_pool_get_max_idle_time () == 0); |
|
86 } |
|
87 |
|
88 static void |
|
89 test_count_threads_foreach (GThread *thread, |
|
90 guint *count) |
|
91 { |
|
92 ++*count; |
|
93 } |
|
94 |
|
95 static guint |
|
96 test_count_threads (void) |
|
97 { |
|
98 guint count = 0; |
|
99 |
|
100 g_thread_foreach ((GFunc) test_count_threads_foreach, &count); |
|
101 |
|
102 /* Exclude main thread */ |
|
103 return count - 1; |
|
104 } |
|
105 |
|
106 static void |
|
107 test_thread_stop_unused (void) |
|
108 { |
|
109 GThreadPool *pool; |
|
110 guint i; |
|
111 guint limit = 100; |
|
112 |
|
113 /* Spawn a few threads. */ |
|
114 g_thread_pool_set_max_unused_threads (-1); |
|
115 pool = g_thread_pool_new ((GFunc) g_usleep, NULL, -1, FALSE, NULL); |
|
116 |
|
117 for (i = 0; i < limit; i++) |
|
118 g_thread_pool_push (pool, GUINT_TO_POINTER (1000), NULL); |
|
119 |
|
120 DEBUG_MSG (("[unused] ===> pushed %d threads onto the idle pool", |
|
121 limit)); |
|
122 |
|
123 /* Wait for the threads to migrate. */ |
|
124 g_usleep (G_USEC_PER_SEC); |
|
125 |
|
126 DEBUG_MSG (("[unused] current threads %d", |
|
127 test_count_threads())); |
|
128 |
|
129 DEBUG_MSG (("[unused] stopping unused threads")); |
|
130 g_thread_pool_stop_unused_threads (); |
|
131 |
|
132 DEBUG_MSG (("[unused] waiting ONE second for threads to die")); |
|
133 |
|
134 /* Some time for threads to die. */ |
|
135 g_usleep (G_USEC_PER_SEC); |
|
136 |
|
137 DEBUG_MSG (("[unused] stopped idle threads, %d remain, %d threads still exist", |
|
138 g_thread_pool_get_num_unused_threads (), |
|
139 test_count_threads ())); |
|
140 |
|
141 g_assert (g_thread_pool_get_num_unused_threads () == test_count_threads ()); |
|
142 g_assert (g_thread_pool_get_num_unused_threads () == 0); |
|
143 |
|
144 g_thread_pool_set_max_unused_threads (MAX_THREADS); |
|
145 |
|
146 DEBUG_MSG (("[unused] cleaning up thread pool")); |
|
147 g_thread_pool_free (pool, FALSE, TRUE); |
|
148 } |
|
149 |
|
150 static void |
|
151 test_thread_pools_entry_func (gpointer data, gpointer user_data) |
|
152 { |
|
153 guint id = 0; |
|
154 |
|
155 id = GPOINTER_TO_UINT (data); |
|
156 |
|
157 DEBUG_MSG (("[pool] ---> [%3.3d] entered thread.", id)); |
|
158 |
|
159 G_LOCK (thread_counter_pools); |
|
160 abs_thread_counter++; |
|
161 running_thread_counter++; |
|
162 G_UNLOCK (thread_counter_pools); |
|
163 |
|
164 g_usleep (g_random_int_range (0, 4000)); |
|
165 |
|
166 G_LOCK (thread_counter_pools); |
|
167 running_thread_counter--; |
|
168 leftover_task_counter--; |
|
169 |
|
170 DEBUG_MSG (("[pool] ---> [%3.3d] exiting thread (abs count:%ld, " |
|
171 "running count:%ld, left over:%ld)", |
|
172 id, abs_thread_counter, |
|
173 running_thread_counter, leftover_task_counter)); |
|
174 G_UNLOCK (thread_counter_pools); |
|
175 } |
|
176 |
|
177 static void |
|
178 test_thread_pools (void) |
|
179 { |
|
180 GThreadPool *pool1, *pool2, *pool3; |
|
181 guint runs; |
|
182 guint i; |
|
183 |
|
184 pool1 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 3, FALSE, NULL); |
|
185 pool2 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 5, TRUE, NULL); |
|
186 pool3 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 7, TRUE, NULL); |
|
187 |
|
188 runs = 300; |
|
189 for (i = 0; i < runs; i++) |
|
190 { |
|
191 g_thread_pool_push (pool1, GUINT_TO_POINTER (i + 1), NULL); |
|
192 g_thread_pool_push (pool2, GUINT_TO_POINTER (i + 1), NULL); |
|
193 g_thread_pool_push (pool3, GUINT_TO_POINTER (i + 1), NULL); |
|
194 |
|
195 G_LOCK (thread_counter_pools); |
|
196 leftover_task_counter += 3; |
|
197 G_UNLOCK (thread_counter_pools); |
|
198 } |
|
199 |
|
200 g_thread_pool_free (pool1, TRUE, TRUE); |
|
201 g_thread_pool_free (pool2, FALSE, TRUE); |
|
202 g_thread_pool_free (pool3, FALSE, TRUE); |
|
203 |
|
204 g_assert (runs * 3 == abs_thread_counter + leftover_task_counter); |
|
205 g_assert (running_thread_counter == 0); |
|
206 } |
|
207 |
|
208 static gint |
|
209 test_thread_sort_compare_func (gconstpointer a, gconstpointer b, gpointer user_data) |
|
210 { |
|
211 guint32 id1, id2; |
|
212 |
|
213 id1 = GPOINTER_TO_UINT (a); |
|
214 id2 = GPOINTER_TO_UINT (b); |
|
215 |
|
216 return (id1 > id2 ? +1 : id1 == id2 ? 0 : -1); |
|
217 } |
|
218 |
|
219 static void |
|
220 test_thread_sort_entry_func (gpointer data, gpointer user_data) |
|
221 { |
|
222 guint thread_id; |
|
223 gboolean is_sorted; |
|
224 |
|
225 G_LOCK (last_thread); |
|
226 |
|
227 thread_id = GPOINTER_TO_UINT (data); |
|
228 is_sorted = GPOINTER_TO_INT (user_data); |
|
229 |
|
230 DEBUG_MSG (("%s ---> entered thread:%2.2d, last thread:%2.2d", |
|
231 is_sorted ? "[ sorted]" : "[unsorted]", |
|
232 thread_id, last_thread_id)); |
|
233 |
|
234 if (is_sorted) { |
|
235 static gboolean last_failed = FALSE; |
|
236 |
|
237 if (last_thread_id > thread_id) { |
|
238 if (last_failed) { |
|
239 g_assert (last_thread_id <= thread_id); |
|
240 } |
|
241 |
|
242 /* Here we remember one fail and if it concurrently fails, it |
|
243 * can not be sorted. the last thread id might be < this thread |
|
244 * id if something is added to the queue since threads were |
|
245 * created |
|
246 */ |
|
247 last_failed = TRUE; |
|
248 } else { |
|
249 last_failed = FALSE; |
|
250 } |
|
251 |
|
252 last_thread_id = thread_id; |
|
253 } |
|
254 |
|
255 G_UNLOCK (last_thread); |
|
256 |
|
257 g_usleep (WAIT * 1000); |
|
258 } |
|
259 |
|
260 static void |
|
261 test_thread_sort (gboolean sort) |
|
262 { |
|
263 GThreadPool *pool; |
|
264 guint limit; |
|
265 guint max_threads; |
|
266 gint i; |
|
267 |
|
268 limit = MAX_THREADS * 10; |
|
269 |
|
270 if (sort) { |
|
271 max_threads = 1; |
|
272 } else { |
|
273 max_threads = MAX_THREADS; |
|
274 } |
|
275 |
|
276 /* It is important that we only have a maximum of 1 thread for this |
|
277 * test since the results can not be guranteed to be sorted if > 1. |
|
278 * |
|
279 * Threads are scheduled by the operating system and are executed at |
|
280 * random. It cannot be assumed that threads are executed in the |
|
281 * order they are created. This was discussed in bug #334943. |
|
282 */ |
|
283 |
|
284 pool = g_thread_pool_new (test_thread_sort_entry_func, |
|
285 GINT_TO_POINTER (sort), |
|
286 max_threads, |
|
287 FALSE, |
|
288 NULL); |
|
289 |
|
290 g_thread_pool_set_max_unused_threads (MAX_UNUSED_THREADS); |
|
291 |
|
292 if (sort) { |
|
293 g_thread_pool_set_sort_function (pool, |
|
294 test_thread_sort_compare_func, |
|
295 GUINT_TO_POINTER (69)); |
|
296 } |
|
297 |
|
298 for (i = 0; i < limit; i++) { |
|
299 guint id; |
|
300 |
|
301 id = g_random_int_range (1, limit) + 1; |
|
302 g_thread_pool_push (pool, GUINT_TO_POINTER (id), NULL); |
|
303 DEBUG_MSG (("%s ===> pushed new thread with id:%d, number " |
|
304 "of threads:%d, unprocessed:%d", |
|
305 sort ? "[ sorted]" : "[unsorted]", |
|
306 id, |
|
307 g_thread_pool_get_num_threads (pool), |
|
308 g_thread_pool_unprocessed (pool))); |
|
309 } |
|
310 |
|
311 g_assert (g_thread_pool_get_max_threads (pool) == max_threads); |
|
312 g_assert (g_thread_pool_get_num_threads (pool) == g_thread_pool_get_max_threads (pool)); |
|
313 } |
|
314 |
|
315 static void |
|
316 test_thread_idle_time_entry_func (gpointer data, gpointer user_data) |
|
317 { |
|
318 guint thread_id; |
|
319 |
|
320 thread_id = GPOINTER_TO_UINT (data); |
|
321 |
|
322 DEBUG_MSG (("[idle] ---> entered thread:%2.2d", thread_id)); |
|
323 |
|
324 g_usleep (WAIT * 1000); |
|
325 |
|
326 DEBUG_MSG (("[idle] <--- exiting thread:%2.2d", thread_id)); |
|
327 } |
|
328 |
|
329 static gboolean |
|
330 test_thread_idle_timeout (gpointer data) |
|
331 { |
|
332 guint interval; |
|
333 gint i; |
|
334 |
|
335 interval = GPOINTER_TO_UINT (data); |
|
336 |
|
337 for (i = 0; i < 2; i++) { |
|
338 g_thread_pool_push (idle_pool, GUINT_TO_POINTER (100 + i), NULL); |
|
339 DEBUG_MSG (("[idle] ===> pushed new thread with id:%d, number " |
|
340 "of threads:%d, unprocessed:%d", |
|
341 100 + i, |
|
342 g_thread_pool_get_num_threads (idle_pool), |
|
343 g_thread_pool_unprocessed (idle_pool))); |
|
344 } |
|
345 |
|
346 |
|
347 return FALSE; |
|
348 } |
|
349 |
|
350 static void |
|
351 test_thread_idle_time () |
|
352 { |
|
353 guint limit = 50; |
|
354 guint interval = 10000; |
|
355 gint i; |
|
356 |
|
357 idle_pool = g_thread_pool_new (test_thread_idle_time_entry_func, |
|
358 NULL, |
|
359 MAX_THREADS, |
|
360 FALSE, |
|
361 NULL); |
|
362 |
|
363 g_thread_pool_set_max_unused_threads (MAX_UNUSED_THREADS); |
|
364 g_thread_pool_set_max_idle_time (interval); |
|
365 |
|
366 g_assert (g_thread_pool_get_max_unused_threads () == MAX_UNUSED_THREADS); |
|
367 g_assert (g_thread_pool_get_max_idle_time () == interval); |
|
368 |
|
369 for (i = 0; i < limit; i++) { |
|
370 g_thread_pool_push (idle_pool, GUINT_TO_POINTER (i + 1), NULL); |
|
371 DEBUG_MSG (("[idle] ===> pushed new thread with id:%d, " |
|
372 "number of threads:%d, unprocessed:%d", |
|
373 i, |
|
374 g_thread_pool_get_num_threads (idle_pool), |
|
375 g_thread_pool_unprocessed (idle_pool))); |
|
376 } |
|
377 |
|
378 g_timeout_add ((interval - 1000), |
|
379 test_thread_idle_timeout, |
|
380 GUINT_TO_POINTER (interval)); |
|
381 } |
|
382 |
|
383 static gboolean |
|
384 test_check_start_and_stop (gpointer user_data) |
|
385 { |
|
386 static guint test_number = 0; |
|
387 static gboolean run_next = FALSE; |
|
388 gboolean continue_timeout = TRUE; |
|
389 gboolean quit = TRUE; |
|
390 |
|
391 if (test_number == 0) { |
|
392 run_next = TRUE; |
|
393 DEBUG_MSG (("***** RUNNING TEST %2.2d *****", test_number)); |
|
394 } |
|
395 |
|
396 if (run_next) { |
|
397 test_number++; |
|
398 |
|
399 switch (test_number) { |
|
400 case 1: |
|
401 test_thread_functions (); |
|
402 break; |
|
403 case 2: |
|
404 test_thread_stop_unused (); |
|
405 break; |
|
406 case 3: |
|
407 test_thread_pools (); |
|
408 break; |
|
409 case 4: |
|
410 test_thread_sort (FALSE); |
|
411 break; |
|
412 case 5: |
|
413 test_thread_sort (TRUE); |
|
414 break; |
|
415 case 6: |
|
416 test_thread_stop_unused (); |
|
417 break; |
|
418 case 7: |
|
419 test_thread_idle_time (); |
|
420 break; |
|
421 default: |
|
422 DEBUG_MSG (("***** END OF TESTS *****")); |
|
423 g_main_loop_quit (main_loop); |
|
424 continue_timeout = FALSE; |
|
425 break; |
|
426 } |
|
427 |
|
428 run_next = FALSE; |
|
429 return TRUE; |
|
430 } |
|
431 |
|
432 if (test_number == 3) { |
|
433 G_LOCK (thread_counter_pools); |
|
434 quit &= running_thread_counter <= 0; |
|
435 DEBUG_MSG (("***** POOL RUNNING THREAD COUNT:%ld", |
|
436 running_thread_counter)); |
|
437 G_UNLOCK (thread_counter_pools); |
|
438 } |
|
439 |
|
440 if (test_number == 4 || test_number == 5) { |
|
441 G_LOCK (thread_counter_sort); |
|
442 quit &= sort_thread_counter <= 0; |
|
443 DEBUG_MSG (("***** POOL SORT THREAD COUNT:%ld", |
|
444 sort_thread_counter)); |
|
445 G_UNLOCK (thread_counter_sort); |
|
446 } |
|
447 |
|
448 if (test_number == 7) { |
|
449 guint idle; |
|
450 |
|
451 idle = g_thread_pool_get_num_unused_threads (); |
|
452 quit &= idle < 1; |
|
453 DEBUG_MSG (("***** POOL IDLE THREAD COUNT:%d, UNPROCESSED JOBS:%d", |
|
454 idle, g_thread_pool_unprocessed (idle_pool))); |
|
455 } |
|
456 |
|
457 if (quit) { |
|
458 run_next = TRUE; |
|
459 } |
|
460 |
|
461 return continue_timeout; |
|
462 } |
|
463 |
|
464 int |
|
465 main (int argc, char *argv[]) |
|
466 { |
|
467 /* Only run the test, if threads are enabled and a default thread |
|
468 implementation is available */ |
|
469 #ifdef __SYMBIAN32__ |
|
470 guint g_thread_pool_unprocessed_op = -1; |
|
471 guint res; |
|
472 #endif //__SYMBIAN32__ |
|
473 |
|
474 #if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE) |
|
475 g_thread_init (NULL); |
|
476 |
|
477 DEBUG_MSG (("Starting... (in one second)")); |
|
478 g_timeout_add (1000, test_check_start_and_stop, NULL); |
|
479 |
|
480 main_loop = g_main_loop_new (NULL, FALSE); |
|
481 g_main_loop_run (main_loop); |
|
482 #endif |
|
483 #ifdef __SYMBIAN32__ |
|
484 testResultXml("threadpool-test"); |
|
485 #endif /* EMULATOR */ |
|
486 |
|
487 return 0; |
|
488 } |