1#undef G_DISABLE_ASSERT
2#undef G_LOG_DOMAIN
3
4#include <errno.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <stdio.h>
8#include <sys/time.h>
9#include <sys/resource.h>
10
11#include <glib.h>
12#include <glib-object.h>
13
14static int n_children = 3;
15static int n_active_children;
16static int n_iters = 10000;
17static GMainLoop *loop;
18
19static void
20io_pipe (GIOChannel **channels)
21{
22  int fds[2];
23
24  if (pipe(fds) < 0)
25    {
26      fprintf (stderr, "Cannot create pipe %s\n", g_strerror (errno));
27      exit (1);
28    }
29
30  channels[0] = g_io_channel_unix_new (fds[0]);
31  channels[1] = g_io_channel_unix_new (fds[1]);
32}
33
34static gboolean
35read_all (GIOChannel *channel, char *buf, int len)
36{
37  gsize bytes_read = 0;
38  gsize count;
39  GIOError err;
40
41  while (bytes_read < len)
42    {
43      err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
44      if (err)
45	{
46	  if (err != G_IO_ERROR_AGAIN)
47	    return FALSE;
48	}
49      else if (count == 0)
50	return FALSE;
51
52      bytes_read += count;
53    }
54
55  return TRUE;
56}
57
58static gboolean
59write_all (GIOChannel *channel, char *buf, int len)
60{
61  gsize bytes_written = 0;
62  gsize count;
63  GIOError err;
64
65  while (bytes_written < len)
66    {
67      err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
68      if (err && err != G_IO_ERROR_AGAIN)
69	return FALSE;
70
71      bytes_written += count;
72    }
73
74  return TRUE;
75}
76
77static void
78run_child (GIOChannel *in_channel, GIOChannel *out_channel)
79{
80  int i;
81  int val = 1;
82  GTimer *timer = g_timer_new();
83
84  for (i = 0; i < n_iters; i++)
85    {
86      write_all (out_channel, (char *)&val, sizeof (val));
87      read_all (in_channel, (char *)&val, sizeof (val));
88    }
89
90  val = 0;
91  write_all (out_channel, (char *)&val, sizeof (val));
92
93  val = g_timer_elapsed (timer, NULL) * 1000;
94
95  write_all (out_channel, (char *)&val, sizeof (val));
96  g_timer_destroy (timer);
97
98  exit (0);
99}
100
101static gboolean
102input_callback (GIOChannel   *source,
103		GIOCondition  condition,
104		gpointer      data)
105{
106  int val;
107  GIOChannel *dest = (GIOChannel *)data;
108
109  if (!read_all (source, (char *)&val, sizeof(val)))
110    {
111      fprintf (stderr, "Unexpected EOF\n");
112      exit (1);
113    }
114
115  if (val)
116    {
117      write_all (dest, (char *)&val, sizeof(val));
118
119      return TRUE;
120    }
121  else
122    {
123      g_io_channel_close (source);
124      g_io_channel_close (dest);
125
126      g_io_channel_unref (source);
127      g_io_channel_unref (dest);
128
129      n_active_children--;
130      if (n_active_children == 0)
131	g_main_loop_quit (loop);
132
133      return FALSE;
134    }
135}
136
137static void
138create_child (void)
139{
140  int pid;
141  GIOChannel *in_channels[2];
142  GIOChannel *out_channels[2];
143  GSource *source;
144
145  io_pipe (in_channels);
146  io_pipe (out_channels);
147
148  pid = fork ();
149
150  if (pid > 0)			/* Parent */
151    {
152      g_io_channel_close (in_channels[0]);
153      g_io_channel_close (out_channels[1]);
154
155      source = g_io_create_watch (out_channels[0], G_IO_IN | G_IO_HUP);
156      g_source_set_closure (source,
157			    g_cclosure_new (G_CALLBACK (input_callback), in_channels[1], NULL));
158      g_source_attach (source, NULL);
159    }
160  else if (pid == 0)		/* Child */
161    {
162      g_io_channel_close (in_channels[1]);
163      g_io_channel_close (out_channels[0]);
164
165      setsid ();
166
167      run_child (in_channels[0], out_channels[1]);
168    }
169  else				/* Error */
170    {
171      fprintf (stderr, "Cannot fork: %s\n", g_strerror (errno));
172      exit (1);
173    }
174}
175
176static double
177difftimeval (struct timeval *old, struct timeval *new)
178{
179  return
180    (new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
181}
182
183int
184main (int argc, char **argv)
185{
186  int i;
187  struct rusage old_usage;
188  struct rusage new_usage;
189
190  g_type_init ();
191
192  if (argc > 1)
193    n_children = atoi(argv[1]);
194
195  if (argc > 2)
196    n_iters = atoi(argv[2]);
197
198  printf ("Children: %d     Iters: %d\n", n_children, n_iters);
199
200  n_active_children = n_children;
201  for (i = 0; i < n_children; i++)
202    create_child ();
203
204  getrusage (RUSAGE_SELF, &old_usage);
205  loop = g_main_loop_new (NULL, FALSE);
206  g_main_loop_run (loop);
207  getrusage (RUSAGE_SELF, &new_usage);
208
209  printf ("Elapsed user: %g\n",
210	  difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
211  printf ("Elapsed system: %g\n",
212	  difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
213  printf ("Elapsed total: %g\n",
214	  difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
215	  difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
216  printf ("total / iteration: %g\n",
217	  (difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
218	   difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
219	  (n_iters * n_children));
220
221  return 0;
222}
223