diff -r 4332f0f7be53 -r acd3cd4aaceb glib/tests/mainloop-test.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/glib/tests/mainloop-test.c Tue Aug 31 16:54:36 2010 +0300 @@ -0,0 +1,458 @@ +/* Portion Copyright © 2008-09 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.*/ +#undef G_DISABLE_ASSERT +#undef G_LOG_DOMAIN + +#include +#include +#if (defined G_OS_UNIX)||(defined __SYMBIAN32__) +#include +#endif +#include +#include + +#ifdef G_OS_WIN32 +#include /* For _O_BINARY used by pipe() macro */ +#include /* for _pipe() */ +#define pipe(fds) _pipe(fds, 4096, _O_BINARY) +#endif + + + +#ifdef __SYMBIAN32__ +#include +#include "mrt2_glib2_test.h" +#endif /*__SYMBIAN32__*/ + +#ifndef __SYMBIAN32__ +#define ITERS 10000 +#else +#define ITERS 100 +#endif +#define INCREMENT 10 +#define NTHREADS 4 +#define NCRAWLERS 4 +#define CRAWLER_TIMEOUT_RANGE 40 +#define RECURSER_TIMEOUT 50 + +/* 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 + */ +GPtrArray *context_array; +GMutex *context_array_mutex; +GCond *context_array_cond; + +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); + +gboolean +read_all (GIOChannel *channel, char *buf, gsize len) +{ + gsize bytes_read = 0; + gsize count; + 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; +} + +gboolean +write_all (GIOChannel *channel, char *buf, gsize len) +{ + gsize bytes_written = 0; + gsize count; + 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; +} + +gboolean +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; +} + +gboolean +timeout_callback (gpointer data) +{ + AddrData *addr_data = data; + + addr_data->count++; + + return TRUE; +} + +gpointer +adder_thread (gpointer data) +{ + GMainContext *context; + GSource *adder_source; + GSource *timeout_source; + + GIOChannel **channels = data; + AddrData addr_data; + + context = g_main_context_new (); + + g_mutex_lock (context_array_mutex); + + g_ptr_array_add (context_array, context); + + if (context_array->len == NTHREADS) + g_cond_broadcast (context_array_cond); + + g_mutex_unlock (context_array_mutex); + + addr_data.dest = channels[1]; + addr_data.loop = g_main_loop_new (context, FALSE); + addr_data.count = 0; + + adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP | G_IO_PRI); + g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL); + g_source_attach (adder_source, context); + g_source_unref (adder_source); + + timeout_source = g_timeout_source_new (10); + 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); + g_source_unref (timeout_source); + + g_main_loop_run (addr_data.loop); + + g_io_channel_unref (channels[0]); + g_io_channel_unref (channels[1]); + + g_free (channels); + + g_main_loop_unref (addr_data.loop); + +#ifdef VERBOSE + g_print ("Timeout run %d times\n", addr_data.count); +#endif + + g_mutex_lock (context_array_mutex); + g_ptr_array_remove (context_array, context); + if (context_array->len == 0) + g_main_loop_quit (main_loop); + g_mutex_unlock (context_array_mutex); + + cleanup_crawlers (context); + + return NULL; +} + +void +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]); + + g_io_channel_set_close_on_unref (channels[0], TRUE); + g_io_channel_set_close_on_unref (channels[1], TRUE); +} + +void +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); +} + +gboolean +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); + + g_free (test_data); + + return FALSE; + } + + do_add (test_data->in, test_data->current_val, INCREMENT); + + return TRUE; +} + +void +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]; + + g_thread_create (adder_thread, sub_channels, FALSE, &err); + + 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 | G_IO_PRI, + 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)); + g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL); + + G_LOCK (crawler_array_lock); + g_ptr_array_add (crawler_array, source); + + g_mutex_lock (context_array_mutex); + g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]); + g_source_unref (source); + g_mutex_unlock (context_array_mutex); + + 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++) + g_main_context_iteration (context, FALSE); + + return FALSE; +} + +static gboolean +recurser_start (gpointer data) +{ + GMainContext *context; + GSource *source; + + g_mutex_lock (context_array_mutex); + context = context_array->pdata[g_random_int_range (0, context_array->len)]; + source = g_idle_source_new (); + g_source_set_callback (source, recurser_idle, context, NULL); + g_source_attach (source, context); + g_source_unref (source); + g_mutex_unlock (context_array_mutex); + + return TRUE; +} + +int +main (int argc, + char *argv[]) +{ + /* Only run the test, if threads are enabled and a default thread + implementation is available */ +#if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE) + gint i; + + #ifdef __SYMBIAN32__ + g_log_set_handler (NULL, G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG, &mrtLogHandler, NULL); + g_set_print_handler(mrtPrintHandler); + #endif /*__SYMBIAN32__*/ + g_thread_init (NULL); + + context_array = g_ptr_array_new (); + context_array_mutex = g_mutex_new (); + context_array_cond = g_cond_new (); + + crawler_array = g_ptr_array_new (); + + main_loop = g_main_loop_new (NULL, FALSE); + + for (i = 0; i < NTHREADS; i++) + create_adder_thread (); + + /* Wait for all threads to start + */ + g_mutex_lock (context_array_mutex); + + if (context_array->len < NTHREADS) + g_cond_wait (context_array_cond, context_array_mutex); + + g_mutex_unlock (context_array_mutex); + + for (i = 0; i < NCRAWLERS; i++) + create_crawler (); + + g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL); + + g_main_loop_run (main_loop); + g_main_loop_unref (main_loop); + +#endif + #ifdef __SYMBIAN32__ + testResultXml("mainloop-test"); + #endif /* EMULATOR */ + + return 0; +}