diff --git a/src/admin.cc b/src/admin.cc index 5cf23311281b0170a6acca7c67e6229f9248203f..3f462e7b5d5ec6337b74afed190028193b434717 100644 --- a/src/admin.cc +++ b/src/admin.cc @@ -58,7 +58,7 @@ admin_dispatch(struct ev_io *coio, struct iobuf *iobuf, lua_State *L) char delim[SESSION_DELIM_SIZE + 1]; /* \n must folow user-specified delimiter */ int delim_len = snprintf(delim, sizeof(delim), "%s\n", - fiber()->session->delim); + session()->delim); char *eol; while (in->pos == NULL || diff --git a/src/box/access.h b/src/box/access.h index a101afaf882092bdded6daf893882ee612459a03..6fb011de4edac1cd5caa7ed4f99ac001033cdce5 100644 --- a/src/box/access.h +++ b/src/box/access.h @@ -139,7 +139,7 @@ user_by_name(const char *name, uint32_t len); */ #define user() \ ({ \ - struct session *s = fiber()->session; \ + struct session *s = session(); \ uint8_t auth_token = s ? s->auth_token : (int) ADMIN; \ struct user *u = &users[auth_token]; \ assert(u->auth_token == auth_token); \ diff --git a/src/box/authentication.cc b/src/box/authentication.cc index 5cedb827b1a7806d5e9c54b3ab894f2d6388d594..89f03d5d47bed7b54133e4d72982d7691ae81702 100644 --- a/src/box/authentication.cc +++ b/src/box/authentication.cc @@ -39,7 +39,7 @@ authenticate(const char *user_name, uint32_t len, snprintf(name, sizeof(name), "%.*s", len, user_name); tnt_raise(ClientError, ER_NO_SUCH_USER, name); } - struct session *session = fiber()->session; + struct session *session = session(); uint32_t part_count = mp_decode_array(&tuple); if (part_count < 2) { /* Expected at least: authentication mechanism and data. */ diff --git a/src/box/txn.h b/src/box/txn.h index 2856abcc471f3bc072a6479f78b422ab1a77e4d3..aebe1259a02ba5b08f55372b81d161b230248b9b 100644 --- a/src/box/txn.h +++ b/src/box/txn.h @@ -30,6 +30,7 @@ */ #include "index.h" #include "trigger.h" +#include "session.h" extern double too_long_threshold; struct tuple; @@ -71,7 +72,7 @@ struct txn { }; /* Pointer to the current transaction (if any) */ -#define in_txn() (fiber()->session->txn) +#define in_txn() (session()->txn) /** * Start a new statement. If no current transaction, diff --git a/src/fiber.cc b/src/fiber.cc index 7afdc17dc25eba3b7a9dacc496ea438356020bbf..ca9d4ffb40a9d60fa3af6f9e79678ac3232a8836 100644 --- a/src/fiber.cc +++ b/src/fiber.cc @@ -42,6 +42,14 @@ static struct cord main_cord; __thread struct cord *cord_ptr = NULL; pthread_t main_thread_id; +/* + * Local storage finalizers + */ +static struct { + fiber_key_gc_cb cb; + void *arg; +} key_gc[FIBER_KEY_MAX]; /* zeroed by linker */ + static void update_last_stack_frame(struct fiber *fiber) { @@ -364,9 +372,11 @@ fiber_zombificate() fiber_wakeup(fiber->waiter); rlist_del(&fiber->state); fiber->waiter = NULL; - fiber->session = NULL; fiber_set_name(fiber, "zombie"); fiber->f = NULL; +#if !defined(NDEBUG) + memset(fiber->fls, '#', sizeof(fiber->fls)); +#endif /* !defined(NDEBUG) */ unregister_fid(fiber); fiber->fid = 0; fiber->flags = 0; @@ -397,6 +407,17 @@ fiber_loop(void *data __attribute__((unused))) fiber_name(fiber())); panic("fiber `%s': exiting", fiber_name(fiber())); } + for (int i = 0; i < FIBER_KEY_MAX; i++) { + enum fiber_key key = (enum fiber_key) i; + if (key_gc[key].cb == NULL) + continue; + try { + key_gc[key].cb(key, key_gc[key].arg); + } catch(Exception *e) { + say_error("exception raised on fiber stop"); + e->log(); + } + } fiber_zombificate(); fiber_yield(); /* give control back to scheduler */ } @@ -415,6 +436,19 @@ fiber_set_name(struct fiber *fiber, const char *name) region_set_name(&fiber->gc, name); } +extern inline void +fiber_set_key(struct fiber *fiber, enum fiber_key key, void *value); + +extern inline void * +fiber_get_key(struct fiber *fiber, enum fiber_key key); + +void +fiber_key_on_gc(enum fiber_key key, fiber_key_gc_cb cb, void *arg) +{ + key_gc[key].cb = cb; + key_gc[key].arg = arg; +} + /** * Create a new fiber. * @@ -455,10 +489,9 @@ fiber_new(const char *name, void (*f) (va_list)) if (++cord->max_fid < 100) cord->max_fid = 100; fiber->fid = cord->max_fid; - fiber->session = NULL; + memset(fiber->fls, 0, sizeof(fiber->fls)); /* clear local storage */ fiber->flags = 0; fiber->waiter = NULL; - fiber->lua_storage = -2; /* LUA_NOREF */; fiber_set_name(fiber, name); register_fid(fiber); diff --git a/src/fiber.h b/src/fiber.h index 6ef4bb1a2e89c6e0e95c095d3636e7e76f0abdab..e0dd4581bb7aae459df899aef3208f353bcd663b 100644 --- a/src/fiber.h +++ b/src/fiber.h @@ -75,6 +75,17 @@ class FiberCancelException: public Exception { }; #endif /* defined(__cplusplus) */ +/** + * \brief Pre-defined key for fiber local storage + */ +enum fiber_key { + /** box.session */ + FIBER_KEY_SESSION = 0, + /** Lua fiber.storage */ + FIBER_KEY_LUA_STORAGE = 1, + FIBER_KEY_MAX = 2 +}; + struct fiber { #ifdef ENABLE_BACKTRACE void *last_stack_frame; @@ -85,16 +96,6 @@ struct fiber { struct region gc; /** Fiber id. */ uint32_t fid; - /** - * The logical user session the fiber is running - * on behalf of. The concept of an associated session - * is similar to the concept of controlling tty - * in a UNIX process. When a fiber is created, - * it has no session. If it's running a request on behalf - * of a user connection, it's session is changed - * to represent this connection. - */ - struct session *session; struct rlist link; struct rlist state; @@ -110,7 +111,7 @@ struct fiber { va_list f_data; uint32_t flags; struct fiber *waiter; - int lua_storage; + void *fls[FIBER_KEY_MAX]; /* fiber local storage */ }; enum { FIBER_CALL_STACK = 16 }; @@ -233,13 +234,47 @@ void fiber_sleep(ev_tstamp s); struct tbuf; void fiber_schedule(ev_watcher *watcher, int event __attribute__((unused))); -/** Set or clear this fiber's session. */ -static inline void -fiber_set_session(struct fiber *f, struct session *session) +/** + * \brief Associate \a value with \a key in fiber local storage + * \param fiber fiber + * \param key pre-defined key + * \param value value to set + */ +inline void +fiber_set_key(struct fiber *fiber, enum fiber_key key, void *value) { - f->session = session; + assert(key < FIBER_KEY_MAX); + fiber->fls[key] = value; } +/** + * \brief Retrieve value by \a key from fiber local storage + * \param fiber fiber + * \param key pre-defined key + * \return value from from fiber local storage + */ +inline void * +fiber_get_key(struct fiber *fiber, enum fiber_key key) +{ + assert(key < FIBER_KEY_MAX); + return fiber->fls[key]; +} + +/** + * Finalizer callback + * \sa fiber_key_on_gc() + */ +typedef void (*fiber_key_gc_cb)(enum fiber_key key, void *arg); + +/** + * \brief Set finalizing callback invoked on destroy local storage value + * \param key key + * \param cb callback + * Finalizers are global (i.e. are not cord/thread-local). + */ +void +fiber_key_on_gc(enum fiber_key key, fiber_key_gc_cb cb, void *arg); + typedef int (*fiber_stat_cb)(struct fiber *f, void *ctx); int fiber_stat(fiber_stat_cb cb, void *cb_ctx); diff --git a/src/lua/fiber.cc b/src/lua/fiber.cc index d5724f2b2473acd96ab749305e8f077f5add501b..5fef49d2537b9e2990ada89bedee7346a93e9b10 100644 --- a/src/lua/fiber.cc +++ b/src/lua/fiber.cc @@ -30,7 +30,6 @@ #include <fiber.h> #include "lua/utils.h" -#include <session.h> #include <scoped_guard.h> extern "C" { @@ -261,12 +260,13 @@ box_lua_fiber_run_detached(va_list ap) { LuarefGuard coro_guard(va_arg(ap, int)); struct lua_State *L = va_arg(ap, struct lua_State *); - SessionGuard session_guard(-1, 0); auto storage_guard = make_scoped_guard([=] { /* Destroy local storage */ - if (fiber()->lua_storage != LUA_NOREF) - lua_unref(L, fiber()->lua_storage); - fiber()->lua_storage = LUA_NOREF; + int storage_ref = (int)(intptr_t) + fiber_get_key(fiber(), FIBER_KEY_LUA_STORAGE); + if (storage_ref > 0) + lua_unref(L, storage_ref); + fiber_set_key(fiber(), FIBER_KEY_LUA_STORAGE, NULL); }); try { @@ -368,11 +368,15 @@ static int lbox_fiber_storage(struct lua_State *L) { struct fiber *f = lbox_checkfiber(L, 1); - if (f->lua_storage == LUA_NOREF) { + int storage_ref = (int)(intptr_t) + fiber_get_key(f, FIBER_KEY_LUA_STORAGE); + if (storage_ref <= 0) { lua_newtable(L); /* create local storage on demand */ - f->lua_storage = luaL_ref(L, LUA_REGISTRYINDEX); + storage_ref = luaL_ref(L, LUA_REGISTRYINDEX); + fiber_set_key(f, FIBER_KEY_LUA_STORAGE, + (void *)(intptr_t) storage_ref); } - lua_rawgeti(L, LUA_REGISTRYINDEX, f->lua_storage); + lua_rawgeti(L, LUA_REGISTRYINDEX, storage_ref); return 1; } diff --git a/src/lua/init.cc b/src/lua/init.cc index 8ebca87f4505a1cc93cd4d6315ee9c18cc2abff3..31e13f3305d0d6026118fe24760eac586ccb80d0 100644 --- a/src/lua/init.cc +++ b/src/lua/init.cc @@ -44,7 +44,6 @@ extern "C" { #include <fiber.h> -#include <session.h> #include <scoped_guard.h> #include "coeio.h" #include "lua/fiber.h" @@ -382,9 +381,6 @@ run_script(va_list ap) */ fiber_sleep(0.0); - /* Create session with ADMIN privileges for interactive mode */ - SessionGuard session_guard(0, 0); - if (access(path, F_OK) == 0) { /* Execute script. */ if (luaL_loadfile(L, path) != 0) diff --git a/src/lua/session.cc b/src/lua/session.cc index 1666f27afb623ff31d64cc7f0fff00659b61165d..d5f2dae2faa8c264022f6340736cf52cab03f988 100644 --- a/src/lua/session.cc +++ b/src/lua/session.cc @@ -54,7 +54,7 @@ static const char *sessionlib_name = "session"; static int lbox_session_id(struct lua_State *L) { - lua_pushnumber(L, fiber()->session ? fiber()->session->id : 0); + lua_pushnumber(L, session()->id); return 1; } @@ -62,8 +62,7 @@ lbox_session_id(struct lua_State *L) static int lbox_session_uid(struct lua_State *L) { - lua_pushnumber(L, fiber()->session ? - fiber()->session->uid : (int) GUEST); + lua_pushnumber(L, session()->uid); return 1; } @@ -71,9 +70,7 @@ lbox_session_uid(struct lua_State *L) static int lbox_session_user(struct lua_State *L) { - struct user *user = NULL; - if (fiber()->session) - user = user_cache_find(fiber()->session->uid); + struct user *user = user_cache_find(session()->uid); if (user) lua_pushstring(L, user->name); else @@ -87,7 +84,7 @@ lbox_session_su(struct lua_State *L) { if (lua_gettop(L) != 1) luaL_error(L, "session.su(): bad arguments"); - struct session *session = fiber()->session; + struct session *session = session(); if (session == NULL) luaL_error(L, "session.su(): session does not exit"); struct user *user; @@ -148,7 +145,7 @@ lbox_session_peer(struct lua_State *L) luaL_error(L, "session.peer(sid): bad arguments"); uint32_t sid = lua_gettop(L) == 1 ? - luaL_checkint(L, -1) : fiber()->session->id; + luaL_checkint(L, -1) : session()->id; int fd = session_fd(sid); struct sockaddr_storage addr; @@ -162,12 +159,12 @@ lbox_session_peer(struct lua_State *L) static int lbox_session_delimiter(struct lua_State *L) { - if (fiber()->session == NULL) + if (session() == NULL) luaL_error(L, "session.delimiter(): session does not exit"); if (lua_gettop(L) < 1) { /* Get delimiter */ - lua_pushstring(L, fiber()->session->delim); + lua_pushstring(L, session()->delim); return 1; } @@ -175,7 +172,7 @@ lbox_session_delimiter(struct lua_State *L) if (lua_type(L, 1) != LUA_TSTRING) luaL_error(L, "session.delimiter(string): expected a string"); - snprintf(fiber()->session->delim, SESSION_DELIM_SIZE, "%s", + snprintf(session()->delim, SESSION_DELIM_SIZE, "%s", lua_tostring(L, 1)); return 0; } diff --git a/src/session.cc b/src/session.cc index 788933811d17ba6e33b223763672f60f2b2157f4..e7c0941b2efcc917b402f74042a9e070ed9cab55 100644 --- a/src/session.cc +++ b/src/session.cc @@ -126,12 +126,23 @@ session_fd(uint32_t sid) return session->fd; } +static void +fiber_key_session_gc(enum fiber_key key, void *arg) +{ + (void) arg; + struct session *session = (struct session *) fiber_get_key(fiber(), key); + if (session == NULL) + return; + session_destroy(session); +} + void session_init() { session_registry = mh_i32ptr_new(); if (session_registry == NULL) panic("out of memory"); + fiber_key_on_gc(FIBER_KEY_SESSION, fiber_key_session_gc, NULL); mempool_create(&session_pool, &cord()->slabc, sizeof(struct session)); } @@ -150,8 +161,10 @@ SessionGuard::SessionGuard(int fd, uint64_t cookie) SessionGuard::~SessionGuard() { - assert(session == fiber()->session); + assert(session == (struct session *) fiber_get_key(fiber(), + FIBER_KEY_SESSION)); session_destroy(session); + fiber_set_session(fiber(), NULL); } SessionGuardWithTriggers::SessionGuardWithTriggers(int fd, uint64_t cookie) diff --git a/src/session.h b/src/session.h index 8595c505bb5914180af23bb8f49c89e5e9135480..105801611762d1e7330ceb4893e31454ae1accb5 100644 --- a/src/session.h +++ b/src/session.h @@ -31,6 +31,7 @@ #include <inttypes.h> #include <stdbool.h> #include "trigger.h" +#include "fiber.h" enum { SESSION_SEED_SIZE = 32, SESSION_DELIM_SIZE = 16 }; /** Predefined user ids. */ @@ -138,6 +139,22 @@ session_free(); void session_storage_cleanup(int sid); +static inline void +fiber_set_session(struct fiber *fiber, struct session *session) +{ + fiber_set_key(fiber, FIBER_KEY_SESSION, session); +} + +#define session() ({\ + struct session *s = (struct session *) fiber_get_key(fiber(), \ + FIBER_KEY_SESSION); \ + /* Create session on demand */ \ + if (s == NULL) { \ + s = session_create(-1, 0); \ + fiber_set_session(fiber(), s); \ + } \ + s; }) + /** A helper class to create and set session in single-session fibers. */ struct SessionGuard {