diff --git a/src/box/error.cc b/src/box/error.cc index 8f9bde98775ff00a043a70f2b73e73979686829b..6ccad390efea130d3fd09508ab4f50ae352755bd 100644 --- a/src/box/error.cc +++ b/src/box/error.cc @@ -59,8 +59,11 @@ ClientError::log() const uint32_t -ClientError::get_code_for_foreign_exception(const Exception *e) +ClientError::get_errcode(const Exception *e) { + const ClientError *error = dynamic_cast<const ClientError *>(e); + if (error) + return error->errcode(); if (typeid(*e) == typeid(OutOfMemory)) return ER_MEMORY_ISSUE; return ER_PROC_LUA; diff --git a/src/box/error.h b/src/box/error.h index 908e46c320fa842a02cbb020b74662cfd631943a..caaebc0c6f54ffda39173c14d742c62d44f0e113 100644 --- a/src/box/error.h +++ b/src/box/error.h @@ -51,7 +51,7 @@ class ClientError: public Exception { ClientError(const char *file, unsigned line, const char *msg, uint32_t errcode); - static uint32_t get_code_for_foreign_exception(const Exception *e); + static uint32_t get_errcode(const Exception *e); private: /* client errno code */ int m_errcode; diff --git a/src/box/iproto_port.cc b/src/box/iproto_port.cc index 8cfcb5bfd060460baded7829c46bdb2ab3870590..0624adce98957a840b1eeb37e69842e3393d0edd 100644 --- a/src/box/iproto_port.cc +++ b/src/box/iproto_port.cc @@ -79,20 +79,11 @@ iproto_reply_ok(struct obuf *out, uint64_t sync) obuf_dup(out, &empty_map, sizeof(empty_map)); } -static inline uint32_t -get_errcode(const Exception *e) -{ - const ClientError *error = dynamic_cast<const ClientError *>(e); - if (error) - return error->errcode(); - return ClientError::get_code_for_foreign_exception(e); -} - void iproto_reply_error(struct obuf *out, const Exception *e, uint64_t sync) { uint32_t msg_len = strlen(e->errmsg()); - uint32_t errcode = get_errcode(e); + uint32_t errcode = ClientError::get_errcode(e); struct iproto_header_bin header = iproto_header_bin; struct iproto_body_bin body = iproto_error_bin; diff --git a/src/box/lua/error.cc b/src/box/lua/error.cc index e709a6b12bffa9e3295db3d6ea0b212bbd19e9e5..fa7bcdc825097f43b07dec87b9fa63071cd32d44 100644 --- a/src/box/lua/error.cc +++ b/src/box/lua/error.cc @@ -41,7 +41,7 @@ extern "C" { #include "box/error.h" static int -lbox_raise(lua_State *L) +lbox_error_raise(lua_State *L) { uint32_t code = 0; const char *reason = NULL; @@ -105,6 +105,50 @@ lbox_raise(lua_State *L) return 0; } +static int +lbox_error_last(lua_State *L) +{ + if (lua_gettop(L) >= 1) + luaL_error(L, "box.error.last(): bad arguments"); + + Exception *e = fiber()->exception; + + if (e == NULL) { + lua_pushnil(L); + } else { + lua_newtable(L); + + lua_pushstring(L, "message"); + lua_pushstring(L, e->errmsg()); + lua_settable(L, -3); + + lua_pushstring(L, "type"); + lua_pushstring(L, e->type()); + lua_settable(L, -3); + + lua_pushstring(L, "code"); + lua_pushinteger(L, ClientError::get_errcode(e)); + lua_settable(L, -3); + + if (SystemError *se = dynamic_cast<SystemError *>(e)) { + lua_pushstring(L, "errno"); + lua_pushinteger(L, se->errnum()); + lua_settable(L, -3); + } + } + return 1; +} + +static int +lbox_error_clear(lua_State *L) +{ + if (lua_gettop(L) >= 1) + luaL_error(L, "box.error.clear(): bad arguments"); + + Exception::clear(&fiber()->exception); + return 0; +} + static int lbox_errinj_set(struct lua_State *L) { @@ -155,9 +199,27 @@ box_lua_error_init(struct lua_State *L) { lua_setfield(L, -2, name + 3); } lua_newtable(L); - lua_pushcfunction(L, lbox_raise); - lua_setfield(L, -2, "__call"); + { + lua_pushcfunction(L, lbox_error_raise); + lua_setfield(L, -2, "__call"); + + lua_newtable(L); + { + lua_pushcfunction(L, lbox_error_last); + lua_setfield(L, -2, "last"); + } + { + lua_pushcfunction(L, lbox_error_clear); + lua_setfield(L, -2, "clear"); + } + { + lua_pushcfunction(L, lbox_error_raise); + lua_setfield(L, -2, "raise"); + } + lua_setfield(L, -2, "__index"); + } lua_setmetatable(L, -2); + lua_pop(L, 1); static const struct luaL_reg errinjlib[] = { diff --git a/src/box/replica.cc b/src/box/replica.cc index 0d4b72d7f65232d003c1b62afccb6ae287a88c3a..cfd39d88045d5748c617198af9028c08fb93cfda 100644 --- a/src/box/replica.cc +++ b/src/box/replica.cc @@ -325,7 +325,7 @@ recovery_stop_remote(struct recovery_state *r) * If the remote died from an exception, don't throw it * up. */ - Exception::cleanup(&f->exception); + Exception::clear(&f->exception); fiber_join(f); r->remote.status = "off"; } diff --git a/src/exception.cc b/src/exception.cc index 851942cd3d48dd26ec14f56e502e21a7902bf1c7..7c7af79865d6729fe1a5e9656945188d2253b8b5 100644 --- a/src/exception.cc +++ b/src/exception.cc @@ -84,10 +84,12 @@ Exception::Exception(const Exception& e) memcpy(m_errmsg, e.m_errmsg, sizeof(m_errmsg)); } -/** A quick & dirty version of name demangle for class names */ -static const char * -demangle(const char *name) + +const char * +Exception::type() const { + const char *name = typeid(*this).name(); + /** A quick & dirty version of name demangle for class names */ char *res = NULL; (void) strtol(name, &res, 10); return res && strlen(res) ? res : name; @@ -96,8 +98,7 @@ demangle(const char *name) void Exception::log() const { - _say(S_ERROR, m_file, m_line, m_errmsg, "%s", - demangle(typeid(*this).name())); + _say(S_ERROR, m_file, m_line, m_errmsg, "%s", type()); } diff --git a/src/exception.h b/src/exception.h index 6071eb95bc789940f2af8ec5dc9bf2eea4a7bbe8..c7eec1c14ec925c9ed846cf48b5dc92fabe9524b 100644 --- a/src/exception.h +++ b/src/exception.h @@ -47,6 +47,8 @@ class Exception: public Object { throw this; } + const char *type() const; + const char * errmsg() const { @@ -60,8 +62,8 @@ class Exception: public Object { { *what = NULL; } - /** Clear the last error saved in the current thread's TLS */ - static inline void cleanup(Exception **what) + /** Clear the last error saved in the current fiber's TLS */ + static inline void clear(Exception **what) { if (*what != NULL && (*what)->size > 0) { (*what)->~Exception(); @@ -72,7 +74,7 @@ class Exception: public Object { /** Move an exception from one thread to another. */ static void move(Exception **from, Exception **to) { - Exception::cleanup(to); + Exception::clear(to); *to = *from; Exception::init(from); } diff --git a/src/fiber.cc b/src/fiber.cc index 111aaca7da0c9b4f106119fa603df2b6810263cd..cbffb62147c9e6bb72b000bd8b75256f47319349 100644 --- a/src/fiber.cc +++ b/src/fiber.cc @@ -404,7 +404,7 @@ fiber_loop(void *data __attribute__((unused))) * Make sure a leftover exception does not * propagate up to the joiner. */ - Exception::cleanup(&fiber->exception); + Exception::clear(&fiber->exception); } catch (FiberCancelException *e) { say_info("fiber `%s' has been cancelled", fiber_name(fiber)); @@ -519,7 +519,7 @@ fiber_destroy(struct cord *cord, struct fiber *f) rlist_del(&f->state); region_destroy(&f->gc); tarantool_coro_destroy(&f->coro, &cord->slabc); - Exception::cleanup(&f->exception); + Exception::clear(&f->exception); } void @@ -573,7 +573,7 @@ cord_destroy(struct cord *cord) mh_i32ptr_delete(cord->fiber_registry); } region_destroy(&cord->sched.gc); - Exception::cleanup(&cord->sched.exception); + Exception::clear(&cord->sched.exception); slab_cache_destroy(&cord->slabc); ev_loop_destroy(cord->loop); } @@ -611,7 +611,7 @@ void *cord_thread_func(void *p) * Clear a possible leftover exception object * to not confuse the invoker of the thread. */ - Exception::cleanup(&cord->fiber->exception); + Exception::clear(&cord->fiber->exception); } catch (Exception *) { /* * The exception is now available to the caller diff --git a/test/box/misc.result b/test/box/misc.result index c95502389faba6cb79e9500ae18f68b8d8ff34c9..a7d37b7b8dd343e34d67274c2e06cfba424e7496 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -41,6 +41,10 @@ t = nil ---------------- --# stop server default --# start server default +box.error.last() +--- +- null +... box.error({code = 123, reason = 'test'}) --- - error: test @@ -53,10 +57,26 @@ box.error() --- - error: Illegal parameters, bla bla ... -box.error() +box.error.raise() --- - error: Illegal parameters, bla bla ... +box.error.last() +--- +- type: ClientError + message: Illegal parameters, bla bla + code: 1 +... +box.error.clear() +--- +... +box.error.last() +--- +- null +... +box.error.raise() +--- +... space = box.space.tweedledum --- ... diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua index 9b04e65d972d08e39ee9a99ec30a7335884bfd0b..53d2abee6d5140ab5cc8653418dc239f03da2e12 100644 --- a/test/box/misc.test.lua +++ b/test/box/misc.test.lua @@ -15,10 +15,15 @@ t = nil --# stop server default --# start server default +box.error.last() box.error({code = 123, reason = 'test'}) box.error(box.error.ILLEGAL_PARAMS, "bla bla") box.error() -box.error() +box.error.raise() +box.error.last() +box.error.clear() +box.error.last() +box.error.raise() space = box.space.tweedledum diff --git a/test/unit/fiber.cc b/test/unit/fiber.cc index 451b72bc5f0b98a8a1a06e19e62461934ef50c82..866280a3798482c30fc74867ec5d00a4e22cdbe4 100644 --- a/test/unit/fiber.cc +++ b/test/unit/fiber.cc @@ -89,7 +89,7 @@ fiber_join_test() fiber_yield(); note("by this time the fiber should be dead already"); fiber_cancel(fiber); - Exception::cleanup(&fiber->exception); + Exception::clear(&fiber->exception); fiber_join(fiber); footer();