about summary refs log tree commit diff
path: root/src/rt/libuv/test/test-callback-stack.c
diff options
context:
space:
mode:
authorRob Arnold <robarnold@68-26-94-7.pools.spcsdns.net>2011-06-14 08:25:44 -0700
committerBrian Anderson <banderson@mozilla.com>2011-08-05 11:57:06 -0700
commit904f443fa99a6349a09441c645aa8f5e4d86fd25 (patch)
treebf4b64d14c3081343f7740b735b878c91f42b7d1 /src/rt/libuv/test/test-callback-stack.c
parentb6be83885f26c02888b17cdca78edb8d125e36cb (diff)
downloadrust-904f443fa99a6349a09441c645aa8f5e4d86fd25.tar.gz
rust-904f443fa99a6349a09441c645aa8f5e4d86fd25.zip
Drop in libuv rev 2b5707d834a6b85b8e589ac04cb61a6db2dab94b
Diffstat (limited to 'src/rt/libuv/test/test-callback-stack.c')
-rw-r--r--src/rt/libuv/test/test-callback-stack.c211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/rt/libuv/test/test-callback-stack.c b/src/rt/libuv/test/test-callback-stack.c
new file mode 100644
index 00000000000..5de5265be68
--- /dev/null
+++ b/src/rt/libuv/test/test-callback-stack.c
@@ -0,0 +1,211 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * TODO: Add explanation of why we want on_close to be called from fresh
+ * stack.
+ */
+
+#include "../uv.h"
+#include "task.h"
+
+
+static const char MESSAGE[] = "Failure is for the weak. Everyone dies alone.";
+
+static uv_tcp_t client;
+static uv_timer_t timer;
+static uv_req_t connect_req, write_req, shutdown_req;
+
+static int nested = 0;
+static int close_cb_called = 0;
+static int connect_cb_called = 0;
+static int write_cb_called = 0;
+static int timer_cb_called = 0;
+static int bytes_received = 0;
+static int shutdown_cb_called = 0;
+
+
+static uv_buf_t alloc_cb(uv_tcp_t* tcp, size_t size) {
+  uv_buf_t buf;
+  buf.len = size;
+  buf.base = (char*) malloc(size);
+  ASSERT(buf.base);
+  return buf;
+}
+
+
+static void close_cb(uv_handle_t* handle) {
+  ASSERT(nested == 0 && "close_cb must be called from a fresh stack");
+
+  close_cb_called++;
+}
+
+
+static void shutdown_cb(uv_req_t* req, int status) {
+  ASSERT(status == 0);
+  ASSERT(nested == 0 && "shutdown_cb must be called from a fresh stack");
+
+  shutdown_cb_called++;
+}
+
+
+static void read_cb(uv_tcp_t* tcp, ssize_t nread, uv_buf_t buf) {
+  ASSERT(nested == 0 && "read_cb must be called from a fresh stack");
+
+  printf("Read. nread == %d\n", nread);
+  free(buf.base);
+
+  if (nread == 0) {
+    ASSERT(uv_last_error().code == UV_EAGAIN);
+    return;
+
+  } else if (nread == -1) {
+    ASSERT(uv_last_error().code == UV_EOF);
+
+    nested++;
+    if (uv_close((uv_handle_t*)tcp, close_cb)) {
+      FATAL("uv_close failed");
+    }
+    nested--;
+
+    return;
+  }
+
+  bytes_received += nread;
+
+  /* We call shutdown here because when bytes_received == sizeof MESSAGE */
+  /* there will be no more data sent nor received, so here it would be */
+  /* possible for a backend to to call shutdown_cb immediately and *not* */
+  /* from a fresh stack. */
+  if (bytes_received == sizeof MESSAGE) {
+    nested++;
+    uv_req_init(&shutdown_req, (uv_handle_t*)tcp, shutdown_cb);
+
+    puts("Shutdown");
+
+    if (uv_shutdown(&shutdown_req)) {
+      FATAL("uv_shutdown failed");
+    }
+    nested--;
+  }
+}
+
+
+static void timer_cb(uv_handle_t* handle, int status) {
+  int r;
+
+  ASSERT(handle == (uv_handle_t*)&timer);
+  ASSERT(status == 0);
+  ASSERT(nested == 0 && "timer_cb must be called from a fresh stack");
+
+  puts("Timeout complete. Now read data...");
+
+  nested++;
+  if (uv_read_start(&client, alloc_cb, read_cb)) {
+    FATAL("uv_read_start failed");
+  }
+  nested--;
+
+  timer_cb_called++;
+
+  r = uv_close(handle, close_cb);
+  ASSERT(r == 0);
+}
+
+
+static void write_cb(uv_req_t* req, int status) {
+  int r;
+
+  ASSERT(status == 0);
+  ASSERT(nested == 0 && "write_cb must be called from a fresh stack");
+
+  puts("Data written. 500ms timeout...");
+
+  /* After the data has been sent, we're going to wait for a while, then */
+  /* start reading. This makes us certain that the message has been echoed */
+  /* back to our receive buffer when we start reading. This maximizes the */
+  /* tempation for the backend to use dirty stack for calling read_cb. */
+  nested++;
+  r = uv_timer_init(&timer);
+  ASSERT(r == 0);
+  r = uv_timer_start(&timer, timer_cb, 500, 0);
+  ASSERT(r == 0);
+  nested--;
+
+  write_cb_called++;
+}
+
+
+static void connect_cb(uv_req_t* req, int status) {
+  uv_buf_t buf;
+
+  puts("Connected. Write some data to echo server...");
+
+  ASSERT(status == 0);
+  ASSERT(nested == 0 && "connect_cb must be called from a fresh stack");
+
+  nested++;
+
+  buf.base = (char*) &MESSAGE;
+  buf.len = sizeof MESSAGE;
+
+  uv_req_init(&write_req, req->handle, write_cb);
+
+  if (uv_write(&write_req, &buf, 1)) {
+    FATAL("uv_write failed");
+  }
+
+  nested--;
+
+  connect_cb_called++;
+}
+
+
+TEST_IMPL(callback_stack) {
+  struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT);
+
+  uv_init();
+
+  if (uv_tcp_init(&client)) {
+    FATAL("uv_tcp_init failed");
+  }
+
+  puts("Connecting...");
+
+  nested++;
+  uv_req_init(&connect_req, (uv_handle_t*)&client, connect_cb);
+  if (uv_connect(&connect_req, addr)) {
+    FATAL("uv_connect failed");
+  }
+  nested--;
+
+  uv_run();
+
+  ASSERT(nested == 0);
+  ASSERT(connect_cb_called == 1 && "connect_cb must be called exactly once");
+  ASSERT(write_cb_called == 1 && "write_cb must be called exactly once");
+  ASSERT(timer_cb_called == 1 && "timer_cb must be called exactly once");
+  ASSERT(bytes_received == sizeof MESSAGE);
+  ASSERT(shutdown_cb_called == 1 && "shutdown_cb must be called exactly once");
+  ASSERT(close_cb_called == 2 && "close_cb must be called exactly twice");
+
+  return 0;
+}