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 | |
13 | static int n_children = 3; |
14 | static int n_active_children; |
15 | static int n_iters = 10000; |
16 | static GMainLoop *loop; |
17 | |
18 | static void |
19 | io_pipe (GIOChannel **channels) |
20 | { |
21 | int fds[2]; |
22 | |
23 | if (pipe(pipedes: fds) < 0) |
24 | { |
25 | int errsv = errno; |
26 | fprintf (stderr, format: "Cannot create pipe %s\n" , g_strerror (errnum: errsv)); |
27 | exit (status: 1); |
28 | } |
29 | |
30 | channels[0] = g_io_channel_unix_new (fd: fds[0]); |
31 | channels[1] = g_io_channel_unix_new (fd: fds[1]); |
32 | } |
33 | |
34 | static gboolean |
35 | read_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: buf + bytes_read, count: len - bytes_read, 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 | |
58 | static gboolean |
59 | write_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: buf + bytes_written, count: len - bytes_written, 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 | |
77 | static void |
78 | run_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 (channel: out_channel, buf: (char *)&val, len: sizeof (val)); |
87 | read_all (channel: in_channel, buf: (char *)&val, len: sizeof (val)); |
88 | } |
89 | |
90 | val = 0; |
91 | write_all (channel: out_channel, buf: (char *)&val, len: sizeof (val)); |
92 | |
93 | val = g_timer_elapsed (timer, NULL) * 1000; |
94 | |
95 | write_all (channel: out_channel, buf: (char *)&val, len: sizeof (val)); |
96 | g_timer_destroy (timer); |
97 | |
98 | exit (status: 0); |
99 | } |
100 | |
101 | static gboolean |
102 | input_callback (GIOChannel *source, |
103 | GIOCondition condition, |
104 | gpointer data) |
105 | { |
106 | int val; |
107 | GIOChannel *dest = (GIOChannel *)data; |
108 | |
109 | if (!read_all (channel: source, buf: (char *)&val, len: sizeof(val))) |
110 | { |
111 | fprintf (stderr, format: "Unexpected EOF\n" ); |
112 | exit (status: 1); |
113 | } |
114 | |
115 | if (val) |
116 | { |
117 | write_all (channel: dest, buf: (char *)&val, len: sizeof(val)); |
118 | |
119 | return TRUE; |
120 | } |
121 | else |
122 | { |
123 | g_io_channel_close (channel: source); |
124 | g_io_channel_close (channel: dest); |
125 | |
126 | g_io_channel_unref (channel: source); |
127 | g_io_channel_unref (channel: 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 | |
137 | static void |
138 | create_child (void) |
139 | { |
140 | int pid, errsv; |
141 | GIOChannel *in_channels[2]; |
142 | GIOChannel *out_channels[2]; |
143 | |
144 | io_pipe (channels: in_channels); |
145 | io_pipe (channels: out_channels); |
146 | |
147 | pid = fork (); |
148 | errsv = errno; |
149 | |
150 | if (pid > 0) /* Parent */ |
151 | { |
152 | g_io_channel_close (channel: in_channels[0]); |
153 | g_io_channel_unref (channel: in_channels[0]); |
154 | g_io_channel_close (channel: out_channels[1]); |
155 | g_io_channel_unref (channel: out_channels[1]); |
156 | |
157 | g_io_add_watch (channel: out_channels[0], condition: G_IO_IN | G_IO_HUP, |
158 | func: input_callback, user_data: in_channels[1]); |
159 | } |
160 | else if (pid == 0) /* Child */ |
161 | { |
162 | g_io_channel_close (channel: in_channels[1]); |
163 | g_io_channel_close (channel: out_channels[0]); |
164 | |
165 | setsid (); |
166 | |
167 | run_child (in_channel: in_channels[0], out_channel: out_channels[1]); |
168 | } |
169 | else /* Error */ |
170 | { |
171 | fprintf (stderr, format: "Cannot fork: %s\n" , g_strerror (errnum: errsv)); |
172 | exit (status: 1); |
173 | } |
174 | } |
175 | |
176 | static double |
177 | difftimeval (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 | |
183 | int |
184 | main (int argc, char **argv) |
185 | { |
186 | int i; |
187 | struct rusage old_usage; |
188 | struct rusage new_usage; |
189 | |
190 | if (argc > 1) |
191 | n_children = atoi(nptr: argv[1]); |
192 | |
193 | if (argc > 2) |
194 | n_iters = atoi(nptr: argv[2]); |
195 | |
196 | printf (format: "Children: %d Iters: %d\n" , n_children, n_iters); |
197 | |
198 | n_active_children = n_children; |
199 | for (i = 0; i < n_children; i++) |
200 | create_child (); |
201 | |
202 | getrusage (RUSAGE_SELF, usage: &old_usage); |
203 | loop = g_main_loop_new (NULL, FALSE); |
204 | g_main_loop_run (loop); |
205 | getrusage (RUSAGE_SELF, usage: &new_usage); |
206 | |
207 | printf (format: "Elapsed user: %g\n" , |
208 | difftimeval (old: &old_usage.ru_utime, new: &new_usage.ru_utime)); |
209 | printf (format: "Elapsed system: %g\n" , |
210 | difftimeval (old: &old_usage.ru_stime, new: &new_usage.ru_stime)); |
211 | printf (format: "Elapsed total: %g\n" , |
212 | difftimeval (old: &old_usage.ru_utime, new: &new_usage.ru_utime) + |
213 | difftimeval (old: &old_usage.ru_stime, new: &new_usage.ru_stime)); |
214 | printf (format: "total / iteration: %g\n" , |
215 | (difftimeval (old: &old_usage.ru_utime, new: &new_usage.ru_utime) + |
216 | difftimeval (old: &old_usage.ru_stime, new: &new_usage.ru_stime)) / |
217 | (n_iters * n_children)); |
218 | |
219 | g_main_loop_unref (loop); |
220 | return 0; |
221 | } |
222 | |