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 32typedef 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 40typedef struct buf_s { 41 uv_buf_t uv_buf_t; 42 struct buf_s* next; 43} buf_t; 44 45 46static char PING[] = "PING\n"; 47 48static uv_loop_t* loop; 49 50static buf_t* buf_freelist = NULL; 51static int pinger_shutdown_cb_called; 52static int completed_pingers = 0; 53static int64_t start_time; 54 55 56static 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 72static 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 79static 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 92static void pinger_write_cb(uv_write_t* req, int status) { 93 ASSERT(status == 0); 94 95 free(req); 96} 97 98 99static 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 112static void pinger_shutdown_cb(uv_shutdown_t* req, int status) { 113 ASSERT(status == 0); 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(completed_pingers == 0); 121} 122 123 124static 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(nread == UV_EOF); 134 135 if (buf->base) { 136 buf_free(buf); 137 } 138 139 ASSERT(pinger_shutdown_cb_called == 1); 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(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 166static void pinger_connect_cb(uv_connect_t* req, int status) { 167 pinger_t *pinger = (pinger_t*)req->handle->data; 168 169 ASSERT(status == 0); 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 179static 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(0 == uv_ip4_addr("0.0.0.0", 0, &client_addr)); 186 ASSERT(0 == 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(0 == 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 209BENCHMARK_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(completed_pingers == 1); 218 219 MAKE_VALGRIND_HAPPY(); 220 return 0; 221} 222