xref: /libuv/test/benchmark-ping-pongs.c (revision 011a1ac1)
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "uv.h"
23 #include "task.h"
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 
28 /* Run the benchmark for this many ms */
29 #define TIME 5000
30 
31 
32 typedef struct {
33   int pongs;
34   int state;
35   uv_tcp_t tcp;
36   uv_connect_t connect_req;
37   uv_shutdown_t shutdown_req;
38 } pinger_t;
39 
40 typedef struct buf_s {
41   uv_buf_t uv_buf_t;
42   struct buf_s* next;
43 } buf_t;
44 
45 
46 static char PING[] = "PING\n";
47 
48 static uv_loop_t* loop;
49 
50 static buf_t* buf_freelist = NULL;
51 static int pinger_shutdown_cb_called;
52 static int completed_pingers = 0;
53 static int64_t start_time;
54 
55 
buf_alloc(uv_handle_t * tcp,size_t size,uv_buf_t * buf)56 static void buf_alloc(uv_handle_t* tcp, size_t size, uv_buf_t* buf) {
57   buf_t* ab;
58 
59   ab = buf_freelist;
60   if (ab != NULL)
61     buf_freelist = ab->next;
62   else {
63     ab = malloc(size + sizeof(*ab));
64     ab->uv_buf_t.len = size;
65     ab->uv_buf_t.base = (char*) (ab + 1);
66   }
67 
68   *buf = ab->uv_buf_t;
69 }
70 
71 
buf_free(const uv_buf_t * buf)72 static void buf_free(const uv_buf_t* buf) {
73   buf_t* ab = (buf_t*) buf->base - 1;
74   ab->next = buf_freelist;
75   buf_freelist = ab;
76 }
77 
78 
pinger_close_cb(uv_handle_t * handle)79 static void pinger_close_cb(uv_handle_t* handle) {
80   pinger_t* pinger;
81 
82   pinger = (pinger_t*)handle->data;
83   fprintf(stderr, "ping_pongs: %d roundtrips/s\n", (1000 * pinger->pongs) / TIME);
84   fflush(stderr);
85 
86   free(pinger);
87 
88   completed_pingers++;
89 }
90 
91 
pinger_write_cb(uv_write_t * req,int status)92 static void pinger_write_cb(uv_write_t* req, int status) {
93   ASSERT_OK(status);
94 
95   free(req);
96 }
97 
98 
pinger_write_ping(pinger_t * pinger)99 static void pinger_write_ping(pinger_t* pinger) {
100   uv_write_t* req;
101   uv_buf_t buf;
102 
103   buf = uv_buf_init(PING, sizeof(PING) - 1);
104 
105   req = malloc(sizeof *req);
106   if (uv_write(req, (uv_stream_t*) &pinger->tcp, &buf, 1, pinger_write_cb)) {
107     FATAL("uv_write failed");
108   }
109 }
110 
111 
pinger_shutdown_cb(uv_shutdown_t * req,int status)112 static void pinger_shutdown_cb(uv_shutdown_t* req, int status) {
113   ASSERT_OK(status);
114   pinger_shutdown_cb_called++;
115 
116   /*
117    * The close callback has not been triggered yet. We must wait for EOF
118    * until we close the connection.
119    */
120   ASSERT_OK(completed_pingers);
121 }
122 
123 
pinger_read_cb(uv_stream_t * tcp,ssize_t nread,const uv_buf_t * buf)124 static void pinger_read_cb(uv_stream_t* tcp,
125                            ssize_t nread,
126                            const uv_buf_t* buf) {
127   ssize_t i;
128   pinger_t* pinger;
129 
130   pinger = (pinger_t*)tcp->data;
131 
132   if (nread < 0) {
133     ASSERT_EQ(nread, UV_EOF);
134 
135     if (buf->base) {
136       buf_free(buf);
137     }
138 
139     ASSERT_EQ(1, pinger_shutdown_cb_called);
140     uv_close((uv_handle_t*)tcp, pinger_close_cb);
141 
142     return;
143   }
144 
145   /* Now we count the pings */
146   for (i = 0; i < nread; i++) {
147     ASSERT_EQ(buf->base[i], PING[pinger->state]);
148     pinger->state = (pinger->state + 1) % (sizeof(PING) - 1);
149     if (pinger->state == 0) {
150       pinger->pongs++;
151       if (uv_now(loop) - start_time > TIME) {
152         uv_shutdown(&pinger->shutdown_req,
153                     (uv_stream_t*) tcp,
154                     pinger_shutdown_cb);
155         break;
156       } else {
157         pinger_write_ping(pinger);
158       }
159     }
160   }
161 
162   buf_free(buf);
163 }
164 
165 
pinger_connect_cb(uv_connect_t * req,int status)166 static void pinger_connect_cb(uv_connect_t* req, int status) {
167   pinger_t *pinger = (pinger_t*)req->handle->data;
168 
169   ASSERT_OK(status);
170 
171   pinger_write_ping(pinger);
172 
173   if (uv_read_start(req->handle, buf_alloc, pinger_read_cb)) {
174     FATAL("uv_read_start failed");
175   }
176 }
177 
178 
pinger_new(void)179 static void pinger_new(void) {
180   struct sockaddr_in client_addr;
181   struct sockaddr_in server_addr;
182   pinger_t *pinger;
183   int r;
184 
185   ASSERT_OK(uv_ip4_addr("0.0.0.0", 0, &client_addr));
186   ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr));
187   pinger = malloc(sizeof(*pinger));
188   pinger->state = 0;
189   pinger->pongs = 0;
190 
191   /* Try to connect to the server and do NUM_PINGS ping-pongs. */
192   r = uv_tcp_init(loop, &pinger->tcp);
193   ASSERT(!r);
194 
195   pinger->tcp.data = pinger;
196 
197   ASSERT_OK(uv_tcp_bind(&pinger->tcp,
198                         (const struct sockaddr*) &client_addr,
199                         0));
200 
201   r = uv_tcp_connect(&pinger->connect_req,
202                      &pinger->tcp,
203                      (const struct sockaddr*) &server_addr,
204                      pinger_connect_cb);
205   ASSERT(!r);
206 }
207 
208 
BENCHMARK_IMPL(ping_pongs)209 BENCHMARK_IMPL(ping_pongs) {
210   loop = uv_default_loop();
211 
212   start_time = uv_now(loop);
213 
214   pinger_new();
215   uv_run(loop, UV_RUN_DEFAULT);
216 
217   ASSERT_EQ(1, completed_pingers);
218 
219   MAKE_VALGRIND_HAPPY(loop);
220   return 0;
221 }
222