about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--include/uv_link_t.h21
-rw-r--r--src/uv_link_observer_t.c34
-rw-r--r--src/uv_link_source_t.c33
-rw-r--r--src/uv_link_t.c36
-rw-r--r--test/src/test-uv-link-observer-t.c30
-rw-r--r--test/src/test-uv-link-source-t.c13
7 files changed, 132 insertions, 36 deletions
diff --git a/README.md b/README.md
index 2003bff..981bbe0 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ static uv_link_methods_t methods = {
   .write = write_impl,
   .try_write = try_write_impl,
   .shutdown = shutdown_impl,
+  .close = close_impl,
 
   /* These will be used only when chaining two links together */
 
diff --git a/include/uv_link_t.h b/include/uv_link_t.h
index 25c229a..68b66c1 100644
--- a/include/uv_link_t.h
+++ b/include/uv_link_t.h
@@ -16,6 +16,7 @@ typedef void (*uv_link_read_cb)(uv_link_t* link,
                                 const uv_buf_t* buf);
 typedef void (*uv_link_write_cb)(uv_link_t* source, int status, void* arg);
 typedef void (*uv_link_shutdown_cb)(uv_link_t* source, int status, void* arg);
+typedef void (*uv_link_close_cb)(uv_link_t* source);
 
 struct uv_link_methods_s {
   int (*read_start)(uv_link_t* link);
@@ -37,6 +38,8 @@ struct uv_link_methods_s {
                   uv_link_shutdown_cb cb,
                   void* arg);
 
+  void (*close)(uv_link_t* link, uv_link_t* source, uv_link_close_cb cb);
+
   /* Overriding callbacks */
   uv_link_alloc_cb alloc_cb_override;
   uv_link_read_cb read_cb_override;
@@ -59,10 +62,14 @@ struct uv_link_s {
   /* Private, used for chain/unchain */
   uv_link_alloc_cb saved_alloc_cb;
   uv_link_read_cb saved_read_cb;
+
+  /* Private, used for close */
+  uv_link_t* saved_close_source;
+  uv_link_close_cb saved_close_cb;
 };
 
 UV_EXTERN int uv_link_init(uv_link_t* link, uv_link_methods_t const* methods);
-UV_EXTERN void uv_link_close(uv_link_t* link);
+static void uv_link_close(uv_link_t* link, uv_link_close_cb cb);
 
 UV_EXTERN int uv_link_chain(uv_link_t* from, uv_link_t* to);
 UV_EXTERN int uv_link_unchain(uv_link_t* from, uv_link_t* to);
@@ -89,8 +96,15 @@ static int uv_link_propagate_shutdown(uv_link_t* link,
   return link->methods->shutdown(link, source, cb, arg);
 }
 
+void uv_link_propagate_close(uv_link_t* link, uv_link_t* source,
+                             uv_link_close_cb cb);
+
 /* Use this to invoke methods of `link` */
 
+void uv_link_close(uv_link_t* link, uv_link_close_cb cb) {
+  return uv_link_propagate_close(link, link, cb);
+}
+
 static int uv_link_read_start(uv_link_t* link) {
   return link->methods->read_start(link);
 }
@@ -122,12 +136,14 @@ struct uv_link_source_s {
   uv_link_t link;
 
   uv_stream_t* stream;
+
+  uv_link_close_cb close_cb;
+  uv_link_t* close_source;
 };
 
 /* NOTE: uses `stream->data` field */
 UV_EXTERN int uv_link_source_init(uv_link_source_t* source,
                                   uv_stream_t* stream);
-UV_EXTERN void uv_link_source_close(uv_link_source_t* source);
 
 /* Link Observer */
 
@@ -143,6 +159,5 @@ struct uv_link_observer_s {
 
 UV_EXTERN int uv_link_observer_init(uv_link_observer_t* observer,
                                     uv_link_t* target);
-UV_EXTERN int uv_link_observer_close(uv_link_observer_t* observer);
 
 #endif  /* INCLUDE_UV_LINK_H_ */
diff --git a/src/uv_link_observer_t.c b/src/uv_link_observer_t.c
index e4deabb..5479f67 100644
--- a/src/uv_link_observer_t.c
+++ b/src/uv_link_observer_t.c
@@ -82,18 +82,35 @@ static void uv_link_observer_read_cb(uv_link_t* link,
 }
 
 
+void uv_link_observer_close(uv_link_t* link, uv_link_t* source,
+                            uv_link_close_cb cb) {
+  uv_link_observer_t* observer;
+
+  observer = container_of(link, uv_link_observer_t, link);
+
+  uv_link_propagate_close(observer->target, source, cb);
+  observer->target = NULL;
+}
+
+
 static uv_link_methods_t uv_link_observer_methods = {
   .read_start = uv_link_observer_read_start,
   .read_stop = uv_link_observer_read_stop,
   .write = uv_link_observer_write,
   .try_write = uv_link_observer_try_write,
   .shutdown = uv_link_observer_shutdown,
+  .close = uv_link_observer_close,
 
   .alloc_cb_override = uv_link_observer_alloc_cb,
   .read_cb_override = uv_link_observer_read_cb
 };
 
 
+static void uv_link_observer_empty_close_cb(uv_link_t* link) {
+  /* no-op */
+}
+
+
 int uv_link_observer_init(uv_link_observer_t* observer,
                           uv_link_t* target) {
   int err;
@@ -108,24 +125,9 @@ int uv_link_observer_init(uv_link_observer_t* observer,
 
   err = uv_link_chain(target, &observer->link);
   if (err != 0) {
-    uv_link_close(&observer->link);
+    uv_link_close(&observer->link, uv_link_observer_empty_close_cb);
     return err;
   }
 
   return 0;
 }
-
-
-int uv_link_observer_close(uv_link_observer_t* observer) {
-  int err;
-
-  err = uv_link_unchain(observer->target, &observer->link);
-  if (err != 0)
-    return err;
-
-  uv_link_close(&observer->link);
-
-  observer->target = NULL;
-
-  return 0;
-}
diff --git a/src/uv_link_source_t.c b/src/uv_link_source_t.c
index c445fab..3d94ab9 100644
--- a/src/uv_link_source_t.c
+++ b/src/uv_link_source_t.c
@@ -137,12 +137,36 @@ static int uv_link_source_shutdown(uv_link_t* link,
 }
 
 
+static void uv_link_source_close_cb(uv_handle_t* handle) {
+  uv_link_source_t* source;
+
+  source = handle->data;
+
+  source->stream = NULL;
+  source->close_cb(source->close_source);
+}
+
+
+static void uv_link_source_close(uv_link_t* link, uv_link_t* source,
+                                 uv_link_close_cb cb) {
+  uv_link_source_t* s;
+
+  s = container_of(link, uv_link_source_t, link);
+
+  s->close_cb = cb;
+  s->close_source = source;
+
+  uv_close((uv_handle_t*) s->stream, uv_link_source_close_cb);
+}
+
+
 static uv_link_methods_t uv_link_source_methods = {
   .read_start = uv_link_source_read_start,
   .read_stop = uv_link_source_read_stop,
   .write = uv_link_source_write,
   .try_write = uv_link_source_try_write,
-  .shutdown = uv_link_source_shutdown
+  .shutdown = uv_link_source_shutdown,
+  .close = uv_link_source_close
 };
 
 
@@ -161,10 +185,3 @@ int uv_link_source_init(uv_link_source_t* source,
 
   return 0;
 }
-
-
-void uv_link_source_close(uv_link_source_t* source) {
-  uv_link_close(&source->link);
-
-  source->stream = NULL;
-}
diff --git a/src/uv_link_t.c b/src/uv_link_t.c
index 08b5064..e930a8f 100644
--- a/src/uv_link_t.c
+++ b/src/uv_link_t.c
@@ -35,10 +35,38 @@ int uv_link_init(uv_link_t* link, uv_link_methods_t const* methods) {
 }
 
 
-void uv_link_close(uv_link_t* link) {
-  link->alloc_cb = NULL;
-  link->read_cb = NULL;
-  link->methods = NULL;
+static void uv_link_close_parent(uv_link_t* link) {
+  uv_link_t* target;
+  uv_link_t* source;
+  uv_link_close_cb cb;
+
+  target = link->parent;
+  source = link->saved_close_source;
+  cb = link->saved_close_cb;
+
+  memset(link, 0, sizeof(*link));
+
+  if (target == NULL)
+    cb(source);
+  else
+    uv_link_propagate_close(target, source, cb);
+}
+
+
+void uv_link_propagate_close(uv_link_t* link, uv_link_t* source,
+                             uv_link_close_cb cb) {
+  CHECK_EQ(link->child, NULL, "uv_link_t: attempt to close chained link");
+  uv_link_methods_t const* methods;
+
+  methods = link->methods;
+
+  if (link->parent != NULL)
+    CHECK_EQ(uv_link_unchain(link->parent, link), 0, "uv_link_unchain()");
+
+  link->saved_close_source = source;
+  link->saved_close_cb = cb;
+
+  methods->close(link, link, uv_link_close_parent);
 }
 
 
diff --git a/test/src/test-uv-link-observer-t.c b/test/src/test-uv-link-observer-t.c
index eaba1ab..3c48908 100644
--- a/test/src/test-uv-link-observer-t.c
+++ b/test/src/test-uv-link-observer-t.c
@@ -7,9 +7,25 @@ static uv_link_t source;
 static uv_link_observer_t observer;
 
 static int observer_read_cb_called;
+static int fake_close_called;
+static int close_cb_called;
+
+static void fake_close(uv_link_t* link,
+                       uv_link_t* src,
+                       uv_link_close_cb cb) {
+  CHECK_EQ(link, &source, "fake_close link");
+  /* NOTE: `src` may not be a proper source here */
+
+  fake_close_called++;
+
+  /* Not 100% correct, since it is sync, but works here */
+  cb(src);
+}
 
 static uv_link_methods_t methods = {
-  /* no-op, won't be called */
+  .close = fake_close
+
+  /* rest are no-op, won't be called */
 };
 
 static void observer_read_cb(uv_link_observer_t* o,
@@ -23,6 +39,13 @@ static void observer_read_cb(uv_link_observer_t* o,
 }
 
 
+static void close_cb(uv_link_t* link) {
+  CHECK_EQ(link, &observer.link, "close_cb link");
+
+  close_cb_called++;
+}
+
+
 TEST_IMPL(uv_link_observer_t) {
   uv_buf_t buf;
 
@@ -39,6 +62,7 @@ TEST_IMPL(uv_link_observer_t) {
   uv_link_propagate_read_cb(&source, 1, &buf);
   CHECK_EQ(observer_read_cb_called, 1, "observer.read_cb must be called");
 
-  CHECK_EQ(uv_link_observer_close(&observer), 0, "uv_link_observer_close");
-  uv_link_close(&source);
+  uv_link_close(&observer.link, close_cb);
+  CHECK_EQ(fake_close_called, 1, "fake close count");
+  CHECK_EQ(close_cb_called, 1, "close_cb count");
 }
diff --git a/test/src/test-uv-link-source-t.c b/test/src/test-uv-link-source-t.c
index ba445a7..1d12ac0 100644
--- a/test/src/test-uv-link-source-t.c
+++ b/test/src/test-uv-link-source-t.c
@@ -12,6 +12,7 @@ static int test_arg;
 static int write_cb_called;
 static int alloc_cb_called;
 static int read_cb_called;
+static int close_cb_called;
 
 static void read_one() {
   char buf[1024];
@@ -103,6 +104,12 @@ static void test_reads() {
 }
 
 
+static void close_cb(uv_link_t* link) {
+  CHECK_EQ(link, &source.link, "close_cb link");
+  close_cb_called++;
+}
+
+
 TEST_IMPL(uv_link_source_t) {
   CHECK_NE(loop = uv_default_loop(), NULL, "uv_default_loop()");
 
@@ -117,8 +124,10 @@ TEST_IMPL(uv_link_source_t) {
   test_writes();
   test_reads();
 
-  uv_link_source_close(&source);
-  uv_close((uv_handle_t*) &pair_right, NULL);
+  uv_link_close(&source.link, close_cb);
+  CHECK_EQ(uv_run(loop, UV_RUN_DEFAULT), 0, "uv_run()");
+  CHECK_EQ(close_cb_called, 1, "close_cb count");
 
   CHECK_EQ(close(fds[0]), 0, "close(fds[0])");
+  CHECK_NE(close(fds[1]), 0, "close(fds[1]) must fail");
 }