about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--docs/api.md24
-rw-r--r--docs/implementation-guide.md4
-rw-r--r--include/uv_link_t.h8
-rw-r--r--src/defaults.c5
-rw-r--r--src/uv_link_t.c65
-rw-r--r--test/src/test-list.h1
-rw-r--r--test/src/test-strerror.c73
-rw-r--r--test/test.gyp1
9 files changed, 175 insertions, 11 deletions
diff --git a/README.md b/README.md
index 31952b9..a8287c6 100644
--- a/README.md
+++ b/README.md
@@ -80,11 +80,6 @@ static int shutdown_impl(uv_link_t* link,
 [API Docs][2]
 [Implementation Guide][3]
 
-## Further Work
-
-* Error reporting. Right now all we get is a UV_... error, it would be nice to
-  have a method for obtaining string description.
-
 ## LICENSE
 
 This software is licensed under the MIT License.
diff --git a/docs/api.md b/docs/api.md
index 2d2877b..0d7e634 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -118,6 +118,16 @@ Invoke `shutdown` from the link's [`uv_link_methods_t`][]. Acts similarly to
 `uv_shutdown()`. `cb(uv_link_t* link, int status, void* arg)` is invoked on
 completion.
 
+### const char* uv_link_strerror(...)
+
+* `uv_link_t* link`
+* `int err` - error code, previously either returned the one of the
+  `uv_link...` methods or passed as a negative `nread` to `link->read_cb`
+
+Invoke `strerror` from the link's [`uv_link_methods_t`][]. Acts similarly to
+`uv_strerror()`. Returns a description of error code that has just been given
+back to the user.
+
 ### void uv_link_propagate_alloc_cb(...)
 
 Should be used only by [`uv_link_methods_t`][] implementation.
@@ -291,6 +301,7 @@ int uv_link_default_shutdown(uv_link_t* link,
                              void* arg);
 void uv_link_default_close(uv_link_t* link, uv_link_t* source,
                            uv_link_close_cb cb);
+const char* uv_link_default_strerror(uv_link_t* link, int err);
 ```
 
 These maybe used for [`uv_methods_talloc_cb_override`][] and
@@ -385,6 +396,18 @@ is passed only only for internal operation.
 
 *NOTE: semantics are the same as of `uv_close`.*
 
+### .strerror
+
+```c
+const char* (*strerror)(uv_link_t* link, int err);
+```
+
+Invoked by [`uv_link_strerror()`][].
+
+Should return a description string of the `err` that was emitted by the `link`.
+
+*NOTE: semantics are the same as of `uv_strerror`.*
+
 ### .alloc_cb_override
 
 A method used to override that value of [`uv_link_t.alloc_cb`][] by
@@ -468,6 +491,7 @@ Invoked by `uv_link_propagate_read_cb`. MUST not manage the data in `buf`.
 
 [`uv_link_chain()`]: #int-uv_link_chain
 [`uv_link_close()`]: #void-uv_link_close
+[`uv_link_strerror()`]: #const-char-uv_link_strerror
 [`uv_link_init()`]: #int-uv_link_init
 [`uv_link_methods_t`]: #uv_link_methods_t
 [`uv_link_observer_t.observer_read_cb`]: #observer_read_cb
diff --git a/docs/implementation-guide.md b/docs/implementation-guide.md
index 9393bdc..65fb8fe 100644
--- a/docs/implementation-guide.md
+++ b/docs/implementation-guide.md
@@ -3,6 +3,10 @@
 `uv_link_t` behaves very similar to [`uv_stream_t`][0]. All `uv_link_methods_t`
 MUST conform to this semantics.
 
+## Error Codes
+
+All error codes MUST be negative and be less than `UV_ERRNO_MAX`.
+
 ## uv_link_init()
 
 Links start in non-reading mode, `alloc_cb`/`read_cb` MUST NOT be called until
diff --git a/include/uv_link_t.h b/include/uv_link_t.h
index 61d4cf0..2f09d36 100644
--- a/include/uv_link_t.h
+++ b/include/uv_link_t.h
@@ -40,6 +40,8 @@ struct uv_link_methods_s {
 
   void (*close)(uv_link_t* link, uv_link_t* source, uv_link_close_cb cb);
 
+  const char* (*strerror)(uv_link_t* link, int err);
+
   /* Overriding callbacks */
   uv_link_alloc_cb alloc_cb_override;
   uv_link_read_cb read_cb_override;
@@ -63,6 +65,9 @@ struct uv_link_methods_s {
     uv_link_alloc_cb saved_alloc_cb;                                          \
     uv_link_read_cb saved_read_cb;                                            \
                                                                               \
+    /* Private, used for error reporting */                                   \
+    unsigned int err_prefix;                                                  \
+                                                                              \
     /* Private, used for close */                                             \
     int close_depth;                                                          \
     int close_waiting;                                                        \
@@ -120,6 +125,8 @@ static int uv_link_shutdown(uv_link_t* link, uv_link_shutdown_cb cb,
   return uv_link_propagate_shutdown(link, link, cb, arg);
 }
 
+const char* uv_link_strerror(uv_link_t* link, int err);
+
 /* Link Source */
 
 struct uv_link_source_s {
@@ -170,6 +177,7 @@ int uv_link_default_shutdown(uv_link_t* link,
                              void* arg);
 void uv_link_default_close(uv_link_t* link, uv_link_t* source,
                            uv_link_close_cb cb);
+const char* uv_link_default_strerror(uv_link_t* link, int err);
 
 void uv_link_default_alloc_cb_override(uv_link_t* link,
                                        size_t suggested_size,
diff --git a/src/defaults.c b/src/defaults.c
index 1c486f8..5df3862 100644
--- a/src/defaults.c
+++ b/src/defaults.c
@@ -43,6 +43,11 @@ void uv_link_default_close(uv_link_t* link, uv_link_t* source,
 }
 
 
+const char* uv_link_default_strerror(uv_link_t* link, int err) {
+  return NULL;
+}
+
+
 void uv_link_default_alloc_cb_override(uv_link_t* link,
                                        size_t suggested_size,
                                        uv_buf_t* buf) {
diff --git a/src/uv_link_t.c b/src/uv_link_t.c
index f83a8a6..3df5264 100644
--- a/src/uv_link_t.c
+++ b/src/uv_link_t.c
@@ -3,8 +3,27 @@
 
 #include "src/common.h"
 
+static const size_t kErrorPrefixShift = 16;
+static const int kErrorValueMask = (1 << kErrorPrefixShift) - 1;
+static const unsigned int kErrorPrefixMask = ~kErrorValueMask;
+
 static void uv_link_maybe_close(uv_link_t* link);
 
+
+static int uv_link_error(uv_link_t* link, int err) {
+  if (link == NULL)
+    return err;
+
+  if (err >= UV_ERRNO_MAX)
+    return err;
+
+  if (((-err) & kErrorPrefixMask) != 0)
+    return err;
+
+  return -((-err) | link->err_prefix);
+}
+
+
 static void uv_link_def_alloc_cb(uv_link_t* link,
                                  size_t suggested_size,
                                  uv_buf_t* buf) {
@@ -32,6 +51,7 @@ int uv_link_init(uv_link_t* link, uv_link_methods_t const* methods) {
 
   link->alloc_cb = uv_link_def_alloc_cb;
   link->read_cb = uv_link_def_read_cb;
+  link->err_prefix = 1 << kErrorPrefixShift;
 
   link->methods = methods;
 
@@ -46,7 +66,7 @@ int uv_link_init(uv_link_t* link, uv_link_methods_t const* methods) {
       err = (RES);                                                            \
       if (--link->close_depth == 0)                                           \
         uv_link_maybe_close(link);                                            \
-      return err;                                                             \
+      return uv_link_error(link, err);                                        \
     } while (0)
 
 
@@ -55,7 +75,7 @@ int uv_link_propagate_write(uv_link_t* link, uv_link_t* source,
                             uv_stream_t* send_handle,
                             uv_link_write_cb cb, void* arg) {
   if (link == NULL)
-    return UV_EFAULT;
+    return uv_link_error(link, UV_EFAULT);
   CLOSE_WRAP(link->methods->write(link, source, bufs, nbufs, send_handle, cb,
                                   arg));
 }
@@ -66,21 +86,21 @@ int uv_link_propagate_shutdown(uv_link_t* link,
                                uv_link_shutdown_cb cb,
                                void* arg) {
   if (link == NULL)
-    return UV_EFAULT;
+    return uv_link_error(link, UV_EFAULT);
   CLOSE_WRAP(link->methods->shutdown(link, source, cb, arg));
 }
 
 
 int uv_link_read_start(uv_link_t* link) {
   if (link == NULL)
-    return UV_EFAULT;
+    return uv_link_error(link, UV_EFAULT);
   CLOSE_WRAP(link->methods->read_start(link));
 }
 
 
 int uv_link_read_stop(uv_link_t* link) {
   if (link == NULL)
-    return UV_EFAULT;
+    return uv_link_error(link, UV_EFAULT);
   CLOSE_WRAP(link->methods->read_stop(link));
 }
 
@@ -89,7 +109,7 @@ int uv_link_try_write(uv_link_t* link,
                       const uv_buf_t bufs[],
                       unsigned int nbufs) {
   if (link == NULL)
-    return UV_EFAULT;
+    return uv_link_error(link, UV_EFAULT);
   CLOSE_WRAP(link->methods->try_write(link, bufs, nbufs));
 }
 
@@ -154,6 +174,15 @@ void uv_link_propagate_close(uv_link_t* link, uv_link_t* source,
 }
 
 
+static void uv_link_recalculate_prefixes(uv_link_t* link) {
+  unsigned short prev;
+
+  prev = link->parent->err_prefix >> kErrorPrefixShift;
+  for (; link != NULL; link = link->child)
+    link->err_prefix = (++prev) << kErrorPrefixShift;
+}
+
+
 int uv_link_chain(uv_link_t* from, uv_link_t* to) {
   if (from->child != NULL || to->parent != NULL)
     return UV_EINVAL;
@@ -170,6 +199,8 @@ int uv_link_chain(uv_link_t* from, uv_link_t* to) {
   from->alloc_cb = to->methods->alloc_cb_override;
   from->read_cb = to->methods->read_cb_override;
 
+  uv_link_recalculate_prefixes(to);
+
   return 0;
 }
 
@@ -190,6 +221,24 @@ int uv_link_unchain(uv_link_t* from, uv_link_t* to) {
 }
 
 
+const char* uv_link_strerror(uv_link_t* link, int err) {
+  unsigned int prefix;
+  int local_err;
+
+  if (err >= UV_ERRNO_MAX)
+    return uv_strerror(err);
+
+  prefix = (-err) & kErrorPrefixMask;
+  local_err = -((-err) & kErrorValueMask);
+
+  for (; link != NULL; link = link->parent)
+    if (prefix == link->err_prefix)
+      return link->methods->strerror(link, local_err);
+
+  return NULL;
+}
+
+
 void uv_link_propagate_alloc_cb(uv_link_t* link,
                                 size_t suggested_size,
                                 uv_buf_t* buf) {
@@ -215,6 +264,10 @@ void uv_link_propagate_read_cb(uv_link_t* link,
   if (link->child != NULL)
     target = link->child;
 
+  /* Prefix errors */
+  if (nread < 0)
+    nread = uv_link_error(link, nread);
+
   target->close_depth++;
   link->read_cb(target, nread, buf);
   if (--target->close_depth == 0)
diff --git a/test/src/test-list.h b/test/src/test-list.h
index 591b858..abb7cda 100644
--- a/test/src/test-list.h
+++ b/test/src/test-list.h
@@ -6,6 +6,7 @@
     V(uv_link_observer_t)                                                     \
     V(close_depth)                                                            \
     V(stop_read_on_error)                                                     \
+    V(strerror)                                                               \
 
 #define TEST_DECL(N) void test__##N();
 
diff --git a/test/src/test-strerror.c b/test/src/test-strerror.c
new file mode 100644
index 0000000..d2e307c
--- /dev/null
+++ b/test/src/test-strerror.c
@@ -0,0 +1,73 @@
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "test-common.h"
+
+static uv_link_t a_link;
+static uv_link_t b_link;
+
+static int close_cb_called;
+
+
+static int faulty_try_write(uv_link_t* link,
+                            const uv_buf_t bufs[],
+                            unsigned int nbufs) {
+  return UV_ERRNO_MAX - 1;
+}
+
+
+const char* a_strerror(uv_link_t* l, int err) {
+  CHECK_EQ(l, &a_link, "link == a_link");
+  return "a";
+}
+
+
+const char* b_strerror(uv_link_t* l, int err) {
+  CHECK_EQ(l, &b_link, "link == b_link");
+  return "b";
+}
+
+
+static uv_link_methods_t a_methods = {
+  .try_write = faulty_try_write,
+  .strerror = a_strerror,
+  .close = uv_link_default_close
+};
+
+
+static uv_link_methods_t b_methods = {
+  .try_write = faulty_try_write,
+  .strerror = b_strerror,
+  .close = uv_link_default_close,
+
+  .alloc_cb_override = uv_link_default_alloc_cb_override,
+  .read_cb_override = uv_link_default_read_cb_override
+};
+
+
+static void close_cb(uv_link_t* l) {
+  close_cb_called++;
+}
+
+
+TEST_IMPL(strerror) {
+  int err;
+
+  CHECK_EQ(uv_link_init(&a_link, &a_methods), 0, "uv_link_init()");
+  CHECK_EQ(uv_link_init(&b_link, &b_methods), 0, "uv_link_init()");
+  CHECK_EQ(uv_link_chain(&a_link, &b_link), 0, "uv_link_chain()");
+
+  CHECK_EQ(uv_link_strerror(&b_link, UV_ERRNO_MAX - 1), NULL,
+           "unprefixed error should not be found");
+
+  err = uv_link_try_write(&b_link, NULL, 0);
+  CHECK_EQ(strcmp(uv_link_strerror(&b_link, err), "b"), 0,
+           "error description should match");
+
+  err = uv_link_try_write(&a_link, NULL, 0);
+  CHECK_EQ(strcmp(uv_link_strerror(&b_link, err), "a"), 0,
+           "error description should match");
+
+  uv_link_close(&b_link, close_cb);
+  CHECK_EQ(close_cb_called, 1, "close_cb must be called");
+}
diff --git a/test/test.gyp b/test/test.gyp
index 0bd930d..131b93a 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -18,6 +18,7 @@
       "src/test-uv-link-observer-t.c",
       "src/test-defaults.c",
       "src/test-close.c",
+      "src/test-strerror.c",
     ],
   }],
 }