diff options
author | Owen Taylor <otaylor@redhat.com> | 2000-12-05 20:45:33 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2000-12-05 20:45:33 +0000 |
commit | e2fd4e2bd0589b159f87b491095565d16fac2789 (patch) | |
tree | 45fd0ca3f6b665891a38e47713dda373dd13203f /tests/mainloop-test.c | |
parent | 5791ec5b8539e872343667dbdf190dd0d3e493ed (diff) |
Major change in API for creating sources to handle multiple main loops
Tue Dec 5 12:23:04 2000 Owen Taylor <otaylor@redhat.com>
* gmain.[hc]: Major change in API for creating sources
to handle multiple main loops (GMainContext *).
GSources are now exposed as GSource * and implemented
with structure derivation.
* giochannel.[ch]: Changed vtable for GIOChannel to correspond
to the new mainloop API, add g_io_channel_create_watch().
* gtypes.h: Move GTimeVal here.
* gthread.h: Remove gmain.h include to avoid circularity.
* giounix.c: Update for new GMain API.
* giowin32.c: Update for new GMain API. (No check for
proper compilation or working.)
* timeloop.c timeloop-basic.c: A benchmarking program for
the main loop comparing the main loop against a
hand-written (timeloop-basic.c) variant.
* tests/mainloop-test.c: New torture test of mainloop.
* docs/Changes-2.0.txt: Started. Added text about
changes to GMain.
* gmain.c (g_main_add_poll_unlocked): Initial fd->revents
to zero. (#8482, Benjamin Kahn)
Diffstat (limited to 'tests/mainloop-test.c')
-rw-r--r-- | tests/mainloop-test.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/tests/mainloop-test.c b/tests/mainloop-test.c new file mode 100644 index 000000000..aebefd166 --- /dev/null +++ b/tests/mainloop-test.c @@ -0,0 +1,396 @@ +#include <errno.h> +#include <glib.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> + +#define ITERS 10000 +#define INCREMENT 10 +#define NTHREADS 4 +#define NCRAWLERS 4 +#define CRAWLER_TIMEOUT_RANGE 40 +#define RECURSER_TIMEOUT 50 + +G_LOCK_DEFINE_STATIC (context_array_lock); +GPtrArray *context_array; +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, int len) +{ + int bytes_read = 0; + int 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, int len) +{ + int bytes_written = 0; + int 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; +} + +void +adder_thread (gpointer data) +{ + GMainContext *context; + GSource *adder_source; + GSource *timeout_source; + + GIOChannel **channels = data; + AddrData addr_data; + + context = g_main_context_get (g_thread_self()); + + G_LOCK (context_array_lock); + g_ptr_array_add (context_array, context); + G_UNLOCK (context_array_lock); + + 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_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL); + g_source_attach (adder_source, context); + + 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_main_run (addr_data.loop); + + g_io_channel_close (channels[0]); + g_io_channel_close (channels[1]); + g_io_channel_unref (channels[0]); + g_io_channel_unref (channels[1]); + + g_free (channels); + + g_main_loop_destroy (addr_data.loop); + + g_print ("Timeout run %d times\n", addr_data.count); + + G_LOCK (context_array_lock); + g_ptr_array_remove (context_array, context); + if (context_array->len == 0) + g_main_loop_quit (main_loop); + G_UNLOCK (context_array_lock); + + cleanup_crawlers (context); +} + +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]); +} + +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_close (source); + g_io_channel_close (test_data->in); + + g_io_channel_unref (source); + g_io_channel_unref (test_data->in); + + 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, 0, + FALSE, TRUE, G_THREAD_PRIORITY_NORMAL, &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, + 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 (context_array_lock); + g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]); + G_UNLOCK (context_array_lock); + + G_LOCK (crawler_array_lock); + g_ptr_array_add (crawler_array, source); + 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, TRUE); + + return FALSE; +} + +static gboolean +recurser_start (gpointer data) +{ + GMainContext *context; + GSource *source; + + G_LOCK (context_array_lock); + 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_UNLOCK (context_array_lock); + + 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; + + g_thread_init (NULL); + + context_array = g_ptr_array_new (); + crawler_array = g_ptr_array_new (); + + main_loop = g_main_loop_new (NULL, FALSE); + + for (i = 0; i < NTHREADS; i++) + create_adder_thread (); + + 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_destroy (main_loop); + +#endif + return 0; +} |