thread.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /*
  2. * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
  3. * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. */
  19. #include "test.h"
  20. /**
  21. * \page page_pjlib_thread_test Test: Thread Test
  22. *
  23. * This file contains \a thread_test() definition.
  24. *
  25. * \section thread_test_scope_sec Scope of Test
  26. * This tests:
  27. * - whether PJ_THREAD_SUSPENDED flag works.
  28. * - whether multithreading works.
  29. * - whether thread timeslicing works, and threads have equal
  30. * time-slice proportion.
  31. *
  32. * APIs tested:
  33. * - pj_thread_create()
  34. * - pj_thread_register()
  35. * - pj_thread_this()
  36. * - pj_thread_get_name()
  37. * - pj_thread_destroy()
  38. * - pj_thread_resume()
  39. * - pj_thread_sleep()
  40. * - pj_thread_join()
  41. * - pj_thread_destroy()
  42. *
  43. *
  44. * This file is <b>pjlib-test/thread.c</b>
  45. *
  46. * \include pjlib-test/thread.c
  47. */
  48. #if INCLUDE_THREAD_TEST
  49. #include <pjlib.h>
  50. #define THIS_FILE "thread_test"
  51. static volatile int quit_flag=0;
  52. #if 0
  53. # define TRACE__(args) PJ_LOG(3,args)
  54. #else
  55. # define TRACE__(args)
  56. #endif
  57. /*
  58. * The thread's entry point.
  59. *
  60. * Each of the thread mainly will just execute the loop which
  61. * increments a variable.
  62. */
  63. static int thread_proc(void *data)
  64. {
  65. /* Test that pj_thread_register() works. */
  66. pj_thread_desc desc;
  67. pj_thread_t *this_thread;
  68. unsigned id;
  69. pj_status_t rc;
  70. pj_uint32_t *pcounter = (pj_uint32_t *)data;
  71. id = *pcounter;
  72. PJ_UNUSED_ARG(id); /* Warning about unused var if TRACE__ is disabled */
  73. TRACE__((THIS_FILE, " thread %d running..", id));
  74. pj_bzero(desc, sizeof(desc));
  75. rc = pj_thread_register("thread", desc, &this_thread);
  76. if (rc != PJ_SUCCESS) {
  77. app_perror("...error in pj_thread_register", rc);
  78. return rc;
  79. }
  80. /* Test that pj_thread_this() works */
  81. this_thread = pj_thread_this();
  82. if (this_thread == NULL) {
  83. PJ_LOG(3,(THIS_FILE, "...error: pj_thread_this() returns NULL!"));
  84. return -1;
  85. }
  86. /* Test that pj_thread_get_name() works */
  87. if (pj_thread_get_name(this_thread) == NULL) {
  88. PJ_LOG(3,(THIS_FILE, "...error: pj_thread_get_name() returns NULL!"));
  89. return -1;
  90. }
  91. /* Main loop */
  92. for (;!quit_flag;) {
  93. (*pcounter)++;
  94. //Must sleep if platform doesn't do time-slicing.
  95. //pj_thread_sleep(0);
  96. }
  97. TRACE__((THIS_FILE, " thread %d quitting..", id));
  98. return PJ_SUCCESS;
  99. }
  100. /*
  101. * simple_thread()
  102. */
  103. static int simple_thread(const char *title, unsigned flags)
  104. {
  105. pj_pool_t *pool;
  106. pj_thread_t *thread;
  107. pj_status_t rc;
  108. pj_uint32_t counter = 0;
  109. PJ_LOG(3,(THIS_FILE, "..%s", title));
  110. pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
  111. if (!pool)
  112. return -1000;
  113. quit_flag = 0;
  114. TRACE__((THIS_FILE, " Creating thread 0.."));
  115. rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
  116. &counter,
  117. PJ_THREAD_DEFAULT_STACK_SIZE,
  118. flags,
  119. &thread);
  120. if (rc != PJ_SUCCESS) {
  121. app_perror("...error: unable to create thread", rc);
  122. return -1010;
  123. }
  124. TRACE__((THIS_FILE, " Main thread waiting.."));
  125. pj_thread_sleep(1500);
  126. TRACE__((THIS_FILE, " Main thread resuming.."));
  127. if (flags & PJ_THREAD_SUSPENDED) {
  128. /* Check that counter is still zero */
  129. if (counter != 0) {
  130. PJ_LOG(3,(THIS_FILE, "...error: thread is not suspended"));
  131. return -1015;
  132. }
  133. rc = pj_thread_resume(thread);
  134. if (rc != PJ_SUCCESS) {
  135. app_perror("...error: resume thread error", rc);
  136. return -1020;
  137. }
  138. }
  139. PJ_LOG(3,(THIS_FILE, "..waiting for thread to quit.."));
  140. pj_thread_sleep(1500);
  141. quit_flag = 1;
  142. pj_thread_join(thread);
  143. pj_pool_release(pool);
  144. if (counter == 0) {
  145. PJ_LOG(3,(THIS_FILE, "...error: thread is not running"));
  146. return -1025;
  147. }
  148. PJ_LOG(3,(THIS_FILE, "...%s success", title));
  149. return PJ_SUCCESS;
  150. }
  151. /*
  152. * timeslice_test()
  153. */
  154. static int timeslice_test(void)
  155. {
  156. enum { NUM_THREADS = 4 };
  157. pj_pool_t *pool;
  158. pj_uint32_t counter[NUM_THREADS], lowest, highest, diff;
  159. pj_thread_t *thread[NUM_THREADS];
  160. unsigned i;
  161. pj_status_t rc;
  162. quit_flag = 0;
  163. pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
  164. if (!pool)
  165. return -10;
  166. PJ_LOG(3,(THIS_FILE, "..timeslice testing with %d threads", NUM_THREADS));
  167. /* Create all threads in suspended mode. */
  168. for (i=0; i<NUM_THREADS; ++i) {
  169. counter[i] = i;
  170. rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
  171. &counter[i],
  172. PJ_THREAD_DEFAULT_STACK_SIZE,
  173. PJ_THREAD_SUSPENDED,
  174. &thread[i]);
  175. if (rc!=PJ_SUCCESS) {
  176. app_perror("...ERROR in pj_thread_create()", rc);
  177. return -20;
  178. }
  179. }
  180. /* Sleep for 1 second.
  181. * The purpose of this is to test whether all threads are suspended.
  182. */
  183. TRACE__((THIS_FILE, " Main thread waiting.."));
  184. pj_thread_sleep(1000);
  185. TRACE__((THIS_FILE, " Main thread resuming.."));
  186. /* Check that all counters are still zero. */
  187. for (i=0; i<NUM_THREADS; ++i) {
  188. if (counter[i] > i) {
  189. PJ_LOG(3,(THIS_FILE, "....ERROR! Thread %d-th is not suspended!",
  190. i));
  191. return -30;
  192. }
  193. }
  194. /* Now resume all threads. */
  195. for (i=0; i<NUM_THREADS; ++i) {
  196. TRACE__((THIS_FILE, " Resuming thread %d [%p]..", i, thread[i]));
  197. rc = pj_thread_resume(thread[i]);
  198. if (rc != PJ_SUCCESS) {
  199. app_perror("...ERROR in pj_thread_resume()", rc);
  200. return -40;
  201. }
  202. }
  203. /* Main thread sleeps for some time to allow threads to run.
  204. * The longer we sleep, the more accurate the calculation will be,
  205. * but it'll make user waits for longer for the test to finish.
  206. */
  207. TRACE__((THIS_FILE, " Main thread waiting (5s).."));
  208. pj_thread_sleep(5000);
  209. TRACE__((THIS_FILE, " Main thread resuming.."));
  210. /* Signal all threads to quit. */
  211. quit_flag = 1;
  212. /* Wait until all threads quit, then destroy. */
  213. for (i=0; i<NUM_THREADS; ++i) {
  214. TRACE__((THIS_FILE, " Main thread joining thread %d [%p]..",
  215. i, thread[i]));
  216. rc = pj_thread_join(thread[i]);
  217. if (rc != PJ_SUCCESS) {
  218. app_perror("...ERROR in pj_thread_join()", rc);
  219. return -50;
  220. }
  221. TRACE__((THIS_FILE, " Destroying thread %d [%p]..", i, thread[i]));
  222. rc = pj_thread_destroy(thread[i]);
  223. if (rc != PJ_SUCCESS) {
  224. app_perror("...ERROR in pj_thread_destroy()", rc);
  225. return -60;
  226. }
  227. }
  228. TRACE__((THIS_FILE, " Main thread calculating time slices.."));
  229. /* Now examine the value of the counters.
  230. * Check that all threads had equal proportion of processing.
  231. */
  232. lowest = 0xFFFFFFFF;
  233. highest = 0;
  234. for (i=0; i<NUM_THREADS; ++i) {
  235. if (counter[i] < lowest)
  236. lowest = counter[i];
  237. if (counter[i] > highest)
  238. highest = counter[i];
  239. }
  240. /* Check that all threads are running. */
  241. if (lowest < 2) {
  242. PJ_LOG(3,(THIS_FILE, "...ERROR: not all threads were running!"));
  243. return -70;
  244. }
  245. /* The difference between lowest and higest should be lower than 50%.
  246. */
  247. diff = (highest-lowest)*100 / ((highest+lowest)/2);
  248. if ( diff >= 50) {
  249. PJ_LOG(3,(THIS_FILE,
  250. "...ERROR: thread didn't have equal timeslice!"));
  251. PJ_LOG(3,(THIS_FILE,
  252. ".....lowest counter=%u, highest counter=%u, diff=%u%%",
  253. lowest, highest, diff));
  254. return -80;
  255. } else {
  256. PJ_LOG(3,(THIS_FILE,
  257. "...info: timeslice diff between lowest & highest=%u%%",
  258. diff));
  259. }
  260. pj_pool_release(pool);
  261. return 0;
  262. }
  263. int thread_test(void)
  264. {
  265. int rc;
  266. rc = simple_thread("simple thread test", 0);
  267. if (rc != PJ_SUCCESS)
  268. return rc;
  269. rc = simple_thread("suspended thread test", PJ_THREAD_SUSPENDED);
  270. if (rc != PJ_SUCCESS)
  271. return rc;
  272. rc = timeslice_test();
  273. if (rc != PJ_SUCCESS)
  274. return rc;
  275. return rc;
  276. }
  277. #else
  278. /* To prevent warning about "translation unit is empty"
  279. * when this test is disabled.
  280. */
  281. int dummy_thread_test;
  282. #endif /* INCLUDE_THREAD_TEST */