ccan/io: benchmarks.
[ccan] / ccan / io / benchmarks / run-length-prefix.c
1 /* Simulate a server with connections of different speeds.  We count
2  * how many connections complete in 10 seconds. */
3 #include <ccan/io/io.h>
4 #include <ccan/time/time.h>
5 #include <ccan/err/err.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <sys/un.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include <signal.h>
15 #include <assert.h>
16
17 #define REQUEST_MAX 131072
18 #define NUM_CONNS 500 /* per child */
19 #define NUM_CHILDREN 2
20
21 static unsigned int completed;
22
23 struct client {
24         unsigned int len;
25         char *request_buffer;
26 };
27
28 static struct io_op *write_reply(struct io_conn *conn, struct client *client);
29 static struct io_op *read_body(struct io_conn *conn, struct client *client)
30 {
31         assert(client->len <= REQUEST_MAX);
32         return io_read(client->request_buffer, client->len,
33                        io_next(conn, write_reply, client));
34 }
35
36 static struct io_op *read_header(struct io_conn *conn, struct client *client)
37 {
38         return io_read(&client->len, sizeof(client->len),
39                        io_next(conn, read_body, client));
40 }
41
42 /* once we're done, loop again. */
43 static struct io_op *write_complete(struct io_conn *conn, struct client *client)
44 {
45         completed++;
46         return read_header(conn, client);
47 }
48
49 static struct io_op *write_reply(struct io_conn *conn, struct client *client)
50 {
51         return io_write(&client->len, sizeof(client->len),
52                         io_next(conn, write_complete, client));
53 }
54
55 /* This runs in the child. */
56 static void create_clients(struct sockaddr_un *addr, int waitfd)
57 {
58         struct client data;
59         int i, sock[NUM_CONNS], len[NUM_CONNS], done[NUM_CONNS],
60                 result[NUM_CONNS], count = 0;
61
62         for (i = 0; i < NUM_CONNS; i++) {
63                 len[i] = (random() % REQUEST_MAX) + 1;
64                 sock[i] = socket(AF_UNIX, SOCK_STREAM, 0);
65                 if (sock[i] < 0)
66                         err(1, "creating socket");
67                 if (connect(sock[i], (void *)addr, sizeof(*addr)) != 0)
68                         err(1, "connecting socket");
69                 /* Make nonblocking. */
70                 fcntl(sock[i], F_SETFD, fcntl(sock[i], F_GETFD)|O_NONBLOCK);
71                 done[i] = 0;
72         }
73
74         read(waitfd, &i, 1);
75
76         for (;;) {
77                 for (i = 0; i < NUM_CONNS; i++) {
78                         int ret, totlen = len[i] + sizeof(len[i]);
79                         if (done[i] < sizeof(len[i]) + len[i]) {
80                                 data.len = len[i];
81                                 ret = write(sock[i], (void *)&data + done[i],
82                                             totlen - done[i]);
83                                 if (ret > 0)
84                                         done[i] += ret;
85                                 else if (ret < 0 && errno != EAGAIN)
86                                         goto fail;
87                         } else {
88                                 int off = done[i] - totlen;
89                                 ret = read(sock[i], (void *)&result[i] + off,
90                                            sizeof(result[i]) - off);
91                                 if (ret > 0) {
92                                         done[i] += ret;
93                                         if (done[i] == totlen
94                                             + sizeof(result[i])) {
95                                                 assert(result[i] == len[i]);
96                                                 count++;
97                                                 done[i] = 0;
98                                         }
99                                 } else if (ret < 0 && errno != EAGAIN)
100                                         goto fail;
101                         }
102                 }
103         }
104 fail:
105         printf("Child did %u\n", count);
106         exit(0);
107 }
108
109 static int timeout[2];
110 static void sigalarm(int sig)
111 {
112         write(timeout[1], "1", 1);
113 }
114
115 static struct io_op *do_timeout(struct io_conn *conn, char *buf)
116 {
117         return io_break(conn, NULL);
118 }
119
120 static struct io_op *do_timeout_read(struct io_conn *conn, char *buf)
121 {
122         return io_read(buf, 1, io_next(conn, do_timeout, buf));
123 }
124
125 int main(int argc, char *argv[])
126 {
127         unsigned int i, j;
128         struct sockaddr_un addr;
129         struct timespec start, end;
130         char buffer[REQUEST_MAX];
131         int fd, wake[2];
132         char buf;
133
134         addr.sun_family = AF_UNIX;
135         sprintf(addr.sun_path, "/tmp/run-different-speed.sock.%u", getpid());
136
137         if (pipe(wake) != 0 || pipe(timeout) != 0)
138                 err(1, "Creating pipes");
139
140         fd = socket(AF_UNIX, SOCK_STREAM, 0);
141         if (fd < 0)
142                 err(1, "Creating socket");
143
144         if (bind(fd, (void *)&addr, sizeof(addr)) != 0)
145                 err(1, "Binding to %s", addr.sun_path);
146
147         if (listen(fd, NUM_CONNS) != 0)
148                 err(1, "Listening on %s", addr.sun_path);
149
150         for (i = 0; i < NUM_CHILDREN; i++) {
151                 switch (fork()) {
152                 case -1:
153                         err(1, "forking");
154                 case 0:
155                         close(wake[1]);
156                         create_clients(&addr, wake[0]);
157                         break;
158                 }
159                 for (j = 0; j < NUM_CONNS; j++) {
160                         struct client *client = malloc(sizeof(*client));
161                         int ret = accept(fd, NULL, 0);
162                         if (ret < 0)
163                                 err(1, "Accepting fd");
164                         /* For efficiency, we share buffer */
165                         client->request_buffer = buffer;
166                         io_new_conn(ret, read_header, NULL, client);
167                 }
168         }
169
170         io_new_conn(timeout[0], do_timeout_read, NULL, &buf);
171
172         close(wake[0]);
173         for (i = 0; i < NUM_CHILDREN; i++)
174                 write(wake[1], "1", 1);
175
176         signal(SIGALRM, sigalarm);
177         alarm(10);
178         start = time_now();
179         io_loop();
180         end = time_now();
181         close(fd);
182
183         printf("%u connections complete (%u ns per conn)\n",
184                completed,
185                (int)time_to_nsec(time_divide(time_sub(end, start), completed)));
186         return 0;
187 }