diff --git a/gobject/ChangeLog b/gobject/ChangeLog index 7e5a608bf..c64bc0843 100644 --- a/gobject/ChangeLog +++ b/gobject/ChangeLog @@ -1,3 +1,9 @@ +Mon Aug 27 14:55:27 2001 Owen Taylor + + * gsourceclosure.[ch] (g_source_set_closure): Implement. + + * gsourceclosure.[ch]: Add GType's for GIOChannel, GIOCondition. + 2001-08-17 James Henstridge * gobject.c (WeakRefStack): add an object member to the structure. diff --git a/gobject/Makefile.am b/gobject/Makefile.am index f018ea165..b5817b62e 100644 --- a/gobject/Makefile.am +++ b/gobject/Makefile.am @@ -46,6 +46,7 @@ gruntime_public_h_sources = @STRIP_BEGIN@ \ gparam.h \ gparamspecs.h \ gsignal.h \ + gsourceclosure.h \ gtype.h \ gtypemodule.h \ gtypeplugin.h \ @@ -68,6 +69,7 @@ gruntime_c_sources = @STRIP_BEGIN@ \ gparam.c \ gparamspecs.c \ gsignal.c \ + gsourceclosure.c \ gtype.c \ gtypemodule.c \ gtypeplugin.c \ diff --git a/gobject/gmarshal.list b/gobject/gmarshal.list index 908910658..83af2cd4f 100644 --- a/gobject/gmarshal.list +++ b/gobject/gmarshal.list @@ -44,3 +44,4 @@ STRING:OBJECT,POINTER # GRuntime specific marshallers VOID:UINT,POINTER +BOOL:FLAGS diff --git a/gobject/gsourceclosure.c b/gobject/gsourceclosure.c new file mode 100644 index 000000000..90b581af4 --- /dev/null +++ b/gobject/gsourceclosure.c @@ -0,0 +1,201 @@ +/* GObject - GLib Type, Object, Parameter and Signal Library + * Copyright (C) 2001 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gsourceclosure.h" +#include "gboxed.h" +#include "genums.h" +#include "gmarshal.h" +#include "gvalue.h" +#include "gvaluetypes.h" + +GType +g_io_channel_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static ("GIOChannel", + NULL, + (GBoxedCopyFunc) g_io_channel_ref, + (GBoxedFreeFunc) g_io_channel_unref, + FALSE); + + return our_type; +} + +GType +g_io_condition_get_type (void) +{ + static GType etype = 0; + if (etype == 0) + { + static const GFlagsValue values[] = { + { G_IO_IN, "G_IO_IN", "in" }, + { G_IO_OUT, "G_IO_OUT", "out" }, + { G_IO_PRI, "G_IO_PRI", "pri" }, + { G_IO_ERR, "G_IO_ERR", "err" }, + { G_IO_HUP, "G_IO_HUP", "hup" }, + { G_IO_NVAL, "G_IO_NVAL", "nval" }, + { 0, NULL, NULL } + }; + etype = g_flags_register_static ("GIOCondition", values); + } + return etype; +} + +/* We need to hand-write this marshaler, since it doesn't have an + * instance object. + */ +static void +source_closure_marshal_BOOLEAN__VOID (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GSourceFunc callback; + GCClosure *cc = (GCClosure*) closure; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 0); + + callback = (GSourceFunc) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (closure->data); + + g_value_set_boolean (return_value, v_return); +} + +static gboolean +io_watch_closure_callback (GIOChannel *channel, + GIOCondition condition, + gpointer data) +{ + GClosure *closure = data; + + GValue params[2] = { { 0, }, { 0, } }; + GValue result_value = { 0, }; + gboolean result; + + g_value_init (&result_value, G_TYPE_BOOLEAN); + g_value_init (¶ms[0], G_TYPE_IO_CHANNEL); + g_value_set_boxed (¶ms[0], channel); + + g_value_init (¶ms[1], G_TYPE_IO_CONDITION); + g_value_set_flags (¶ms[1], condition); + + g_closure_invoke (closure, &result_value, 2, params, NULL); + + result = g_value_get_boolean (&result_value); + g_value_unset (&result_value); + g_value_unset (¶ms[0]); + g_value_unset (¶ms[1]); + + return result; +} + +static gboolean +source_closure_callback (gpointer data) +{ + GClosure *closure = data; + GValue result_value = { 0, }; + gboolean result; + + g_value_init (&result_value, G_TYPE_BOOLEAN); + + g_closure_invoke (closure, &result_value, 0, NULL, NULL); + + result = g_value_get_boolean (&result_value); + g_value_unset (&result_value); + + return result; +} + +static void +closure_callback_get (gpointer cb_data, + GSource *source, + GSourceFunc *func, + gpointer *data) +{ + GSourceFunc closure_callback = source->source_funcs->closure_callback; + + if (!closure_callback) + { + if (source->source_funcs == &g_io_watch_funcs) + closure_callback = (GSourceFunc)io_watch_closure_callback; + else if (source->source_funcs == &g_timeout_funcs || + source->source_funcs == &g_idle_funcs) + closure_callback = source_closure_callback; + } + + *func = closure_callback; + *data = cb_data; +} + +GSourceCallbackFuncs closure_callback_funcs = { + (void (*) (gpointer)) g_closure_ref, + (void (*) (gpointer)) g_closure_unref, + closure_callback_get +}; + +/** + * g_source_set_callback: + * @source: the source + * @func: a #GClosure + * + * Set the callback for a source as a #GClosure. + * + * If the source is not one of the standard GLib types, the closure_callback + * and closure_marshal fields of the GSourceFuncs structure must have been + * filled in with pointers to appropriate functions. + **/ +void +g_source_set_closure (GSource *source, + GClosure *closure) +{ + if (!source->source_funcs->closure_callback && + source->source_funcs != &g_io_watch_funcs && + source->source_funcs != &g_timeout_funcs && + source->source_funcs != &g_idle_funcs) + { + g_critical (G_STRLOC "closure can not be set on closure without GSourceFuncs::closure_callback\n"); + return; + } + + g_closure_ref (closure); + g_closure_sink (closure); + g_source_set_callback_indirect (source, closure, &closure_callback_funcs); + + if (G_CLOSURE_NEEDS_MARSHAL (closure)) + { + GClosureMarshal marshal = (GClosureMarshal)source->source_funcs->closure_marshal; + if (!marshal) + { + if (source->source_funcs == &g_idle_funcs || + source->source_funcs == &g_timeout_funcs) + marshal = source_closure_marshal_BOOLEAN__VOID; + else if (source->source_funcs == &g_io_watch_funcs) + marshal = g_cclosure_marshal_BOOLEAN__FLAGS; + } + if (marshal) + g_closure_set_marshal (closure, marshal); + } +} diff --git a/gobject/gsourceclosure.h b/gobject/gsourceclosure.h new file mode 100644 index 000000000..e12e15bfb --- /dev/null +++ b/gobject/gsourceclosure.h @@ -0,0 +1,37 @@ +/* GObject - GLib Type, Object, Parameter and Signal Library + * Copyright (C) 2001 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_SOURCECLOSURE_H__ + +#include + +G_BEGIN_DECLS + +void g_source_set_closure (GSource *source, + GClosure *closure); + +GType g_io_channel_get_type (void); +GType g_io_condition_get_type (void); + +#define G_TYPE_IO_CHANNEL (g_io_channel_get_type ()) +#define G_TYPE_IO_CONDITION (g_io_condition_get_type ()) + +G_END_DECLS + +#endif /* __G_SOURCECLOSURE_H__ */ diff --git a/tests/Makefile.am b/tests/Makefile.am index d2fdf5ed0..46765b3a1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -37,7 +37,7 @@ CXX_TEST = endif if ENABLE_TIMELOOP -timeloop = timeloop +timeloop = timeloop timeloop-closure endif noinst_PROGRAMS = testglib testgdate testgdateparser unicode-normalize unicode-collate $(timeloop) testglib_LDADD = $(libglib) @@ -47,6 +47,7 @@ unicode_normalize_LDADD = $(libglib) unicode_collate_LDADD = $(libglib) if ENABLE_TIMELOOP timeloop_LDADD = $(libglib) +timeloop_closure_LDADD = $(libglib) $(libgobject) endif test_programs = \ diff --git a/tests/timeloop-closure.c b/tests/timeloop-closure.c new file mode 100644 index 000000000..798cb3a0a --- /dev/null +++ b/tests/timeloop-closure.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +static int n_children = 3; +static int n_active_children; +static int n_iters = 10000; +static GMainLoop *loop; + +static void +io_pipe (GIOChannel **channels) +{ + int fds[2]; + + if (pipe(fds) < 0) + { + fprintf (stderr, "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]); +} + +static gboolean +read_all (GIOChannel *channel, char *buf, int 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; +} + +static gboolean +write_all (GIOChannel *channel, char *buf, int 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; +} + +static void +run_child (GIOChannel *in_channel, GIOChannel *out_channel) +{ + int i; + int val = 1; + GTimer *timer = g_timer_new(); + + for (i = 0; i < n_iters; i++) + { + write_all (out_channel, (char *)&val, sizeof (val)); + read_all (in_channel, (char *)&val, sizeof (val)); + } + + val = 0; + write_all (out_channel, (char *)&val, sizeof (val)); + + val = g_timer_elapsed (timer, NULL) * 1000; + + write_all (out_channel, (char *)&val, sizeof (val)); + g_timer_destroy (timer); + + exit (0); +} + +static gboolean +input_callback (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + int val; + GIOChannel *dest = (GIOChannel *)data; + + if (!read_all (source, (char *)&val, sizeof(val))) + { + fprintf (stderr, "Unexpected EOF\n"); + exit (1); + } + + if (val) + { + write_all (dest, (char *)&val, sizeof(val)); + + return TRUE; + } + else + { + g_io_channel_close (source); + g_io_channel_close (dest); + + g_io_channel_unref (source); + g_io_channel_unref (dest); + + n_active_children--; + if (n_active_children == 0) + g_main_loop_quit (loop); + + return FALSE; + } +} + +static void +create_child () +{ + int pid; + GIOChannel *in_channels[2]; + GIOChannel *out_channels[2]; + GSource *source; + + io_pipe (in_channels); + io_pipe (out_channels); + + pid = fork (); + + if (pid > 0) /* Parent */ + { + g_io_channel_close (in_channels[0]); + g_io_channel_close (out_channels[1]); + + source = g_io_create_watch (out_channels[0], G_IO_IN | G_IO_HUP); + g_source_set_closure (source, + g_cclosure_new (G_CALLBACK (input_callback), in_channels[1], NULL)); + g_source_attach (source, NULL); + } + else if (pid == 0) /* Child */ + { + g_io_channel_close (in_channels[1]); + g_io_channel_close (out_channels[0]); + + setsid (); + + run_child (in_channels[0], out_channels[1]); + } + else /* Error */ + { + fprintf (stderr, "Cannot fork: %s\n", g_strerror (errno)); + exit (1); + } +} + +static double +difftimeval (struct timeval *old, struct timeval *new) +{ + return + (new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000; +} + +int +main (int argc, char **argv) +{ + int i; + struct rusage old_usage; + struct rusage new_usage; + + g_type_init (); + + if (argc > 1) + n_children = atoi(argv[1]); + + if (argc > 2) + n_iters = atoi(argv[2]); + + printf ("Children: %d Iters: %d\n", n_children, n_iters); + + n_active_children = n_children; + for (i = 0; i < n_children; i++) + create_child (); + + getrusage (RUSAGE_SELF, &old_usage); + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + getrusage (RUSAGE_SELF, &new_usage); + + printf ("Elapsed user: %g\n", + difftimeval (&old_usage.ru_utime, &new_usage.ru_utime)); + printf ("Elapsed system: %g\n", + difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)); + printf ("Elapsed total: %g\n", + difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) + + difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)); + printf ("total / iteration: %g\n", + (difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) + + difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) / + (n_iters * n_children)); + + return 0; +}