api: experimental error reporting
This commit is contained in:
parent
a66bb042e0
commit
af170cc9bb
|
@ -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.
|
||||
|
|
24
docs/api.md
24
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
"src/test-uv-link-observer-t.c",
|
||||
"src/test-defaults.c",
|
||||
"src/test-close.c",
|
||||
"src/test-strerror.c",
|
||||
],
|
||||
}],
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue