diff --git a/src/box/applier.cc b/src/box/applier.cc index 1d937b456454faa8969b3f49fe8522eb11e2f586..dea38ed3ad0eca44395891e85a4ee497d2ebe66b 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 56b319c7edda91d5833c47339a12864f7e309c32..5ef7e5ad5a68ea36d02925aede34b9426e8657e8 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 d7dc50d2974b6c94dc2aa3184c983ec14e9e9c45..ae76cd2ad46573e77c3b24eab2301266f542d213 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 a0abef1fb528b417560d4939075fe77682aad937..9e3933b36762a5fc6f1ef975e9e5e92d7e294ecb 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 2f07732855d9adc1d53172c96f6f6751ff5989b5..5cefa0f1afcde2950660e743154a4f091e8ca231 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 f1b62b7bd82c9e4e0f19188fe489c2c6446e69d1..ab6aef3b126b45ec6bc32a3fc9e0ffe327206e23 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 c6c67e04c2979200ac9cbd9a6c248c49bce4edec..54914e13637319291f8fd6d61b4e4d416c703c4c 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 7ebe7955679efa37849a1693a3bc52d3b192611d..91aa12c0645278eb96919a8947f622f497f4eaac 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 d3b176d04995328f162d32948e661cb8a673526a..25a54b9a867e8ff9d55c14785091e86cc5be25eb 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 25dd6cf06567c2d47c5a130a6c39235dfda925b9..c302f52295b88d4957bdeef85182d06e800431ac 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 c803a79eaeeb041d057698ed770614135ab48874..7406d3f43c6419b450d9252c71480e67b1233b87 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 1e1c783e11cb0d195804c4789ab7277c01acfe4c..5e68f406991900aeecfeb3878fbdb134398e6ca8 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 9477087a163000c86c4bd2f35a8d54a8cf015285..a63ef85ee5ad95511f73504abf07856d8f317c5b 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 39bcdf15916984fb2c0d87043d32d96505503382..f2a125895967bcbf7aaa471a448152dc4ff36977 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 337a4c72d4e3b1312601dbe149548d19cf8aa54c..cb71d68c814e64b0c92ee4df23a84ecf294059f0 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 9659942886c2a76fabe964b850b24bdf493e11d2..0c739b6037e4034c3582a5590da4698da96338f6 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 9ba9219965a592703b3997a6e5fa640de13e07b5..810ecf2f66e0b663734c80fb887d8887505cfa43 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 d1f0f361c0831c178a827a7f6f40913bcd51fff3..c9c33acf272deab6c85c9ac16a8b46cf1393a0e6 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 e8052b6d5e1b52d66fc85a15a9a1ca361677ab52..5b9655748dcc57dadf87470891b9549362e22ac9 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 a028f4ec861bd78a66bde4d0415d922468f570c2..e1d7da5dbb852b4c0fe5b0279f9e7bbc9d2d3125 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 a5216eaf1175376c398bd1d3350df61b1bafc5ae..2ca520e93571aa7f2ad40ef74f97953d6ba2726e 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 c6a8d8983d9b3bc42e937c63c28f31958efb3912..dc87e311bf5478cb0f959626cf29470d78d09079 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 a0ea80cf1c571c14d41e19ef2cdc1955a85ecc90..51cc22831120da767dd506f72eecb16f7746748d 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