sl@0: /* Portion Copyright © 2008-09 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.*/
sl@0: #undef G_DISABLE_ASSERT
sl@0: #undef G_LOG_DOMAIN
sl@0: 
sl@0: 
sl@0: #include <errno.h>
sl@0: #include <glib.h>
sl@0: 
sl@0: #ifdef G_OS_UNIX
sl@0: #include <unistd.h>
sl@0: #endif
sl@0: #include <stdio.h>
sl@0: #include <stdlib.h>
sl@0: 
sl@0: #ifdef G_OS_WIN32
sl@0: #include <fcntl.h>		/* For _O_BINARY used by pipe() macro */
sl@0: #include <io.h>			/* for _pipe() */
sl@0: #endif
sl@0: 
sl@0: 
sl@0: 
sl@0: #ifdef SYMBIAN
sl@0: #include <glib_global.h>
sl@0: #include "mrt2_glib2_test.h"
sl@0: #endif /*SYMBIAN*/
sl@0: 
sl@0: 
sl@0: #define ITERS 10
sl@0: #define INCREMENT 10
sl@0: #define NTHREADS 4
sl@0: #define NCRAWLERS 4
sl@0: #define CRAWLER_TIMEOUT_RANGE 40
sl@0: #define RECURSER_TIMEOUT 50
sl@0: 
sl@0: /* The partial ordering between the context array mutex and
sl@0:  * crawler array mutex is that the crawler array mutex cannot
sl@0:  * be locked while the context array mutex is locked
sl@0:  */
sl@0: GPtrArray *context_array;
sl@0: GMutex *context_array_mutex;
sl@0: GCond *context_array_cond;
sl@0: 
sl@0: GMainLoop *main_loop;
sl@0: 
sl@0: G_LOCK_DEFINE_STATIC (crawler_array_lock);
sl@0: GPtrArray *crawler_array;
sl@0: 
sl@0: typedef struct _AddrData AddrData;
sl@0: typedef struct _TestData TestData;
sl@0: 
sl@0: struct _AddrData
sl@0: {
sl@0:   GMainLoop *loop;
sl@0:   GIOChannel *dest;
sl@0:   gint count;
sl@0: };
sl@0: 
sl@0: struct _TestData
sl@0: {
sl@0:   gint current_val;
sl@0:   gint iters;
sl@0:   GIOChannel *in;
sl@0: };
sl@0: 
sl@0: static void cleanup_crawlers (GMainContext *context);
sl@0: 
sl@0: gboolean
sl@0: read_all (GIOChannel *channel, char *buf, gsize len)
sl@0: {
sl@0:   gsize bytes_read = 0;
sl@0:   gsize count;
sl@0:   GIOError err;
sl@0: 
sl@0:   while (bytes_read < len)
sl@0:     {
sl@0:       err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
sl@0:       if (err)
sl@0: 	{
sl@0: 	  if (err != G_IO_ERROR_AGAIN)
sl@0: 	  g_assert(FALSE && "mainloop-test failed");
sl@0: 	    return FALSE;
sl@0: 	}
sl@0:       else if (count == 0)
sl@0: 	return FALSE;
sl@0: 
sl@0:       bytes_read += count;
sl@0:     }
sl@0: 
sl@0:   return TRUE;
sl@0: }
sl@0: 
sl@0: gboolean
sl@0: write_all (GIOChannel *channel, char *buf, gsize len)
sl@0: {
sl@0:   gsize bytes_written = 0;
sl@0:   gsize count;
sl@0:   GIOError err;
sl@0: 
sl@0:   while (bytes_written < len)
sl@0:     {
sl@0:       err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
sl@0:       if (err && err != G_IO_ERROR_AGAIN)
sl@0: 	return FALSE;
sl@0: 
sl@0:       bytes_written += count;
sl@0:     }
sl@0: 
sl@0:   return TRUE;
sl@0: }
sl@0: 
sl@0: gboolean
sl@0: adder_callback (GIOChannel   *source,
sl@0: 		GIOCondition  condition,
sl@0: 		gpointer      data)
sl@0: {
sl@0:   char buf1[32];
sl@0:   char buf2[32];
sl@0: 
sl@0:   char result[32];
sl@0: 
sl@0:   AddrData *addr_data = data;
sl@0: 
sl@0:   if (!read_all (source, buf1, 32) ||
sl@0:       !read_all (source, buf2, 32))
sl@0:     {
sl@0:       g_main_loop_quit (addr_data->loop);
sl@0:       return FALSE;
sl@0:     }
sl@0: 
sl@0:   sprintf (result, "%d", atoi(buf1) + atoi(buf2));
sl@0:   write_all (addr_data->dest, result, 32);
sl@0: 
sl@0:   return TRUE;
sl@0: }
sl@0: 
sl@0: gboolean
sl@0: timeout_callback (gpointer data)
sl@0: {
sl@0:   AddrData *addr_data = data;
sl@0: 
sl@0:   addr_data->count++;
sl@0: 
sl@0:   return TRUE;
sl@0: }
sl@0: 
sl@0: gpointer
sl@0: adder_thread (gpointer data)
sl@0: {
sl@0:   GMainContext *context;
sl@0:   GSource *adder_source;
sl@0:   GSource *timeout_source;
sl@0: 
sl@0:   GIOChannel **channels = data;
sl@0:   AddrData addr_data;
sl@0: 
sl@0:   context = g_main_context_new ();
sl@0: 
sl@0:   g_assert(context != NULL);
sl@0: 
sl@0:   g_mutex_lock (context_array_mutex);
sl@0: 
sl@0:   g_ptr_array_add (context_array, context);
sl@0: 
sl@0:   if (context_array->len == NTHREADS)
sl@0:     g_cond_broadcast (context_array_cond);
sl@0: 
sl@0:   g_mutex_unlock (context_array_mutex);
sl@0: 
sl@0:   addr_data.dest = channels[1];
sl@0:   addr_data.loop = g_main_loop_new (context, FALSE);
sl@0:   addr_data.count = 0;
sl@0: 
sl@0:   adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
sl@0: 
sl@0:   g_assert(adder_source != NULL);
sl@0: 
sl@0:   g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
sl@0:   g_source_attach (adder_source, context);
sl@0:   g_source_unref (adder_source);
sl@0: 
sl@0:   timeout_source = g_timeout_source_new (10);
sl@0: 
sl@0:   g_assert(timeout_source != NULL);
sl@0: 
sl@0:   g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
sl@0:   g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
sl@0:   g_source_attach (timeout_source, context);
sl@0:   g_source_unref (timeout_source);
sl@0: 
sl@0:   g_main_loop_run (addr_data.loop);
sl@0: 
sl@0:   g_io_channel_unref (channels[0]);
sl@0:   g_io_channel_unref (channels[1]);
sl@0: 
sl@0:   g_free (channels);
sl@0: 
sl@0:   g_main_loop_unref (addr_data.loop);
sl@0: 
sl@0: #ifdef VERBOSE
sl@0:   g_print ("Timeout run %d times\n", addr_data.count);
sl@0: #endif
sl@0: 
sl@0:   g_mutex_lock (context_array_mutex);
sl@0:   g_ptr_array_remove (context_array, context);
sl@0:   if (context_array->len == 0)
sl@0:     g_main_loop_quit (main_loop);
sl@0:   g_mutex_unlock (context_array_mutex);
sl@0: 
sl@0:   cleanup_crawlers (context);
sl@0: 
sl@0:   return NULL;
sl@0: }
sl@0: 
sl@0: 
sl@0: void
sl@0: io_pipe (GIOChannel **channels)
sl@0: {
sl@0:   gint fds[2];
sl@0: 
sl@0:   if (pipe(fds) < 0)
sl@0:     {
sl@0:       g_warning ("Cannot create pipe %s\n", g_strerror (errno));
sl@0: 
sl@0:       g_assert(FALSE && "mainloop-test failed");
sl@0: 
sl@0:       exit (1);
sl@0:     }
sl@0: 
sl@0:   channels[0] = g_io_channel_unix_new (fds[0]);
sl@0:   channels[1] = g_io_channel_unix_new (fds[1]);
sl@0: 
sl@0:   g_io_channel_set_close_on_unref (channels[0], TRUE);
sl@0:   g_io_channel_set_close_on_unref (channels[1], TRUE);
sl@0: }
sl@0: 
sl@0: void
sl@0: do_add (GIOChannel *in, gint a, gint b)
sl@0: {
sl@0:   char buf1[32];
sl@0:   char buf2[32];
sl@0: 
sl@0:   sprintf (buf1, "%d", a);
sl@0:   sprintf (buf2, "%d", b);
sl@0: 
sl@0:   write_all (in, buf1, 32);
sl@0:   write_all (in, buf2, 32);
sl@0: }
sl@0: 
sl@0: gboolean
sl@0: adder_response (GIOChannel   *source,
sl@0: 		GIOCondition  condition,
sl@0: 		gpointer      data)
sl@0: {
sl@0:   char result[32];
sl@0:   TestData *test_data = data;
sl@0: 
sl@0:   if (!read_all (source, result, 32))
sl@0:     return FALSE;
sl@0: 
sl@0:   test_data->current_val = atoi (result);
sl@0:   test_data->iters--;
sl@0: 
sl@0:   if (test_data->iters == 0)
sl@0:     {
sl@0:       if (test_data->current_val != ITERS * INCREMENT)
sl@0: 	{
sl@0: 	  g_print ("Addition failed: %d != %d\n",
sl@0: 		   test_data->current_val, ITERS * INCREMENT);
sl@0: 
sl@0: 	  g_assert(FALSE && "mainloop-test failed");
sl@0: 
sl@0: 	  exit (1);
sl@0: 	}
sl@0: 
sl@0:       g_io_channel_unref (source);
sl@0:       g_io_channel_unref (test_data->in);
sl@0: 
sl@0:       g_free (test_data);
sl@0: 
sl@0:       return FALSE;
sl@0:     }
sl@0: 
sl@0:   do_add (test_data->in, test_data->current_val, INCREMENT);
sl@0: 
sl@0:   return TRUE;
sl@0: }
sl@0: 
sl@0: void
sl@0: create_adder_thread (void)
sl@0: {
sl@0:   GError *err = NULL;
sl@0:   TestData *test_data;
sl@0:   GThread *thread;
sl@0: 
sl@0:   GIOChannel *in_channels[2];
sl@0:   GIOChannel *out_channels[2];
sl@0: 
sl@0:   GIOChannel **sub_channels;
sl@0: 
sl@0:   sub_channels = g_new (GIOChannel *, 2);
sl@0: 
sl@0:   io_pipe (in_channels);
sl@0:   io_pipe (out_channels);
sl@0: 
sl@0:   sub_channels[0] = in_channels[0];
sl@0:   sub_channels[1] = out_channels[1];
sl@0: 
sl@0:   g_thread_create (adder_thread, sub_channels, FALSE, &err);
sl@0: 
sl@0:   if (err)
sl@0:     {
sl@0:       g_warning ("Cannot create thread: %s", err->message);
sl@0: 
sl@0:       g_assert(FALSE && "mainloop-test failed");
sl@0: 
sl@0:       exit (1);
sl@0:     }
sl@0: 
sl@0:   test_data = g_new (TestData, 1);
sl@0:   test_data->in = in_channels[1];
sl@0:   test_data->current_val = 0;
sl@0:   test_data->iters = ITERS;
sl@0: 
sl@0:   g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
sl@0: 		  adder_response, test_data);
sl@0: 
sl@0:   do_add (test_data->in, test_data->current_val, INCREMENT);
sl@0: }
sl@0: 
sl@0: static void create_crawler (void);
sl@0: 
sl@0: static void
sl@0: remove_crawler (void)
sl@0: {
sl@0:   GSource *other_source;
sl@0: 
sl@0:   if (crawler_array->len > 0)
sl@0:     {
sl@0:       other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)];
sl@0:       g_source_destroy (other_source);
sl@0:       g_assert (g_ptr_array_remove_fast (crawler_array, other_source));
sl@0:     }
sl@0: }
sl@0: 
sl@0: static gint
sl@0: crawler_callback (gpointer data)
sl@0: {
sl@0:   GSource *source = data;
sl@0: 
sl@0:   G_LOCK (crawler_array_lock);
sl@0: 
sl@0:   if (!g_ptr_array_remove_fast (crawler_array, source))
sl@0:     remove_crawler();
sl@0: 
sl@0:   remove_crawler();
sl@0:   G_UNLOCK (crawler_array_lock);
sl@0: 
sl@0:   create_crawler();
sl@0:   //create_crawler();
sl@0: 
sl@0:   return FALSE;
sl@0: }
sl@0: 
sl@0: static void
sl@0: create_crawler (void)
sl@0: {
sl@0:   GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
sl@0: 
sl@0:   g_assert(source != NULL);
sl@0: 
sl@0:   g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);
sl@0: 
sl@0:   G_LOCK (crawler_array_lock);
sl@0:   g_ptr_array_add (crawler_array, source);
sl@0: 
sl@0:   g_mutex_lock (context_array_mutex);
sl@0:   if(context_array->len == 0)
sl@0:   	g_source_attach (source, context_array->pdata[0]);
sl@0:   else
sl@0:   	g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
sl@0:   g_source_unref (source);
sl@0:   g_mutex_unlock (context_array_mutex);
sl@0: 
sl@0:   G_UNLOCK (crawler_array_lock);
sl@0: }
sl@0: 
sl@0: static void
sl@0: cleanup_crawlers (GMainContext *context)
sl@0: {
sl@0:   gint i;
sl@0: 
sl@0:   G_LOCK (crawler_array_lock);
sl@0:   for (i=0; i < crawler_array->len; i++)
sl@0:     {
sl@0:       if (g_source_get_context (crawler_array->pdata[i]) == context)
sl@0: 	{
sl@0: 	  g_source_destroy (g_ptr_array_remove_index (crawler_array, i));
sl@0: 	  i--;
sl@0: 	}
sl@0:     }
sl@0:   G_UNLOCK (crawler_array_lock);
sl@0: }
sl@0: 
sl@0: static gboolean
sl@0: recurser_idle (gpointer data)
sl@0: {
sl@0:   GMainContext *context = data;
sl@0:   gint i;
sl@0: 
sl@0:   for (i = 0; i < 10; i++)
sl@0:     g_main_context_iteration (context, FALSE);
sl@0: 
sl@0:   return FALSE;
sl@0: }
sl@0: 
sl@0: static gboolean
sl@0: recurser_start (gpointer data)
sl@0: {
sl@0:   GMainContext *context;
sl@0:   GSource *source;
sl@0: 
sl@0:   g_mutex_lock (context_array_mutex);
sl@0: 
sl@0:   if(context_array->len == 0)
sl@0:   	context = context_array->pdata[0];
sl@0:   else
sl@0:   	context = context_array->pdata[g_random_int_range (0, context_array->len)];
sl@0:   source = g_idle_source_new ();
sl@0:   g_source_set_callback (source, recurser_idle, context, NULL);
sl@0:   g_source_attach (source, context);
sl@0:   g_source_unref (source);
sl@0:   g_mutex_unlock (context_array_mutex);
sl@0: 
sl@0:   return TRUE;
sl@0: }
sl@0: 
sl@0: int
sl@0: main (int   argc,
sl@0:       char *argv[])
sl@0: {
sl@0:   /* Only run the test, if threads are enabled and a default thread
sl@0:      implementation is available */
sl@0: #if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE)
sl@0:   gint i;
sl@0: 
sl@0:   #ifdef SYMBIAN
sl@0:   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);
sl@0:   g_set_print_handler(mrtPrintHandler);
sl@0:   #endif /*SYMBIAN*/
sl@0: #if 0
sl@0:   g_thread_init (NULL);
sl@0: 
sl@0:   context_array = g_ptr_array_new ();
sl@0: 
sl@0:   g_assert(context_array != NULL);
sl@0: 
sl@0:   context_array_mutex = g_mutex_new ();
sl@0: 
sl@0:   g_assert(context_array_mutex != NULL);
sl@0: 
sl@0:   context_array_cond = g_cond_new ();
sl@0: 
sl@0:   g_assert(context_array_cond != NULL);
sl@0: 
sl@0:   crawler_array = g_ptr_array_new ();
sl@0: 
sl@0:   g_assert(crawler_array != NULL);
sl@0: 
sl@0:   main_loop = g_main_loop_new (NULL, FALSE);
sl@0: 
sl@0:   g_assert(main_loop != NULL);
sl@0: 
sl@0:   for (i = 0; i < NTHREADS; i++)
sl@0:     create_adder_thread ();
sl@0: 
sl@0:   /* Wait for all threads to start
sl@0:    */
sl@0:   g_mutex_lock (context_array_mutex);
sl@0: 
sl@0:   if (context_array->len < NTHREADS)
sl@0:     g_cond_wait (context_array_cond, context_array_mutex);
sl@0: 
sl@0:   g_mutex_unlock (context_array_mutex);
sl@0: 
sl@0:   for (i = 0; i < NCRAWLERS; i++)
sl@0:     create_crawler ();
sl@0: 
sl@0:   g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL);
sl@0: 
sl@0:   g_main_loop_run (main_loop);
sl@0:   g_main_loop_unref (main_loop);
sl@0: #endif
sl@0: #endif
sl@0: 
sl@0:   #ifdef SYMBIAN
sl@0:   testResultXml("mainloop-test");
sl@0:   #endif /* EMULATOR */
sl@0: 
sl@0:   return 0;
sl@0: }