/tests/mainloop-test.c

https://github.com/rikaunite/gst-opera_glib · C · 442 lines · 328 code · 106 blank · 8 comment · 33 complexity · 1a90fd36d02a893730830689763612ed MD5 · raw file

  1. #undef G_DISABLE_ASSERT
  2. #undef G_LOG_DOMAIN
  3. #include <errno.h>
  4. #include <glib.h>
  5. #ifdef G_OS_UNIX
  6. #include <unistd.h>
  7. #endif
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #ifdef G_OS_WIN32
  11. #include <fcntl.h> /* For _O_BINARY used by pipe() macro */
  12. #include <io.h> /* for _pipe() */
  13. #define pipe(fds) _pipe(fds, 4096, _O_BINARY)
  14. #endif
  15. #define ITERS 10000
  16. #define INCREMENT 10
  17. #define NTHREADS 4
  18. #define NCRAWLERS 4
  19. #define CRAWLER_TIMEOUT_RANGE 40
  20. #define RECURSER_TIMEOUT 50
  21. /* The partial ordering between the context array mutex and
  22. * crawler array mutex is that the crawler array mutex cannot
  23. * be locked while the context array mutex is locked
  24. */
  25. GPtrArray *context_array;
  26. GMutex *context_array_mutex;
  27. GCond *context_array_cond;
  28. GMainLoop *main_loop;
  29. G_LOCK_DEFINE_STATIC (crawler_array_lock);
  30. GPtrArray *crawler_array;
  31. typedef struct _AddrData AddrData;
  32. typedef struct _TestData TestData;
  33. struct _AddrData
  34. {
  35. GMainLoop *loop;
  36. GIOChannel *dest;
  37. gint count;
  38. };
  39. struct _TestData
  40. {
  41. gint current_val;
  42. gint iters;
  43. GIOChannel *in;
  44. };
  45. static void cleanup_crawlers (GMainContext *context);
  46. gboolean
  47. read_all (GIOChannel *channel, char *buf, gsize len)
  48. {
  49. gsize bytes_read = 0;
  50. gsize count;
  51. GIOError err;
  52. while (bytes_read < len)
  53. {
  54. err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
  55. if (err)
  56. {
  57. if (err != G_IO_ERROR_AGAIN)
  58. return FALSE;
  59. }
  60. else if (count == 0)
  61. return FALSE;
  62. bytes_read += count;
  63. }
  64. return TRUE;
  65. }
  66. gboolean
  67. write_all (GIOChannel *channel, char *buf, gsize len)
  68. {
  69. gsize bytes_written = 0;
  70. gsize count;
  71. GIOError err;
  72. while (bytes_written < len)
  73. {
  74. err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
  75. if (err && err != G_IO_ERROR_AGAIN)
  76. return FALSE;
  77. bytes_written += count;
  78. }
  79. return TRUE;
  80. }
  81. gboolean
  82. adder_callback (GIOChannel *source,
  83. GIOCondition condition,
  84. gpointer data)
  85. {
  86. char buf1[32];
  87. char buf2[32];
  88. char result[32];
  89. AddrData *addr_data = data;
  90. if (!read_all (source, buf1, 32) ||
  91. !read_all (source, buf2, 32))
  92. {
  93. g_main_loop_quit (addr_data->loop);
  94. return FALSE;
  95. }
  96. sprintf (result, "%d", atoi(buf1) + atoi(buf2));
  97. write_all (addr_data->dest, result, 32);
  98. return TRUE;
  99. }
  100. gboolean
  101. timeout_callback (gpointer data)
  102. {
  103. AddrData *addr_data = data;
  104. addr_data->count++;
  105. return TRUE;
  106. }
  107. gpointer
  108. adder_thread (gpointer data)
  109. {
  110. GMainContext *context;
  111. GSource *adder_source;
  112. GSource *timeout_source;
  113. GIOChannel **channels = data;
  114. AddrData addr_data;
  115. context = g_main_context_new ();
  116. g_mutex_lock (context_array_mutex);
  117. g_ptr_array_add (context_array, context);
  118. if (context_array->len == NTHREADS)
  119. g_cond_broadcast (context_array_cond);
  120. g_mutex_unlock (context_array_mutex);
  121. addr_data.dest = channels[1];
  122. addr_data.loop = g_main_loop_new (context, FALSE);
  123. addr_data.count = 0;
  124. adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
  125. g_source_set_name (adder_source, "Adder I/O");
  126. g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
  127. g_source_attach (adder_source, context);
  128. g_source_unref (adder_source);
  129. timeout_source = g_timeout_source_new (10);
  130. g_source_set_name (timeout_source, "Adder timeout");
  131. g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
  132. g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
  133. g_source_attach (timeout_source, context);
  134. g_source_unref (timeout_source);
  135. g_main_loop_run (addr_data.loop);
  136. g_io_channel_unref (channels[0]);
  137. g_io_channel_unref (channels[1]);
  138. g_free (channels);
  139. g_main_loop_unref (addr_data.loop);
  140. #ifdef VERBOSE
  141. g_print ("Timeout run %d times\n", addr_data.count);
  142. #endif
  143. g_mutex_lock (context_array_mutex);
  144. g_ptr_array_remove (context_array, context);
  145. if (context_array->len == 0)
  146. g_main_loop_quit (main_loop);
  147. g_mutex_unlock (context_array_mutex);
  148. cleanup_crawlers (context);
  149. return NULL;
  150. }
  151. void
  152. io_pipe (GIOChannel **channels)
  153. {
  154. gint fds[2];
  155. if (pipe(fds) < 0)
  156. {
  157. g_warning ("Cannot create pipe %s\n", g_strerror (errno));
  158. exit (1);
  159. }
  160. channels[0] = g_io_channel_unix_new (fds[0]);
  161. channels[1] = g_io_channel_unix_new (fds[1]);
  162. g_io_channel_set_close_on_unref (channels[0], TRUE);
  163. g_io_channel_set_close_on_unref (channels[1], TRUE);
  164. }
  165. void
  166. do_add (GIOChannel *in, gint a, gint b)
  167. {
  168. char buf1[32];
  169. char buf2[32];
  170. sprintf (buf1, "%d", a);
  171. sprintf (buf2, "%d", b);
  172. write_all (in, buf1, 32);
  173. write_all (in, buf2, 32);
  174. }
  175. gboolean
  176. adder_response (GIOChannel *source,
  177. GIOCondition condition,
  178. gpointer data)
  179. {
  180. char result[32];
  181. TestData *test_data = data;
  182. if (!read_all (source, result, 32))
  183. return FALSE;
  184. test_data->current_val = atoi (result);
  185. test_data->iters--;
  186. if (test_data->iters == 0)
  187. {
  188. if (test_data->current_val != ITERS * INCREMENT)
  189. {
  190. g_print ("Addition failed: %d != %d\n",
  191. test_data->current_val, ITERS * INCREMENT);
  192. exit (1);
  193. }
  194. g_io_channel_unref (source);
  195. g_io_channel_unref (test_data->in);
  196. g_free (test_data);
  197. return FALSE;
  198. }
  199. do_add (test_data->in, test_data->current_val, INCREMENT);
  200. return TRUE;
  201. }
  202. void
  203. create_adder_thread (void)
  204. {
  205. GError *err = NULL;
  206. TestData *test_data;
  207. GIOChannel *in_channels[2];
  208. GIOChannel *out_channels[2];
  209. GIOChannel **sub_channels;
  210. sub_channels = g_new (GIOChannel *, 2);
  211. io_pipe (in_channels);
  212. io_pipe (out_channels);
  213. sub_channels[0] = in_channels[0];
  214. sub_channels[1] = out_channels[1];
  215. g_thread_create (adder_thread, sub_channels, FALSE, &err);
  216. if (err)
  217. {
  218. g_warning ("Cannot create thread: %s", err->message);
  219. exit (1);
  220. }
  221. test_data = g_new (TestData, 1);
  222. test_data->in = in_channels[1];
  223. test_data->current_val = 0;
  224. test_data->iters = ITERS;
  225. g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
  226. adder_response, test_data);
  227. do_add (test_data->in, test_data->current_val, INCREMENT);
  228. }
  229. static void create_crawler (void);
  230. static void
  231. remove_crawler (void)
  232. {
  233. GSource *other_source;
  234. if (crawler_array->len > 0)
  235. {
  236. other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)];
  237. g_source_destroy (other_source);
  238. g_assert (g_ptr_array_remove_fast (crawler_array, other_source));
  239. }
  240. }
  241. static gint
  242. crawler_callback (gpointer data)
  243. {
  244. GSource *source = data;
  245. G_LOCK (crawler_array_lock);
  246. if (!g_ptr_array_remove_fast (crawler_array, source))
  247. remove_crawler();
  248. remove_crawler();
  249. G_UNLOCK (crawler_array_lock);
  250. create_crawler();
  251. create_crawler();
  252. return FALSE;
  253. }
  254. static void
  255. create_crawler (void)
  256. {
  257. GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
  258. g_source_set_name (source, "Crawler timeout");
  259. g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);
  260. G_LOCK (crawler_array_lock);
  261. g_ptr_array_add (crawler_array, source);
  262. g_mutex_lock (context_array_mutex);
  263. g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
  264. g_source_unref (source);
  265. g_mutex_unlock (context_array_mutex);
  266. G_UNLOCK (crawler_array_lock);
  267. }
  268. static void
  269. cleanup_crawlers (GMainContext *context)
  270. {
  271. gint i;
  272. G_LOCK (crawler_array_lock);
  273. for (i=0; i < crawler_array->len; i++)
  274. {
  275. if (g_source_get_context (crawler_array->pdata[i]) == context)
  276. {
  277. g_source_destroy (g_ptr_array_remove_index (crawler_array, i));
  278. i--;
  279. }
  280. }
  281. G_UNLOCK (crawler_array_lock);
  282. }
  283. static gboolean
  284. recurser_idle (gpointer data)
  285. {
  286. GMainContext *context = data;
  287. gint i;
  288. for (i = 0; i < 10; i++)
  289. g_main_context_iteration (context, FALSE);
  290. return FALSE;
  291. }
  292. static gboolean
  293. recurser_start (gpointer data)
  294. {
  295. GMainContext *context;
  296. GSource *source;
  297. g_mutex_lock (context_array_mutex);
  298. context = context_array->pdata[g_random_int_range (0, context_array->len)];
  299. source = g_idle_source_new ();
  300. g_source_set_name (source, "Recursing idle source");
  301. g_source_set_callback (source, recurser_idle, context, NULL);
  302. g_source_attach (source, context);
  303. g_source_unref (source);
  304. g_mutex_unlock (context_array_mutex);
  305. return TRUE;
  306. }
  307. int
  308. main (int argc,
  309. char *argv[])
  310. {
  311. /* Only run the test, if threads are enabled and a default thread
  312. implementation is available */
  313. #if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE)
  314. gint i;
  315. g_thread_init (NULL);
  316. context_array = g_ptr_array_new ();
  317. context_array_mutex = g_mutex_new ();
  318. context_array_cond = g_cond_new ();
  319. crawler_array = g_ptr_array_new ();
  320. main_loop = g_main_loop_new (NULL, FALSE);
  321. for (i = 0; i < NTHREADS; i++)
  322. create_adder_thread ();
  323. /* Wait for all threads to start
  324. */
  325. g_mutex_lock (context_array_mutex);
  326. if (context_array->len < NTHREADS)
  327. g_cond_wait (context_array_cond, context_array_mutex);
  328. g_mutex_unlock (context_array_mutex);
  329. for (i = 0; i < NCRAWLERS; i++)
  330. create_crawler ();
  331. g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL);
  332. g_main_loop_run (main_loop);
  333. g_main_loop_unref (main_loop);
  334. #endif
  335. return 0;
  336. }