From 682e922100c1b4813e46cd244ee036dc9c3ac7c4 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov <kostja@tarantool.org> Date: Tue, 13 Oct 2015 17:37:24 +0300 Subject: [PATCH] arm: add error factory to raise errors from plain C Add exception methods to plain C. Add an error factory to raise exceptions from plain C. --- src/box/applier.cc | 10 +++---- src/box/error.cc | 7 ++--- src/box/error.h | 4 +-- src/box/iproto.cc | 6 ++-- src/box/iproto_port.cc | 6 ++-- src/box/iproto_port.h | 2 +- src/box/lua/call.cc | 2 +- src/box/memtx_engine.cc | 8 ++--- src/box/recovery.cc | 2 +- src/box/relay.cc | 4 +-- src/coio.cc | 4 +-- src/coro.c | 4 ++- src/diag.c | 11 ++++--- src/diag.h | 47 +++++++++++++++++++++++------ src/evio.cc | 30 +++++++++---------- src/exception.cc | 65 ++++++++++++++++++++++++++++++++--------- src/exception.h | 8 ++++- src/fiber.cc | 7 +---- src/fiber.h | 11 ++++--- src/lua/fiber.cc | 8 +---- src/main.cc | 6 ++-- test/unit/fiber.cc | 2 +- test/unit/fiber.result | 2 -- 23 files changed, 161 insertions(+), 95 deletions(-) diff --git a/src/box/applier.cc b/src/box/applier.cc index 1d937b4564..dea38ed3ad 100644 --- a/src/box/applier.cc +++ b/src/box/applier.cc @@ -272,7 +272,7 @@ applier_subscribe(struct applier *applier, struct recovery *r) * in applier_f(). */ static inline void -applier_log_exception(struct applier *applier, Exception *e) +applier_log_error(struct applier *applier, struct error *e) { if (type_cast(FiberCancelException, e)) return; @@ -295,17 +295,17 @@ applier_log_exception(struct applier *applier, Exception *e) default: break; } - e->log(); + error_log(e); if (type_cast(SocketError, e)) say_info("will retry every %i second", RECONNECT_DELAY); applier->warning_said = true; } static inline void -applier_disconnect(struct applier *applier, Exception *e, +applier_disconnect(struct applier *applier, struct error *e, enum applier_state state) { - applier_log_exception(applier, e); + applier_log_error(applier, e); coio_close(loop(), &applier->io); iobuf_reset(applier->iobuf); applier_set_state(applier, state); @@ -411,7 +411,7 @@ applier_wait(struct applier *applier) assert(applier->reader != NULL); auto fiber_guard = make_scoped_guard([=] { applier->reader = NULL; }); fiber_join(applier->reader); - fiber_testerror(); + diag_raise(); } struct applier * diff --git a/src/box/error.cc b/src/box/error.cc index 56b319c7ed..5ef7e5ad5a 100644 --- a/src/box/error.cc +++ b/src/box/error.cc @@ -76,7 +76,7 @@ ClientError::log() const uint32_t -ClientError::get_errcode(const Exception *e) +ClientError::get_errcode(const struct error *e) { ClientError *client_error = type_cast(ClientError, e); if (client_error) @@ -99,9 +99,8 @@ box_error_type(const box_error_t *e) } uint32_t -box_error_code(const box_error_t *error) +box_error_code(const box_error_t *e) { - Exception *e = (Exception *) error; return ClientError::get_errcode(e); } @@ -111,7 +110,7 @@ box_error_message(const box_error_t *error) return error->errmsg; } -const box_error_t * +box_error_t * box_error_last(void) { return diag_last_error(&fiber()->diag); diff --git a/src/box/error.h b/src/box/error.h index d7dc50d297..ae76cd2ad4 100644 --- a/src/box/error.h +++ b/src/box/error.h @@ -63,7 +63,7 @@ class ClientError: public Exception { ClientError(const char *file, unsigned line, const char *msg, uint32_t errcode); - static uint32_t get_errcode(const Exception *e); + static uint32_t get_errcode(const struct error *e); private: /* client errno code */ int m_errcode; @@ -145,7 +145,7 @@ box_error_message(const box_error_t *error); * * \return last error. */ -API_EXPORT const box_error_t * +API_EXPORT box_error_t * box_error_last(void); /** diff --git a/src/box/iproto.cc b/src/box/iproto.cc index a0abef1fb5..9e3933b367 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -653,7 +653,7 @@ tx_process_msg(struct cmsg *m) */ if (port.found) obuf_rollback_to_svp(out, &port.svp); - throw (Exception *) box_error_last(); + diag_raise(); } break; } @@ -666,7 +666,7 @@ tx_process_msg(struct cmsg *m) assert(msg->request.type == msg->header.type); struct tuple *tuple; if (box_process1(&msg->request, &tuple) < 0) - throw (Exception *) box_error_last(); + diag_raise(); struct obuf_svp svp = iproto_prepare_select(out); if (tuple) tuple_to_obuf(tuple, out); @@ -997,7 +997,7 @@ iproto_set_listen(const char *uri) fiber_yield(); if (! diag_is_empty(&msg.diag)) { diag_move(&msg.diag, &fiber()->diag); - fiber_testerror(); + diag_raise(); } } diff --git a/src/box/iproto_port.cc b/src/box/iproto_port.cc index 2f07732855..5cefa0f1af 100644 --- a/src/box/iproto_port.cc +++ b/src/box/iproto_port.cc @@ -82,9 +82,9 @@ iproto_reply_ok(struct obuf *out, uint64_t sync) } void -iproto_reply_error(struct obuf *out, const Exception *e, uint64_t sync) +iproto_reply_error(struct obuf *out, const struct error *e, uint64_t sync) { - uint32_t msg_len = strlen(e->get_errmsg()); + uint32_t msg_len = strlen(e->errmsg); uint32_t errcode = ClientError::get_errcode(e); struct iproto_header_bin header = iproto_header_bin; @@ -99,7 +99,7 @@ iproto_reply_error(struct obuf *out, const Exception *e, uint64_t sync) obuf_dup(out, &header, sizeof(header)); obuf_dup(out, &body, sizeof(body)); - obuf_dup(out, e->get_errmsg(), msg_len); + obuf_dup(out, e->errmsg, msg_len); } static inline struct iproto_port * diff --git a/src/box/iproto_port.h b/src/box/iproto_port.h index f1b62b7bd8..ab6aef3b12 100644 --- a/src/box/iproto_port.h +++ b/src/box/iproto_port.h @@ -86,7 +86,7 @@ iproto_reply_ok(struct obuf *out, uint64_t sync); /** Send an error packet back. */ void -iproto_reply_error(struct obuf *out, const Exception *e, uint64_t sync); +iproto_reply_error(struct obuf *out, const struct error *e, uint64_t sync); struct obuf_svp iproto_prepare_select(struct obuf *buf); diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index c6c67e04c2..54914e1363 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -464,7 +464,7 @@ execute_c_call(struct func *func, struct request *request, struct obuf *out) } if (rc != 0) { - fiber_testerror(); + diag_raise(); tnt_raise(ClientError, ER_PROC_C, "unknown procedure error"); } diff --git a/src/box/memtx_engine.cc b/src/box/memtx_engine.cc index 7ebe795567..91aa12c064 100644 --- a/src/box/memtx_engine.cc +++ b/src/box/memtx_engine.cc @@ -1116,9 +1116,9 @@ MemtxEngine::waitCheckpoint() /* wait for memtx-part snapshot completion */ int result = cord_cojoin(&m_checkpoint->cord); - Exception *e = (Exception *) diag_last_error(&fiber()->diag); + struct error *e = diag_last_error(&fiber()->diag); if (e != NULL) { - e->log(); + error_log(e); result = -1; SystemError *se = type_cast(SystemError, e); if (se) @@ -1163,9 +1163,9 @@ MemtxEngine::abortCheckpoint() /* wait for memtx-part snapshot completion */ cord_cojoin(&m_checkpoint->cord); - Exception *e = (Exception *) diag_last_error(&fiber()->diag); + struct error *e = diag_last_error(&fiber()->diag); if (e) - e->log(); + error_log(e); m_checkpoint->waiting_for_snap_thread = false; } diff --git a/src/box/recovery.cc b/src/box/recovery.cc index d3b176d049..25a54b9a86 100644 --- a/src/box/recovery.cc +++ b/src/box/recovery.cc @@ -536,7 +536,7 @@ recovery_stop_local(struct recovery *r) r->watcher = NULL; fiber_cancel(f); fiber_join(f); - fiber_testerror(); + diag_raise(); } } diff --git a/src/box/relay.cc b/src/box/relay.cc index 25dd6cf065..c302f52295 100644 --- a/src/box/relay.cc +++ b/src/box/relay.cc @@ -111,7 +111,7 @@ relay_join(int fd, struct xrow_header *packet, cord_costart(&relay.cord, "join", relay_join_f, &relay); cord_cojoin(&relay.cord); - fiber_testerror(); + diag_raise(); /** * Call the server-side hook which stores the replica uuid * in _cluster space after sending the last row but before @@ -255,7 +255,7 @@ relay_subscribe(int fd, struct xrow_header *packet, struct cord cord; cord_costart(&cord, "subscribe", relay_subscribe_f, &relay); cord_cojoin(&cord); - fiber_testerror(); + diag_raise(); } void diff --git a/src/coio.cc b/src/coio.cc index c803a79eae..7406d3f43c 100644 --- a/src/coio.cc +++ b/src/coio.cc @@ -609,8 +609,8 @@ coio_service_on_accept(struct evio_service *evio_service, try { iobuf = iobuf_new(); f = fiber_new(fiber_name, service->handler); - } catch (Exception *e) { - e->log(); + } catch (struct error *e) { + error_log(e); say_error("can't create a handler fiber, dropping client connection"); evio_close(loop(), &coio); if (iobuf) diff --git a/src/coro.c b/src/coro.c index 1e1c783e11..5e68f40699 100644 --- a/src/coro.c +++ b/src/coro.c @@ -36,7 +36,7 @@ #include <sys/mman.h> #include "small/slab_cache.h" #include "third_party/valgrind/memcheck.h" - +#include "diag.h" int tarantool_coro_create(struct tarantool_coro *coro, @@ -53,6 +53,8 @@ tarantool_coro_create(struct tarantool_coro *coro, + slab_sizeof(); if (coro->stack == NULL) { + diag_set(OutOfMemory, coro->stack_size + slab_sizeof(), + "runtime arena", "coro stack"); return -1; } diff --git a/src/diag.c b/src/diag.c index 9477087a16..a63ef85ee5 100644 --- a/src/diag.c +++ b/src/diag.c @@ -31,14 +31,17 @@ #include "diag.h" #include "fiber.h" +/* Must be set by the library user */ +struct error_factory *error_factory = NULL; + void error_create(struct error *e, - void (*destroy)(struct error *), - const struct type *type, - const char *file, - unsigned line) + error_f destroy, error_f raise, error_f log, + const struct type *type, const char *file, unsigned line) { e->destroy = destroy; + e->raise = raise; + e->log = log; e->type = type; e->refs = 0; if (file != NULL) { diff --git a/src/diag.h b/src/diag.h index 39bcdf1591..f2a1258959 100644 --- a/src/diag.h +++ b/src/diag.h @@ -34,6 +34,7 @@ #include <stddef.h> #include <stdbool.h> #include <assert.h> +#include "say.h" #if defined(__cplusplus) extern "C" { @@ -45,6 +46,11 @@ enum { }; struct type; +struct error; +struct error_factory; +extern struct error_factory *error_factory; + +typedef void (*error_f)(struct error *e); /** * Error diagnostics needs to be equally usable in C and C++ @@ -61,7 +67,9 @@ struct type; * (destroy) is there to gracefully delete C++ exceptions from C. */ struct error { - void (*destroy)(struct error *e); + error_f destroy; + error_f raise; + error_f log; const struct type *type; int refs; /** Line number. */ @@ -87,12 +95,22 @@ error_unref(struct error *e) e->destroy(e); } +static inline void +error_raise(struct error *e) +{ + e->raise(e); +} + +static inline void +error_log(struct error *e) +{ + e->log(e); +} + void error_create(struct error *e, - void (*destroy)(struct error *e), - const struct type *type, - const char *file, - unsigned line); + error_f create, error_f raise, error_f log, + const struct type *type, const char *file, unsigned line); void error_format_msg(struct error *e, const char *format, ...); @@ -192,13 +210,24 @@ diag_last_error(struct diag *diag) return diag->last; } -/** - * A helper for tnt_error to avoid cyclic includes (fiber.h and exception.h) - * \cond false - * */ +struct error_factory { + struct error *(*OutOfMemory)(const char *file, + unsigned line, size_t amount, + const char *allocator, + const char *object); +}; + struct diag * diag_get(); +#define diag_set(class, ...) ({ \ + say_debug("%s at %s:%i", #class, __FILE__, __LINE__); \ + struct error *e = error_factory->class(__FILE__, __LINE__, \ + ##__VA_ARGS__); \ + diag_add_error(diag_get(), e); \ + e; \ +}) + #if defined(__cplusplus) } /* extern "C" */ #endif /* defined(__cplusplus) */ diff --git a/src/evio.cc b/src/evio.cc index 337a4c72d4..cb71d68c81 100644 --- a/src/evio.cc +++ b/src/evio.cc @@ -201,29 +201,27 @@ evio_service_bind_addr(struct evio_service *service) int fd = sio_socket(service->addr.sa_family, SOCK_STREAM, IPPROTO_TCP); - try { - evio_setsockopt_server(fd, service->addr.sa_family, SOCK_STREAM); - - if (sio_bind(fd, &service->addr, service->addr_len) || - sio_listen(fd)) { - assert(errno == EADDRINUSE); - close(fd); - return -1; - } - say_info("%s: bound to %s", evio_service_name(service), - sio_strfaddr(&service->addr, service->addr_len)); + auto fd_guard = make_scoped_guard([=]{ close(fd); }); - /* Invoke on_bind callback if it is set. */ - if (service->on_bind) - service->on_bind(service->on_bind_param); + evio_setsockopt_server(fd, service->addr.sa_family, SOCK_STREAM); - } catch (Exception *e) { + if (sio_bind(fd, &service->addr, service->addr_len) || + sio_listen(fd)) { + assert(errno == EADDRINUSE); close(fd); - throw; + return -1; } + say_info("%s: bound to %s", evio_service_name(service), + sio_strfaddr(&service->addr, service->addr_len)); + + /* Invoke on_bind callback if it is set. */ + if (service->on_bind) + service->on_bind(service->on_bind_param); + /* Register the socket in the event loop. */ ev_io_set(&service->ev, fd, EV_READ); ev_io_start(service->loop, &service->ev); + fd_guard.is_active = false; return 0; } diff --git a/src/exception.cc b/src/exception.cc index 9659942886..0c739b6037 100644 --- a/src/exception.cc +++ b/src/exception.cc @@ -29,12 +29,35 @@ * SUCH DAMAGE. */ #include "exception.h" -#include "say.h" #include <stdio.h> #include <string.h> #include <errno.h> +extern "C" { + +static void +exception_destroy(struct error *e) +{ + delete (Exception *) e; +} + +static void +exception_raise(struct error *error) +{ + Exception *e = (Exception *) error; + e->raise(); +} + +static void +exception_log(struct error *error) +{ + Exception *e = (Exception *) error; + e->log(); +} + +} /* extern "C" */ + /** out_of_memory::size is zero-initialized by the linker. */ static OutOfMemory out_of_memory(__FILE__, __LINE__, sizeof(OutOfMemory), "malloc", "exception"); @@ -72,22 +95,11 @@ Exception::~Exception() } } -extern "C" void -exception_destroy(struct error *msg) -{ - delete (Exception *) msg; -} - Exception::Exception(const struct type *type_arg, const char *file, unsigned line) { - error_create(this, exception_destroy, type_arg, - file, line); - if (this == &out_of_memory) { - /* A special workaround for out_of_memory static init */ - out_of_memory.refs = 1; - return; - } + error_create(this, exception_destroy, exception_raise, + exception_log, type_arg, file, line); } void @@ -143,6 +155,18 @@ OutOfMemory::OutOfMemory(const char *file, unsigned line, (unsigned) amount, allocator, object); } +static struct error * +BuildOutOfMemory(const char *file, unsigned line, + size_t amount, const char *allocator, + const char *object) +{ + void *p = malloc(sizeof(OutOfMemory)); + if (p == NULL) + return &out_of_memory; + return new (p) OutOfMemory(file, line, amount, allocator, + object); +} + const struct type type_TimedOut = make_type("TimedOut", &type_SystemError); @@ -151,3 +175,16 @@ TimedOut::TimedOut(const char *file, unsigned line) { m_errno = ETIMEDOUT; } + +void +exception_init() +{ + static struct error_factory exception_error_factory; + + exception_error_factory.OutOfMemory = BuildOutOfMemory; + + error_factory = &exception_error_factory; + + /* A special workaround for out_of_memory static init */ + out_of_memory.refs = 1; +} diff --git a/src/exception.h b/src/exception.h index 9ba9219965..810ecf2f66 100644 --- a/src/exception.h +++ b/src/exception.h @@ -35,13 +35,13 @@ #include "reflection.h" #include "diag.h" -#include "say.h" extern const struct type type_Exception; class Exception: public error { public: void *operator new(size_t size); + void *operator new(size_t size, void *p) { (void) size; return p; } void operator delete(void*); const char *get_file() const { return file; } @@ -92,6 +92,12 @@ class TimedOut: public SystemError { virtual void raise() { throw this; } }; +/** + * Initialize the exception subsystem. + */ +void +exception_init(); + #define tnt_error(class, ...) ({ \ say_debug("%s at %s:%i", #class, __FILE__, __LINE__); \ class *e = new class(__FILE__, __LINE__, ##__VA_ARGS__); \ diff --git a/src/fiber.cc b/src/fiber.cc index d1f0f361c0..c9c33acf27 100644 --- a/src/fiber.cc +++ b/src/fiber.cc @@ -418,10 +418,6 @@ fiber_loop(void *data __attribute__((unused))) * propagate up to the joiner. */ diag_clear(&fiber->diag); - } catch (FiberCancelException *e) { - say_info("fiber `%s' has been cancelled", - fiber_name(fiber)); - say_info("fiber `%s': exiting", fiber_name(fiber)); } catch (Exception *e) { /* * For joinable fibers, it's the business @@ -495,8 +491,7 @@ fiber_new(const char *name, void (*f) (va_list)) if (tarantool_coro_create(&fiber->coro, &cord->slabc, fiber_loop, NULL)) { - tnt_raise(OutOfMemory, 65536, - "runtime arena", "coro stack"); + diag_raise(); } region_create(&fiber->gc, &cord->slabc); diff --git a/src/fiber.h b/src/fiber.h index e8052b6d5e..5b9655748d 100644 --- a/src/fiber.h +++ b/src/fiber.h @@ -430,7 +430,9 @@ class FiberCancelException: public Exception { } virtual void log() const { - say_debug("FiberCancelException"); + say_info("fiber `%s' has been cancelled", + fiber_name(fiber())); + say_info("fiber `%s': exiting", fiber_name(fiber())); } virtual void raise() { throw this; } }; @@ -454,12 +456,13 @@ fiber_testcancel(void) tnt_raise(FiberCancelException); } + static inline void -fiber_testerror(void) +diag_raise(void) { - Exception *e = (Exception *) diag_last_error(&fiber()->diag); + struct error *e = diag_last_error(&fiber()->diag); if (e) - e->raise(); + error_raise(e); } #endif /* defined(__cplusplus) */ diff --git a/src/lua/fiber.cc b/src/lua/fiber.cc index a028f4ec86..e1d7da5dbb 100644 --- a/src/lua/fiber.cc +++ b/src/lua/fiber.cc @@ -300,13 +300,7 @@ box_lua_fiber_run(va_list ap) lua_unref(L, storage_ref); }); - try { - lbox_call(L, lua_gettop(L) - 1, LUA_MULTRET); - } catch (FiberCancelException *e) { - throw; - } catch (Exception *e) { - e->log(); - } + lbox_call(L, lua_gettop(L) - 1, LUA_MULTRET); } /** diff --git a/src/main.cc b/src/main.cc index a5216eaf11..2ca520e935 100644 --- a/src/main.cc +++ b/src/main.cc @@ -629,6 +629,8 @@ main(int argc, char **argv) main_argc = argc; main_argv = argv; + exception_init(); + fiber_init(); /* Init iobuf library with default readahead */ iobuf_init(); @@ -660,8 +662,8 @@ main(int argc, char **argv) ev_now_update(loop()); ev_run(loop(), 0); } - } catch (Exception *e) { - e->log(); + } catch (struct error *e) { + error_log(e); panic("%s", "fatal error, exiting the event loop"); } diff --git a/test/unit/fiber.cc b/test/unit/fiber.cc index c6a8d8983d..dc87e311bf 100644 --- a/test/unit/fiber.cc +++ b/test/unit/fiber.cc @@ -64,7 +64,7 @@ fiber_join_test() fiber_wakeup(fiber); try { fiber_join(fiber); - fiber_testerror(); + diag_raise(); fail("exception not raised", ""); } catch (Exception *e) { note("exception propagated"); diff --git a/test/unit/fiber.result b/test/unit/fiber.result index a0ea80cf1c..51cc228311 100644 --- a/test/unit/fiber.result +++ b/test/unit/fiber.result @@ -1,5 +1,3 @@ -(null): fiber `cancel' has been cancelled -(null): fiber `cancel': exiting *** fiber_join_test *** # exception propagated # cancel dead has started -- GitLab