mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-05 08:56:16 +01:00
5bc7729d16
G_THREADS_ENABLED still exists, but is always defined. It is still possible to use libglib without threads, but gobject (and everything above it) is now guaranteed to be using threads (as, in fact, it was before, since it was accidentally impossible to compile with --disable-threads). https://bugzilla.gnome.org/show_bug.cgi?id=616754
439 lines
8.9 KiB
C
439 lines
8.9 KiB
C
#undef G_DISABLE_ASSERT
|
|
#undef G_LOG_DOMAIN
|
|
|
|
#include <errno.h>
|
|
#include <glib.h>
|
|
#ifdef G_OS_UNIX
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef G_OS_WIN32
|
|
#include <fcntl.h> /* For _O_BINARY used by pipe() macro */
|
|
#include <io.h> /* for _pipe() */
|
|
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
|
|
#endif
|
|
|
|
#define ITERS 10000
|
|
#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_source_set_name (adder_source, "Adder I/O");
|
|
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_name (timeout_source, "Adder timeout");
|
|
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,
|
|
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_name (source, "Crawler timeout");
|
|
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_name (source, "Recursing idle source");
|
|
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[])
|
|
{
|
|
gint i;
|
|
|
|
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);
|
|
|
|
return 0;
|
|
}
|