mainloop-test.c 8.88 KB
Newer Older
1 2 3
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN

4 5
#include <errno.h>
#include <glib.h>
6
#ifdef G_OS_UNIX
7
#include <unistd.h>
8
#endif
9 10 11
#include <stdio.h>
#include <stdlib.h>

12 13
#ifdef G_OS_WIN32
#include <fcntl.h>		/* For _O_BINARY used by pipe() macro */
Hans Breuer's avatar
Hans Breuer committed
14
#include <io.h>			/* for _pipe() */
15
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
16 17
#endif

18 19 20 21 22 23 24
#define ITERS 10000
#define INCREMENT 10
#define NTHREADS 4
#define NCRAWLERS 4
#define CRAWLER_TIMEOUT_RANGE 40
#define RECURSER_TIMEOUT 50

25 26 27 28
/* The partial ordering between the context array mutex and
 * crawler array mutex is that the crawler array mutex cannot
 * be locked while the context array mutex is locked
 */
29
GPtrArray *context_array;
30 31
GMutex context_array_mutex;
GCond context_array_cond;
32

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
GMainLoop *main_loop;

G_LOCK_DEFINE_STATIC (crawler_array_lock);
GPtrArray *crawler_array;

typedef struct _AddrData AddrData;
typedef struct _TestData TestData;

struct _AddrData
{
  GMainLoop *loop;
  GIOChannel *dest;
  gint count;
};

struct _TestData
{
  gint current_val;
  gint iters;
  GIOChannel *in;
};

static void cleanup_crawlers (GMainContext *context);

57
static gboolean
58
read_all (GIOChannel *channel, char *buf, gsize len)
59
{
60 61
  gsize bytes_read = 0;
  gsize count;
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
  GIOError err;

  while (bytes_read < len)
    {
      err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
      if (err)
	{
	  if (err != G_IO_ERROR_AGAIN)
	    return FALSE;
	}
      else if (count == 0)
	return FALSE;

      bytes_read += count;
    }

  return TRUE;
}

81
static gboolean
82
write_all (GIOChannel *channel, char *buf, gsize len)
83
{
84 85
  gsize bytes_written = 0;
  gsize count;
86 87 88 89 90 91 92 93 94 95 96 97 98 99
  GIOError err;

  while (bytes_written < len)
    {
      err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
      if (err && err != G_IO_ERROR_AGAIN)
	return FALSE;

      bytes_written += count;
    }

  return TRUE;
}

100
static gboolean
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
adder_callback (GIOChannel   *source,
		GIOCondition  condition,
		gpointer      data)
{
  char buf1[32];
  char buf2[32];

  char result[32];

  AddrData *addr_data = data;

  if (!read_all (source, buf1, 32) ||
      !read_all (source, buf2, 32))
    {
      g_main_loop_quit (addr_data->loop);
      return FALSE;
    }

  sprintf (result, "%d", atoi(buf1) + atoi(buf2));
  write_all (addr_data->dest, result, 32);
  
  return TRUE;
}

125
static gboolean
126 127 128 129 130 131 132 133 134
timeout_callback (gpointer data)
{
  AddrData *addr_data = data;

  addr_data->count++;
  
  return TRUE;
}

135
static gpointer
136 137 138 139 140 141 142 143 144
adder_thread (gpointer data)
{
  GMainContext *context;
  GSource *adder_source;
  GSource *timeout_source;

  GIOChannel **channels = data;
  AddrData addr_data;

145
  context = g_main_context_new ();
146

147
  g_mutex_lock (&context_array_mutex);
148
  
149
  g_ptr_array_add (context_array, context);
150 151

  if (context_array->len == NTHREADS)
152
    g_cond_broadcast (&context_array_cond);
153
  
154
  g_mutex_unlock (&context_array_mutex);
155 156 157 158

  addr_data.dest = channels[1];
  addr_data.loop = g_main_loop_new (context, FALSE);
  addr_data.count = 0;
159

160
  adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
161
  g_source_set_name (adder_source, "Adder I/O");
162 163
  g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
  g_source_attach (adder_source, context);
Owen Taylor's avatar
Owen Taylor committed
164
  g_source_unref (adder_source);
165 166

  timeout_source = g_timeout_source_new (10);
167
  g_source_set_name (timeout_source, "Adder timeout");
168 169 170
  g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
  g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
  g_source_attach (timeout_source, context);
Owen Taylor's avatar
Owen Taylor committed
171
  g_source_unref (timeout_source);
172

173
  g_main_loop_run (addr_data.loop);
174 175 176 177 178 179

  g_io_channel_unref (channels[0]);
  g_io_channel_unref (channels[1]);

  g_free (channels);
  
180
  g_main_loop_unref (addr_data.loop);
181

182
#ifdef VERBOSE
183
  g_print ("Timeout run %d times\n", addr_data.count);
184
#endif
185

186
  g_mutex_lock (&context_array_mutex);
187 188 189
  g_ptr_array_remove (context_array, context);
  if (context_array->len == 0)
    g_main_loop_quit (main_loop);
190
  g_mutex_unlock (&context_array_mutex);
191 192

  cleanup_crawlers (context);
193 194

  return NULL;
195 196
}

197
static void
198 199 200 201 202 203 204 205 206 207 208 209
io_pipe (GIOChannel **channels)
{
  gint fds[2];

  if (pipe(fds) < 0)
    {
      g_warning ("Cannot create pipe %s\n", g_strerror (errno));
      exit (1);
    }

  channels[0] = g_io_channel_unix_new (fds[0]);
  channels[1] = g_io_channel_unix_new (fds[1]);
210 211 212

  g_io_channel_set_close_on_unref (channels[0], TRUE);
  g_io_channel_set_close_on_unref (channels[1], TRUE);
213 214
}

