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