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 /* 23 * TODO: Add explanation of why we want on_close to be called from fresh 24 * stack. 25 */ 26 27 #include "uv.h" 28 #include "task.h" 29 30 31 static const char MESSAGE[] = "Failure is for the weak. Everyone dies alone."; 32 33 static uv_tcp_t client; 34 static uv_timer_t timer; 35 static uv_connect_t connect_req; 36 static uv_write_t write_req; 37 static uv_shutdown_t shutdown_req; 38 39 static int nested = 0; 40 static int close_cb_called = 0; 41 static int connect_cb_called = 0; 42 static int write_cb_called = 0; 43 static int timer_cb_called = 0; 44 static int bytes_received = 0; 45 static int shutdown_cb_called = 0; 46 47 48 static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { 49 buf->len = size; 50 buf->base = malloc(size); 51 ASSERT(buf->base != NULL); 52 } 53 54 55 static void close_cb(uv_handle_t* handle) { 56 ASSERT(nested == 0 && "close_cb must be called from a fresh stack"); 57 58 close_cb_called++; 59 } 60 61 62 static void shutdown_cb(uv_shutdown_t* req, int status) { 63 ASSERT(status == 0); 64 ASSERT(nested == 0 && "shutdown_cb must be called from a fresh stack"); 65 66 shutdown_cb_called++; 67 } 68 69 70 static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { 71 ASSERT(nested == 0 && "read_cb must be called from a fresh stack"); 72 73 printf("Read. nread == %d\n", (int)nread); 74 free(buf->base); 75 76 if (nread == 0) { 77 return; 78 79 } else if (nread < 0) { 80 ASSERT(nread == UV_EOF); 81 82 nested++; 83 uv_close((uv_handle_t*)tcp, close_cb); 84 nested--; 85 86 return; 87 } 88 89 bytes_received += nread; 90 91 /* We call shutdown here because when bytes_received == sizeof MESSAGE there 92 * will be no more data sent nor received, so here it would be possible for a 93 * backend to call shutdown_cb immediately and *not* from a fresh stack. */ 94 if (bytes_received == sizeof MESSAGE) { 95 nested++; 96 97 puts("Shutdown"); 98 99 if (uv_shutdown(&shutdown_req, (uv_stream_t*)tcp, shutdown_cb)) { 100 FATAL("uv_shutdown failed"); 101 } 102 nested--; 103 } 104 } 105 106 107 static void timer_cb(uv_timer_t* handle) { 108 ASSERT(handle == &timer); 109 ASSERT(nested == 0 && "timer_cb must be called from a fresh stack"); 110 111 puts("Timeout complete. Now read data..."); 112 113 nested++; 114 if (uv_read_start((uv_stream_t*)&client, alloc_cb, read_cb)) { 115 FATAL("uv_read_start failed"); 116 } 117 nested--; 118 119 timer_cb_called++; 120 121 uv_close((uv_handle_t*)handle, close_cb); 122 } 123 124 125 static void write_cb(uv_write_t* req, int status) { 126 int r; 127 128 ASSERT(status == 0); 129 ASSERT(nested == 0 && "write_cb must be called from a fresh stack"); 130 131 puts("Data written. 500ms timeout..."); 132 133 /* After the data has been sent, we're going to wait for a while, then start 134 * reading. This makes us certain that the message has been echoed back to 135 * our receive buffer when we start reading. This maximizes the temptation 136 * for the backend to use dirty stack for calling read_cb. */ 137 nested++; 138 r = uv_timer_init(uv_default_loop(), &timer); 139 ASSERT(r == 0); 140 r = uv_timer_start(&timer, timer_cb, 500, 0); 141 ASSERT(r == 0); 142 nested--; 143 144 write_cb_called++; 145 } 146 147 148 static void connect_cb(uv_connect_t* req, int status) { 149 uv_buf_t buf; 150 151 puts("Connected. Write some data to echo server..."); 152 153 ASSERT(status == 0); 154 ASSERT(nested == 0 && "connect_cb must be called from a fresh stack"); 155 156 nested++; 157 158 buf.base = (char*) &MESSAGE; 159 buf.len = sizeof MESSAGE; 160 161 if (uv_write(&write_req, (uv_stream_t*)req->handle, &buf, 1, write_cb)) { 162 FATAL("uv_write failed"); 163 } 164 165 nested--; 166 167 connect_cb_called++; 168 } 169 170 171 TEST_IMPL(callback_stack) { 172 struct sockaddr_in addr; 173 174 ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); 175 176 if (uv_tcp_init(uv_default_loop(), &client)) { 177 FATAL("uv_tcp_init failed"); 178 } 179 180 puts("Connecting..."); 181 182 nested++; 183 184 if (uv_tcp_connect(&connect_req, 185 &client, 186 (const struct sockaddr*) &addr, 187 connect_cb)) { 188 FATAL("uv_tcp_connect failed"); 189 } 190 nested--; 191 192 uv_run(uv_default_loop(), UV_RUN_DEFAULT); 193 194 ASSERT(nested == 0); 195 ASSERT(connect_cb_called == 1 && "connect_cb must be called exactly once"); 196 ASSERT(write_cb_called == 1 && "write_cb must be called exactly once"); 197 ASSERT(timer_cb_called == 1 && "timer_cb must be called exactly once"); 198 ASSERT(bytes_received == sizeof MESSAGE); 199 ASSERT(shutdown_cb_called == 1 && "shutdown_cb must be called exactly once"); 200 ASSERT(close_cb_called == 2 && "close_cb must be called exactly twice"); 201 202 MAKE_VALGRIND_HAPPY(); 203 return 0; 204 } 205