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