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