215
static void
216 217 218 219 220 221 222 223 224 225 226 227
do_add (GIOChannel *in, gint a, gint b)
{
  char buf1[32];
  char buf2[32];

  sprintf (buf1, "%d", a);
  sprintf (buf2, "%d", b);

  write_all (in, buf1, 32);
  write_all (in, buf2, 32);
}

228
static gboolean
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
adder_response (GIOChannel   *source,
		GIOCondition  condition,
		gpointer      data)
{
  char result[32];
  TestData *test_data = data;
  
  if (!read_all (source, result, 32))
    return FALSE;

  test_data->current_val = atoi (result);
  test_data->iters--;

  if (test_data->iters == 0)
    {
      if (test_data->current_val != ITERS * INCREMENT)
	{
	  g_print ("Addition failed: %d != %d\n",
		   test_data->current_val, ITERS * INCREMENT);
	  exit (1);
	}

      g_io_channel_unref (source);
      g_io_channel_unref (test_data->in);
Owen Taylor's avatar
Owen Taylor committed
253 254

      g_free (test_data);
255 256 257 258 259 260 261 262 263
      
      return FALSE;
    }
  
  do_add (test_data->in, test_data->current_val, INCREMENT);

  return TRUE;
}

264
static void
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
create_adder_thread (void)
{
  GError *err = NULL;
  TestData *test_data;
  
  GIOChannel *in_channels[2];
  GIOChannel *out_channels[2];

  GIOChannel **sub_channels;

  sub_channels = g_new (GIOChannel *, 2);

  io_pipe (in_channels);
  io_pipe (out_channels);

  sub_channels[0] = in_channels[0];
  sub_channels[1] = out_channels[1];

283
  g_thread_create (adder_thread, sub_channels, FALSE, &err);
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

  if (err)
    {
      g_warning ("Cannot create thread: %s", err->message);
      exit (1);
    }

  test_data = g_new (TestData, 1);
  test_data->in = in_channels[1];
  test_data->current_val = 0;
  test_data->iters = ITERS;

  g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
		  adder_response, test_data);
  
  do_add (test_data->in, test_data->current_val, INCREMENT);
}

static void create_crawler (void);

static void
remove_crawler (void)
{
  GSource *other_source;

  if (crawler_array->len > 0)
    {
      other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)];
      g_source_destroy (other_source);
      g_assert (g_ptr_array_remove_fast (crawler_array, other_source));
    }
}

static gint
crawler_callback (gpointer data)
{
  GSource *source = data;

  G_LOCK (crawler_array_lock);
  
  if (!g_ptr_array_remove_fast (crawler_array, source))
    remove_crawler();

  remove_crawler();
  G_UNLOCK (crawler_array_lock);
	    
  create_crawler();
  create_crawler();

  return FALSE;
}

static void
create_crawler (void)
{
  GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
340
  g_source_set_name (source, "Crawler timeout");
341 342
  g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);

343 344 345
  G_LOCK (crawler_array_lock);
  g_ptr_array_add (crawler_array, source);
  
346
  g_mutex_lock (&context_array_mutex);
347
  g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
Owen Taylor's avatar
Owen Taylor committed
348
  g_source_unref (source);
349
  g_mutex_unlock (&context_array_mutex);
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

  G_UNLOCK (crawler_array_lock);
}

static void
cleanup_crawlers (GMainContext *context)
{
  gint i;
  
  G_LOCK (crawler_array_lock);
  for (i=0; i < crawler_array->len; i++)
    {
      if (g_source_get_context (crawler_array->pdata[i]) == context)
	{
	  g_source_destroy (g_ptr_array_remove_index (crawler_array, i));
	  i--;
	}
    }
  G_UNLOCK (crawler_array_lock);
}

static gboolean
recurser_idle (gpointer data)
{
  GMainContext *context = data;
  gint i;

  for (i = 0; i < 10; i++)
378
    g_main_context_iteration (context, FALSE);
379 380 381 382 383 384 385 386 387 388

  return FALSE;
}

static gboolean
recurser_start (gpointer data)
{
  GMainContext *context;
  GSource *source;
  
389
  g_mutex_lock (&context_array_mutex);
390 391
  context = context_array->pdata[g_random_int_range (0, context_array->len)];
  source = g_idle_source_new ();
392
  g_source_set_name (source, "Recursing idle source");
393 394
  g_source_set_callback (source, recurser_idle, context, NULL);
  g_source_attach (source, context);
Owen Taylor's avatar
Owen Taylor committed
395
  g_source_unref (source);
396
  g_mutex_unlock (&context_array_mutex);
397 398 399 400

  return TRUE;
}

401
int
402 403 404 405 406 407
main (int   argc,
      char *argv[])
{
  gint i;

  context_array = g_ptr_array_new ();
408

409 410 411 412 413 414 415
  crawler_array = g_ptr_array_new ();

  main_loop = g_main_loop_new (NULL, FALSE);

  for (i = 0; i < NTHREADS; i++)
    create_adder_thread ();

416 417
  /* Wait for all threads to start
   */
418
  g_mutex_lock (&context_array_mutex);
419 420
  
  if (context_array->len < NTHREADS)
421
    g_cond_wait (&context_array_cond, &context_array_mutex);
422
  
423
  g_mutex_unlock (&context_array_mutex);
424
  
425 426 427 428 429 430
  for (i = 0; i < NCRAWLERS; i++)
    create_crawler ();

  g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL);

  g_main_loop_run (main_loop);
431
  g_main_loop_unref (main_loop);
432 433 434

  return 0;
}