diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml index cd1ece8e8bab0ce37e5f3f91b85b3bf3c7cd5cd1..7ec2ed0fefdf3ad833bf17a2b3da95c87e832821 100644 --- a/doc/user/stored-procedures.xml +++ b/doc/user/stored-procedures.xml @@ -919,22 +919,10 @@ tarantool> box.space.tester:insert{20,msgpack.NULL,20} called the <emphasis>fiber function</emphasis>. A fiber has three possible states: running, suspended or dead. - When a fiber is created with <code>fiber.create()</code>, it is suspended. - When a fiber is started with <code>fiber.resume()</code>, it is running. - When a fiber yields control with <code>fiber.yield()</code>, it is suspended. + When a fiber is created with <code>fiber.create()</code>, it is running. + When a fiber yields control with <code>fiber.sleep()</code>, it is suspended. When a fiber ends (because the fiber function ends), it is dead. </para> - <para> - A fiber can also be attached or detached. - An attached fiber is a child of the creator, - and is running only if the creator has called - <code>fiber.resume()</code>. A detached fiber is a child of - the Tarantool internal <quote>sched</quote> fiber, and gets - scheduled only if there is a libev event associated - with it. - To detach, a running fiber should invoke <code>fiber.wrap()</code>. - A detached fiber loses connection with its parent forever. - </para> <para> All fibers are part of the fiber registry. This registry can be searched (<code>fiber.find()</code>) @@ -959,7 +947,7 @@ procedures. --> </para> <para> - The other potential problem comes from detached + The other potential problem comes from fibers which never get scheduled, because they are not subscribed to any events, or because no relevant events occur. Such morphing fibers can be killed with <code>fiber.cancel()</code> at any time, @@ -992,7 +980,7 @@ procedures. </term> <listitem> <para> - Returns: (type = userdata) <code>require('fiber')</code> + Returns: (type = userdata) <code>fiber</code> object for the currently scheduled fiber. </para> </listitem> @@ -1007,104 +995,52 @@ procedures. Locate a fiber userdata object by id. </para> <para> - Returns: (type = userdata) require('fiber') object for the specified fiber. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term> - <emphasis role="lua" xml:id="fiber.create">fiber.create(<replaceable>function-name</replaceable>) </emphasis> - </term> - <listitem> - <para> - Create a fiber. - </para> - <para> - Parameters: <code>function-name</code> = the function that the fiber is associated with. - </para> - <para> - Returns: (type = userdata) the require('fiber') object of the new fiber. - </para> - <para> - Possible errors: the function does not exist or if a recursion limit is hit. + Returns: (type = userdata) fiber object for the specified fiber. </para> </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="fiber.resume">fiber.resume(<replaceable>fiber, ...</replaceable>) </emphasis> + <emphasis role="lua" xml:id="fiber.create">fiber.create(<replaceable>function, function-arguments</replaceable>)</emphasis> </term> <listitem> <para> - Resume a created - or suspended fiber. + Create and start a fiber. The fiber is + created and resumed immediately. </para> - </listitem> - </varlistentry> - - <varlistentry> - <term> - <emphasis role="lua" xml:id="fiber.yield" xreflabel="fiber.yield">fiber.yield(<replaceable>yield-arguments</replaceable>) </emphasis> - </term> - <listitem> <para> - If the fiber is attached, yield control to the calling fiber if the fiber - is attached; otherwise, yield to sched. - </para> - <para> - Parameters: <code>yield-arguments</code>: - If the fiber is attached, arguments passed - to fiber.yield are passed on to the calling fiber. - If the fiber is detached, <code>fiber.yield()</code> - arguments passed to fiber.yield are returned after temporarily - yielding control back to the scheduler. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term> - <emphasis role="lua" xml:id="fiber.detach">fiber.detach()</emphasis> - </term> - <listitem> - <para> - Detach the current fiber. This is a cancellation point. This is a yield point. - It is usually more convenient to use <code>fiber.wrap()</code> to create - a fiber which is already detached when it is created. + Parameters: <code>function</code> = the function to be associated with the fiber, + <code>function-arguments</code> = what will be passed to the function. </para> </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="fiber.wrap">fiber.wrap(<replaceable>function, function-arguments</replaceable>)</emphasis> + <emphasis role="lua" xml:id="fiber.sleep">fiber.sleep(<replaceable>time</replaceable>)</emphasis> </term> <listitem> <para> - This is a quick way to create and start a detached - fiber. The fiber is - created, detached, and resumed immediately. + Yield to the sched fiber and sleep for the specified number of seconds. + Only the current fiber can be made to sleep. </para> <para> - Parameters: <code>function</code> = the function to be associated with the fiber, - <code>function-arguments</code> = what will be passed to the function. + Parameters: <code>time</code> = number of seconds to sleep. </para> </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="fiber.sleep">fiber.sleep(<replaceable>time</replaceable>)</emphasis> + <emphasis role="lua" xml:id="fiber.yield" xreflabel="fiber.yield">fiber.yield() </emphasis> </term> <listitem> <para> - Yield to the sched fiber and sleep for the specified number of seconds. - Only the current fiber can be made to sleep. + Yield control to the scheduler. Equivalent to <code>fiber.sleep(0)</code>. </para> <para> - Parameters: <code>time</code> = number of seconds to sleep. + Parameters: <code>none</code>. </para> </listitem> </varlistentry> @@ -1232,12 +1168,12 @@ Make a fiber, associate function_x with the fiber, and start function_x. It will immediately "detach" so it will be running independently of the caller. <programlisting> -<prompt>tarantool></prompt><userinput> fiber_of_x = fiber.wrap(function_x)</userinput> +<prompt>tarantool></prompt><userinput> fiber_of_x = fiber.create(function_x)</userinput> --- ...</programlisting> Get the id of the fiber (fid), to be used in later displays.<programlisting> -<prompt>tarantool></prompt><userinput> fid = fiber.wrap(function_x)</userinput> +<prompt>tarantool></prompt><userinput> fid = fiber.create(function_x)</userinput> --- ... </programlisting> @@ -1522,8 +1458,7 @@ end <para> Returns: (type = number) the unique identifier (ID) for the current session. The result can be 0 meaning - there is no session (for example because a function is - running in a detached fiber). + there is no session. </para> </listitem> </varlistentry> @@ -1993,8 +1928,7 @@ with an Artistic license. The expirationd.lua program is lengthy (about 500 lines), so here we will only highlight the matters that will be enhanced by studying the full source later. <programlisting> - task.worker_fiber = fiber.create(worker_loop) - task.worker_fiber:resume(task) + task.worker_fiber = fiber.create(worker_loop, task) log.info("expiration: task %q restarted", task.name) ... fiber.sleep(expirationd.constants.check_interval) diff --git a/src/box/box.cc b/src/box/box.cc index 3146eef088ccddca6cdb7906ddf7993afdd6f525..ee934b615ead588150315b63d3550bb1341489b1 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -81,7 +81,7 @@ process_rw(struct port *port, struct request *request) request->execute(request, port); port_eof(port); } catch (Exception *e) { - txn_rollback(); + txn_rollback_stmt(); throw; } } @@ -233,8 +233,8 @@ box_leave_local_standby_mode(void *data __attribute__((unused))) if (recovery_has_remote(recovery_state)) recovery_follow_remote(recovery_state); - title("primary", NULL); - say_info("I am primary"); + title("running", NULL); + say_info("ready to accept requests"); } /** diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index f7c10d78e9daf0ccb1f0f578444007d092195830..cebfe88ad7f03b2daa3a7ff472dbb2b31545fb0f 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -45,6 +45,14 @@ ffi.cdef[[ const char *key, const char *key_end); void password_prepare(const char *password, int len, char *out, int out_len); + int + boxffi_txn_begin(); + + int + boxffi_txn_commit(); + + void + boxffi_txn_rollback(); ]] local function user_resolve(user) @@ -61,6 +69,10 @@ local function user_resolve(user) return tuple[1] end +box.begin = function() if ffi.C.boxffi_txn_begin() == -1 then box.raise() end end +box.commit = function() if ffi.C.boxffi_txn_commit() == -1 then box.raise() end end +box.rollback = ffi.C.boxffi_txn_rollback; + box.schema.space = {} box.schema.space.create = function(name, options) local _space = box.space[box.schema.SPACE_ID] diff --git a/src/box/replica.cc b/src/box/replica.cc index b9c6a7be293d7e5053451a34d135a9f30da979f1..bb9a9975876aa862eaa310bf33807b0363297407 100644 --- a/src/box/replica.cc +++ b/src/box/replica.cc @@ -43,6 +43,8 @@ #include "session.h" #include "box/cluster.h" +static const int RECONNECT_DELAY = 1.0; + static void remote_read_row(struct ev_io *coio, struct iobuf *iobuf, struct iproto_header *row) @@ -128,7 +130,26 @@ replica_bootstrap(struct recovery_state *r) evio_close(loop(), &coio); }); - remote_connect(r, &coio, iobuf); + for (;;) { + try { + remote_connect(r, &coio, iobuf); + r->remote.warning_said = false; + break; + } catch (FiberCancelException *e) { + throw; + } catch (Exception *e) { + if (! r->remote.warning_said) { + say_error("can't connect to master"); + e->log(); + say_info("will retry every %i second", + RECONNECT_DELAY); + r->remote.warning_said = true; + } + iobuf_reset(iobuf); + evio_close(loop(), &coio); + } + fiber_sleep(RECONNECT_DELAY); + } /* Send JOIN request */ struct iproto_header row; @@ -168,7 +189,9 @@ replica_bootstrap(struct recovery_state *r) static void remote_set_status(struct remote *remote, const char *status) { - title("replica", "%s/%s", uri_to_string(&remote->uri), status); + (void) remote; + (void) status; + /* title("replica", "%s/%s", uri_to_string(&remote->uri), status); */ } static void @@ -177,8 +200,6 @@ pull_from_remote(va_list ap) struct recovery_state *r = va_arg(ap, struct recovery_state *); struct ev_io coio; struct iobuf *iobuf = NULL; - bool warning_said = false; - const int reconnect_delay = 1; ev_loop *loop = loop(); /** This fiber executes transactions. */ SessionGuard session_guard(-1, 0); @@ -201,7 +222,7 @@ pull_from_remote(va_list ap) iproto_encode_subscribe(&row, &cluster_id, &r->server_uuid, &r->vclock); remote_write_row(&coio, &row); - warning_said = false; + r->remote.warning_said = false; remote_set_status(&r->remote, "connected"); } err = "can't read row"; @@ -226,12 +247,13 @@ pull_from_remote(va_list ap) throw; } catch (Exception *e) { remote_set_status(&r->remote, "failed"); - e->log(); - if (! warning_said) { + if (! r->remote.warning_said) { if (err != NULL) say_info("%s", err); - say_info("will retry every %i second", reconnect_delay); - warning_said = true; + e->log(); + say_info("will retry every %i second", + RECONNECT_DELAY); + r->remote.warning_said = true; } evio_close(loop, &coio); } @@ -250,7 +272,7 @@ pull_from_remote(va_list ap) * See: https://github.com/tarantool/tarantool/issues/136 */ if (! evio_is_active(&coio)) - fiber_sleep(reconnect_delay); + fiber_sleep(RECONNECT_DELAY); } } diff --git a/src/box/replica.h b/src/box/replica.h index bf6dbeb0117a1754d285eeca074b1b8ff3552e84..0f79c512ec4720afef23d8f095bf3c56c24d8c80 100644 --- a/src/box/replica.h +++ b/src/box/replica.h @@ -39,6 +39,7 @@ struct remote { struct uri uri; struct fiber *reader; ev_tstamp recovery_lag, recovery_last_update_tstamp; + bool warning_said; char source[REMOTE_SOURCE_MAXLEN]; }; diff --git a/src/box/space.cc b/src/box/space.cc index ebfc0681cec0734606dba2be327f52d0a7b3551c..1247052a845a6438f8f9ebeb1c276e90499a5a29 100644 --- a/src/box/space.cc +++ b/src/box/space.cc @@ -119,13 +119,7 @@ space_delete(struct space *space) if (space->engine) delete space->engine; - struct trigger *trigger, *tmp; - rlist_foreach_entry_safe(trigger, &space->on_replace, link, tmp) { - trigger_clear(trigger); - if (trigger->destroy) - trigger->destroy(trigger); - - } + trigger_destroy(&space->on_replace); free(space); } diff --git a/src/box/txn.cc b/src/box/txn.cc index 4713ea4cdfe766eddb01b96ed7408462974a24b7..f4167ee0fc422e8afdc1fcadbd0842e3c25eafb7 100644 --- a/src/box/txn.cc +++ b/src/box/txn.cc @@ -174,6 +174,32 @@ txn_commit(struct txn *txn) in_txn() = NULL; } +/** + * Void all effects of the statement, but + * keep it in the list - to maintain + * limit on the number of statements in a + * trasnaction. + */ +void +txn_rollback_stmt() +{ + struct txn *txn = in_txn(); + if (txn == NULL) + return; + if (txn->autocommit) + return txn_rollback(); + struct txn_stmt *stmt = txn_stmt(txn); + if (stmt->old_tuple || stmt->new_tuple) { + space_replace(stmt->space, stmt->new_tuple, + stmt->old_tuple, DUP_INSERT); + if (stmt->new_tuple) + tuple_ref(stmt->new_tuple, -1); + } + stmt->old_tuple = stmt->new_tuple = NULL; + stmt->space = NULL; + stmt->row = NULL; +} + void txn_rollback() { @@ -206,3 +232,40 @@ txn_check_autocommit(struct txn *txn, const char *where) where, "multi-statement transactions"); } } + +extern "C" { + +int +boxffi_txn_begin() +{ + try { + if (in_txn()) + tnt_raise(ClientError, ER_ACTIVE_TRANSACTION); + (void) txn_begin(false); + } catch (Exception *e) { + return -1; /* pass exception through FFI */ + } + return 0; +} + +int +boxffi_txn_commit() +{ + try { + struct txn *txn = in_txn(); + if (txn == NULL) + tnt_raise(ClientError, ER_NO_ACTIVE_TRANSACTION); + txn_commit(txn); + } catch (Exception *e) { + return -1; /* pass exception through FFI */ + } + return 0; +} + +void +boxffi_txn_rollback() +{ + txn_rollback(); /* doesn't throw */ +} + +} /* extern "C" */ diff --git a/src/box/txn.h b/src/box/txn.h index 06cd9c20ba9a631cfee9375817764771b09bdbe3..3f9870b5fbb1b60e5a9450b8fb73bf780daad037 100644 --- a/src/box/txn.h +++ b/src/box/txn.h @@ -85,6 +85,13 @@ txn_begin_stmt(struct request *request); void txn_commit_stmt(struct txn *txn, struct port *port); +/** + * Rollback a statement. In autocommit mode, + * rolls back the entire transaction. + */ +void +txn_rollback_stmt(); + /** * Start a transaction explicitly. * @pre no transaction is active @@ -123,4 +130,30 @@ txn_stmt(struct txn *txn) return rlist_last_entry(&txn->stmts, struct txn_stmt, next); } +/** + * FFI bindings: do not throw exceptions, do not accept extra + * arguments + */ +extern "C" { + +/** + * @retval 0 - success + * @retval -1 - failed, perhaps a transaction has already been + * started + */ +int +boxffi_txn_begin(); + +/** + * @retval 0 - success + * @retval -1 - commit failed + */ +int +boxffi_txn_commit(); + +void +boxffi_txn_rollback(); + +} /* extern "C" */ + #endif /* TARANTOOL_BOX_TXN_H_INCLUDED */ diff --git a/src/errcode.h b/src/errcode.h index 06034cd8e9b4df65011b9aaed14af6260a0c506a..9b233d82b95ced8caf0332acf9855c94681fddd9 100644 --- a/src/errcode.h +++ b/src/errcode.h @@ -128,6 +128,8 @@ enum { TNT_ERRMSG_MAX = 512 }; /* 76 */_(ER_INVALID_XLOG_ORDER, 2, "Invalid xlog order: %lld and %lld") \ /* 77 */_(ER_NO_CONNECTION, 2, "Connection is not established") \ /* 78 */_(ER_TIMEOUT, 2, "Timeout exceeded") \ + /* 79 */_(ER_ACTIVE_TRANSACTION, 2, "Operation is not permitted when there is an active transaction ") \ + /* 80 */_(ER_NO_ACTIVE_TRANSACTION, 2, "Operation is not permitted when there is no active transaction ") \ /* * !IMPORTANT! Please follow instructions at start of the file diff --git a/src/fiber.cc b/src/fiber.cc index dd24d949adcb65607aa5b337417f224114648222..dfed60327ac401a22808c2cdf99522c790e30d92 100644 --- a/src/fiber.cc +++ b/src/fiber.cc @@ -36,6 +36,7 @@ #include "stat.h" #include "assoc.h" #include "memory.h" +#include "trigger.h" static struct cord main_cord; __thread struct cord *cord_ptr = NULL; @@ -201,6 +202,10 @@ fiber_yield(void) struct fiber *callee = *(--cord->sp); struct fiber *caller = cord->fiber; + /** By convention, these triggers must not throw. */ + if (! rlist_empty(&caller->on_yield)) + trigger_run(&caller->on_yield, NULL); + cord->fiber = callee; update_last_stack_frame(caller); @@ -441,6 +446,7 @@ fiber_new(const char *name, void (*f) (va_list)) rlist_add_entry(&cord->fibers, fiber, link); rlist_create(&fiber->state); + rlist_create(&fiber->on_yield); } fiber->f = f; @@ -472,6 +478,7 @@ fiber_destroy(struct fiber *f) if (strcmp(fiber_name(f), "sched") == 0) return; + trigger_destroy(&f->on_yield); rlist_del(&f->state); region_destroy(&f->gc); tarantool_coro_destroy(&f->coro); diff --git a/src/fiber.h b/src/fiber.h index 9797f859d5b13d7cc4f828053d779d2352ef7cd6..0dd014c8d76bccb107321ba233e27ae4ea2ab04f 100644 --- a/src/fiber.h +++ b/src/fiber.h @@ -99,6 +99,9 @@ struct fiber { struct rlist link; struct rlist state; + /** Triggers invoked before this fiber yields. Must not throw. */ + struct rlist on_yield; + /* This struct is considered as non-POD when compiling by g++. * You can safetly ignore all offset_of-related warnings. * See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31488 diff --git a/src/lua/box_net_box.lua b/src/lua/box_net_box.lua index ae76dc81bb1d713496fd05b3bdd623d06b85d887..517fe3fb0739c5fe3220c9f466d0719f06dcfec5 100644 --- a/src/lua/box_net_box.lua +++ b/src/lua/box_net_box.lua @@ -353,9 +353,9 @@ local remote_methods = { self.timeouts = {} - fiber.wrap(function() self:_connect_worker() end) - fiber.wrap(function() self:_read_worker() end) - fiber.wrap(function() self:_write_worker() end) + fiber.create(function() self:_connect_worker() end) + fiber.create(function() self:_read_worker() end) + fiber.create(function() self:_write_worker() end) if self.opts.wait_connected == nil or self.opts.wait_connected then self:wait_connected() diff --git a/src/lua/fiber.cc b/src/lua/fiber.cc index 8300a12c2d89b47edba63719dcf4b00a9513812f..72a04d7fd96742a1adaa6b561bf06d49a860b9d9 100644 --- a/src/lua/fiber.cc +++ b/src/lua/fiber.cc @@ -45,23 +45,8 @@ extern "C" { /* {{{ box.fiber Lua library: access to Tarantool fibers * * Each fiber can be running, suspended or dead. - * A fiber is created (fiber.create()) suspended. - * It can be started with fiber.resume(), yield - * the control back with fiber.yield() end - * with return or just by reaching the end of the - * function. - * - * A fiber can also be attached or detached. - * An attached fiber is a child of the creator, - * and is running only if the creator has called - * fiber.resume(). A detached fiber is a child of - * Tarntool/Box internal 'sched' fiber, and gets - * scheduled only if there is a libev event associated - * with it. - * To detach itself, a running fiber must invoke - * fiber.detach(). - * A detached fiber loses connection with its parent - * forever. + * When a fiber is created (fiber.create()) it's + * running. * * All fibers are part of the fiber registry, fiber. * This registry can be searched either by @@ -85,7 +70,7 @@ extern "C" { * if it does a lot of computations and doesn't check * whether it's been cancelled (just don't do that). * - * The other potential problem comes from detached + * The other potential problem comes from * fibers which never get scheduled, because are subscribed * or get no events. Such morphing fibers can be killed * with fiber.cancel(), since fiber.cancel() @@ -94,7 +79,7 @@ extern "C" { static const char *fiberlib_name = "fiber"; -enum fiber_state { DONE, YIELD, DETACH }; +enum fiber_state { DONE, YIELD }; /** * @pre: stack top contains a table @@ -179,7 +164,7 @@ lbox_checkfiber(struct lua_State *L, int index) uint32_t fid = *(uint32_t *) luaL_checkudata(L, index, fiberlib_name); struct fiber *f = fiber_find(fid); if (f == NULL) - luaL_error(L, "fiber.resume(): the fiber is dead"); + luaL_error(L, "the fiber is dead"); return f; } @@ -222,46 +207,6 @@ box_lua_fiber_clear_coro(struct lua_State *L, struct fiber *f) lua_settable(L, LUA_REGISTRYINDEX); } -/** - * To yield control to the calling fiber - * we need to be able to find the caller of an - * attached fiber. Instead of passing the caller - * around on the child fiber stack, we create a - * weak table associated with child fiber - * lua_State, and save the caller in it. - * - * When the child fiber lua thread is garbage collected, - * the table is automatically cleared. - */ -static void -box_lua_fiber_push_caller(struct lua_State *child_L) -{ - luaL_getmetatable(child_L, fiberlib_name); - lua_getfield(child_L, -1, "callers"); - if (lua_isnil(child_L, -1)) { - lua_pop(child_L, 1); - lbox_create_weak_table(child_L, "callers"); - } - lua_pushthread(child_L); - lua_pushinteger(child_L, fiber()->fid); - lua_settable(child_L, -3); - /* Pop the fiberlib metatable and callers table. */ - lua_pop(child_L, 2); -} - -static struct fiber * -box_lua_fiber_get_caller(struct lua_State *L) -{ - luaL_getmetatable(L, fiberlib_name); - lua_getfield(L, -1, "callers"); - lua_pushthread(L); - lua_gettable(L, -2); - struct fiber *caller = fiber_find(lua_tointeger(L, -1)); - /* Pop the caller, the callers table, the fiberlib metatable. */ - lua_pop(L, 3); - return caller; -} - static int lbox_fiber_gc(struct lua_State *L) { @@ -365,124 +310,6 @@ lbox_fiber_info(struct lua_State *L) return 1; } -/** - * Detach the current fiber. - */ -static int -lbox_fiber_detach(struct lua_State *L) -{ - if (box_lua_fiber_get_coro(L, fiber()) == NULL) - luaL_error(L, "fiber.detach(): not attached"); - struct fiber *caller = box_lua_fiber_get_caller(L); - /* Clear the caller, to avoid a reference leak. */ - /* Request a detach. */ - lua_pushinteger(L, DETACH); - fiber_yield_to(caller); - return 0; -} - -static void -box_lua_fiber_run(va_list ap __attribute__((unused))) -{ - fiber_testcancel(); - fiber_setcancellable(false); - - struct lua_State *L = box_lua_fiber_get_coro(tarantool_L, fiber()); - /* - * Reference the coroutine to make sure it's not garbage - * collected when detached. - */ - lua_pushthread(L); - LuarefGuard coro_guard(L); - SessionGuard session_guard(-1, 0); - /* - * Lua coroutine.resume() returns true/false for - * completion status plus whatever the coroutine main - * function returns. Follow this style here. - */ - try { - lbox_call(L, lua_gettop(L) - 1, LUA_MULTRET); - /* push completion status */ - lua_pushboolean(L, true); - /* move 'true' to stack start */ - lua_insert(L, 1); - } catch (FiberCancelException *e) { - if (box_lua_fiber_get_coro(L, fiber())) { - struct fiber *caller = box_lua_fiber_get_caller(L); - fiber_wakeup(caller); - } - box_lua_fiber_clear_coro(tarantool_L, fiber()); - /* - * Note: FiberCancelException leaves garbage on - * coroutine stack. This is OK since it is only - * possible to cancel a fiber which is not - * scheduled, and cancel() is synchronous. - */ - - throw; - } catch (Exception *e) { - /* pop any possible garbage */ - lua_settop(L, 0); - /* completion status */ - lua_pushboolean(L, false); - /* error message */ - lua_pushstring(L, e->errmsg()); - - /* Always log the error. */ - e->log(); - } - /* - * L stack contains nothing but call results. - * If we're still attached, synchronously pass - * them to the caller, and then terminate. - */ - if (box_lua_fiber_get_coro(L, fiber())) { - struct fiber *caller = box_lua_fiber_get_caller(L); - lua_pushinteger(L, DONE); - fiber_yield_to(caller); - } -} - -/** @retval true if check failed, false otherwise */ -static bool -lbox_fiber_checkstack(struct lua_State *L) -{ - fiber_checkstack(); - struct fiber *f = fiber(); - const int MAX_STACK_DEPTH = 16; - int depth = 1; - while ((L = box_lua_fiber_get_coro(L, f)) != NULL) { - if (depth++ == MAX_STACK_DEPTH) - return true; - f = box_lua_fiber_get_caller(L); - } - return false; -} - - -static int -lbox_fiber_create(struct lua_State *L) -{ - if (lua_gettop(L) != 1 || !lua_isfunction(L, 1)) - luaL_error(L, "fiber.create(function): bad arguments"); - if (lbox_fiber_checkstack(L)) - luaL_error(L, "fiber.create(function): recursion limit" - " reached"); - - struct fiber *f = fiber_new("lua", box_lua_fiber_run); - /* Initially the fiber is cancellable */ - f->flags |= FIBER_USER_MODE | FIBER_CANCELLABLE; - - /* associate coro with fiber */ - lua_pushlightuserdata(L, f); - struct lua_State *child_L = lua_newthread(L); - lua_settable(L, LUA_REGISTRYINDEX); - /* Move the argument (function of the coro) to the new coro */ - lua_xmove(L, child_L, 1); - lbox_pushfiber(L, f); - return 1; -} - static int lbox_fiber_wakeup(struct lua_State *L) { @@ -491,65 +318,6 @@ lbox_fiber_wakeup(struct lua_State *L) return 0; } -static int -lbox_fiber_resume(struct lua_State *L) -{ - struct fiber *f = lbox_checkfiber(L, 1); - struct lua_State *child_L = box_lua_fiber_get_coro(L, f); - if (child_L == NULL) - luaL_error(L, "fiber.resume(): can't resume a " - "detached fiber"); - int nargs = lua_gettop(L) - 1; - if (nargs > 0) - lua_xmove(L, child_L, nargs); - /* dup 'out' for admin fibers */ - int fid = f->fid; - /* Silent compiler warnings in a release build. */ - (void) fid; - box_lua_fiber_push_caller(child_L); - /* - * We don't use fiber_call() since this breaks any sort - * of yield in the called fiber: for a yield to work, - * the callee got to be scheduled by 'sched'. - */ - fiber_yield_to(f); - /* - * The called fiber could have done 4 things: - * - yielded to us (then we should grab its return) - * - completed (grab return values, wake up the fiber, - * so that it can die) - * - detached (grab return values, wakeup the fiber so it - * can continue). - * - got cancelled (return) - */ - if (f->fid != fid) - luaL_error(L, "fiber.resume(): the child fiber got cancelled"); - /* Find out the state of the child fiber. */ - enum fiber_state child_state = (enum fiber_state) lua_tointeger(child_L, -1); - lua_pop(child_L, 1); - /* Get the results */ - nargs = lua_gettop(child_L); - lua_xmove(child_L, L, nargs); - if (child_state != YIELD) { - /* - * The fiber is dead or requested a detach. - * Garbage collect the associated coro. - */ - box_lua_fiber_clear_coro(L, f); - if (child_state == DETACH) { - /* - * Schedule the runaway child at least - * once. - */ - fiber_wakeup(f); - } else { - /* Synchronously reap a dead child. */ - fiber_call(f); - } - } - return nargs; -} - static void box_lua_fiber_run_detached(va_list ap) { @@ -571,10 +339,10 @@ box_lua_fiber_run_detached(va_list ap) * given the function and its arguments. */ static int -lbox_fiber_wrap(struct lua_State *L) +lbox_fiber_create(struct lua_State *L) { if (lua_gettop(L) < 1 || !lua_isfunction(L, 1)) - luaL_error(L, "fiber.wrap(function, ...): bad arguments"); + luaL_error(L, "fiber.create(function, ...): bad arguments"); fiber_checkstack(); struct fiber *f = fiber_new("lua", box_lua_fiber_run_detached); @@ -584,65 +352,11 @@ lbox_fiber_wrap(struct lua_State *L) int coro_ref = luaL_ref(L, LUA_REGISTRYINDEX); /* Move the arguments to the new coro */ lua_xmove(L, child_L, lua_gettop(L)); + lbox_pushfiber(L, f); fiber_call(f, coro_ref, child_L); - if (f->fid) - lbox_pushfiber(L, f); - else - lua_pushnil(L); return 1; } -/** - * Yield the current fiber. - * - * Yield control to the calling fiber -- if the fiber - * is attached, or to sched otherwise. - * If the fiber is attached, whatever arguments are passed - * to this call, are passed on to the calling fiber. - * If the fiber is detached, simply returns everything back. - */ -static int -lbox_fiber_yield(struct lua_State *L) -{ - /* - * Yield to the caller. The caller will take care of - * whatever arguments are taken. - */ - fiber_setcancellable(true); - struct lua_State *coro_L; - struct fiber *caller; - if ((coro_L = box_lua_fiber_get_coro(L, fiber())) && - (caller = box_lua_fiber_get_caller(coro_L))) { - lua_pushinteger(L, YIELD); - fiber_yield_to(caller); - } else { - fiber_wakeup(fiber()); - fiber_yield(); - fiber_testcancel(); - } - fiber_setcancellable(false); - /* - * Got resumed. Return whatever the caller has passed - * to us with fiber.resume(). - * As a side effect, the detached fiber which yields - * to sched always gets back whatever it yields. - */ - return lua_gettop(L); -} - -static bool -fiber_is_caller(struct lua_State *L, struct fiber *f) -{ - struct fiber *child = fiber(); - while ((L = box_lua_fiber_get_coro(L, child)) != NULL) { - struct fiber *caller = box_lua_fiber_get_caller(L); - if (caller == f) - return true; - child = caller; - } - return false; -} - /** * Get fiber status. * This follows the rules of Lua coroutine.status() function: @@ -673,9 +387,6 @@ lbox_fiber_status(struct lua_State *L) } else if (f == fiber()) { /* The fiber is the current running fiber. */ status = "running"; - } else if (fiber_is_caller(L, f)) { - /* The fiber is current fiber's caller. */ - status = "normal"; } else { /* None of the above: must be suspended. */ status = "suspended"; @@ -727,6 +438,15 @@ lbox_fiber_sleep(struct lua_State *L) return 0; } +static int +lbox_fiber_yield(struct lua_State * /* L */) +{ + fiber_setcancellable(true); + fiber_sleep(0); + fiber_setcancellable(false); + return 0; +} + static int lbox_fiber(struct lua_State *L) { @@ -798,7 +518,8 @@ static const struct luaL_reg lbox_fiber_meta [] = { {"name", lbox_fiber_name}, {"wakeup", lbox_fiber_wakeup}, {"cancel", lbox_fiber_cancel}, - {"resume", lbox_fiber_resume}, + {"status", lbox_fiber_status}, + {"testcancel", lbox_fiber_testcancel}, {"__gc", lbox_fiber_gc}, {NULL, NULL} }; @@ -806,18 +527,15 @@ static const struct luaL_reg lbox_fiber_meta [] = { static const struct luaL_reg fiberlib[] = { {"info", lbox_fiber_info}, {"sleep", lbox_fiber_sleep}, + {"yield", lbox_fiber_yield}, {"self", lbox_fiber}, {"id", lbox_fiber_id}, {"find", lbox_fiber_find}, {"cancel", lbox_fiber_cancel}, {"testcancel", lbox_fiber_testcancel}, {"create", lbox_fiber_create}, - {"resume", lbox_fiber_resume}, - {"wrap", lbox_fiber_wrap}, - {"yield", lbox_fiber_yield}, {"status", lbox_fiber_status}, {"name", lbox_fiber_name}, - {"detach", lbox_fiber_detach}, {"time", lbox_fiber_time}, {"time64", lbox_fiber_time64}, {NULL, NULL} diff --git a/src/session.cc b/src/session.cc index 7a089fc6626f7a5e9cfbccea7fbc406288f16d70..788933811d17ba6e33b223763672f60f2b2157f4 100644 --- a/src/session.cc +++ b/src/session.cc @@ -35,6 +35,7 @@ #include "exception.h" #include "random.h" #include <sys/socket.h> +#include "box/txn.h" static struct mh_i32ptr_t *session_registry; @@ -104,6 +105,10 @@ session_run_on_connect_triggers(struct session *session) void session_destroy(struct session *session) { + if (session->txn) { + assert(session->txn == in_txn()); + txn_rollback(); + } assert(session->txn == NULL); struct mh_i32ptr_node_t node = { session->id, NULL }; mh_i32ptr_remove(session_registry, &node, NULL); diff --git a/src/trigger.h b/src/trigger.h index d230c3d88a1694407a007039c1cd3f5ea65f7580..c64f05942e9b9e3b1d99b4d0973a8ceb682c745d 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -92,4 +92,16 @@ trigger_clear(struct trigger *trigger) rlist_del_entry(trigger, link); } + +static inline void +trigger_destroy(struct rlist *list) +{ + struct trigger *trigger, *tmp; + rlist_foreach_entry_safe(trigger, list, link, tmp) { + trigger_clear(trigger); + if (trigger->destroy) + trigger->destroy(trigger); + } +} + #endif /* INCLUDES_TARANTOOL_TRIGGER_H */ diff --git a/test/app/init_script.result b/test/app/init_script.result index beb33411320862e570ea8084e9673d68885817d2..ce6ab4aa1bf947f847f16d260135e17b143d1a27 100644 --- a/test/app/init_script.result +++ b/test/app/init_script.result @@ -28,7 +28,7 @@ box.cfg -- --- -[] +- [1, 2, 4, 8] ... -- diff --git a/test/app/init_script.test.lua b/test/app/init_script.test.lua index 41b523858abc6b5ef004e55987ad4c19450d03cf..4bc28a7a002db9a84f0b6ff5bc2764cb27b29036 100755 --- a/test/app/init_script.test.lua +++ b/test/app/init_script.test.lua @@ -34,12 +34,10 @@ for k,v in pairs(t) do print(k, v) end -- Insert tests -- local function do_insert() - fiber.detach() space:insert{1, 2, 4, 8} end fiber1 = fiber.create(do_insert) -fiber.resume(fiber1) print[[ -- diff --git a/test/box/box.net.box.result b/test/box/box.net.box.result index d410e5032e755b06a6705e0299ae6f795228a505..907414a7e56febfc3b5d488e1f2a8a7bd77e38e8 100644 --- a/test/box/box.net.box.result +++ b/test/box/box.net.box.result @@ -302,7 +302,7 @@ cn:_select(space.id, 0, {}, { iterator = 'ALL' }) - [354, 1, 2, 4] ... -- -- error while waiting for response -type(fiber.wrap(function() fiber.sleep(.5) cn:_fatal('Test error') end)) +type(fiber.create(function() fiber.sleep(.5) cn:_fatal('Test error') end)) --- - userdata ... diff --git a/test/box/box.net.box.test.lua b/test/box/box.net.box.test.lua index 68e9356a6d511351462d71633864ec8ad3576bd9..4911ce6a9002c41682a08705f0ea53bbdb4967f0 100644 --- a/test/box/box.net.box.test.lua +++ b/test/box/box.net.box.test.lua @@ -109,7 +109,7 @@ cn:_fatal 'Test error' cn:_select(space.id, 0, {}, { iterator = 'ALL' }) -- -- error while waiting for response -type(fiber.wrap(function() fiber.sleep(.5) cn:_fatal('Test error') end)) +type(fiber.create(function() fiber.sleep(.5) cn:_fatal('Test error') end)) function pause() fiber.sleep(10) return true end cn:call('pause') diff --git a/test/box/bsdsocket.result b/test/box/bsdsocket.result index 4c9fb2fdf41e4f2fa5a6ca4aa621c356acdf862f..2beae4da829428713b16718ade2cad772fdea891 100644 --- a/test/box/bsdsocket.result +++ b/test/box/bsdsocket.result @@ -183,7 +183,7 @@ s:listen(128) sevres = {} --- ... -type(require('fiber').wrap(function() s:readable() do local sc = s:accept() table.insert(sevres, sc) sc:syswrite('ok') sc:close() end end)) +type(require('fiber').create(function() s:readable() do local sc = s:accept() table.insert(sevres, sc) sc:syswrite('ok') sc:close() end end)) --- - userdata ... @@ -832,7 +832,7 @@ sa:writable(0) ch = fiber.channel() --- ... -f = fiber.wrap(function() s:read(12) ch:put(true) end) +f = fiber.create(function() s:read(12) ch:put(true) end) --- ... fiber.sleep(.1) diff --git a/test/box/bsdsocket.test.lua b/test/box/bsdsocket.test.lua index 43a87179bbe9e9a82696edb1c82dbc0f84ac47a6..394065bf9a4206aaf73f7516b1a5173c0cf97dd6 100644 --- a/test/box/bsdsocket.test.lua +++ b/test/box/bsdsocket.test.lua @@ -58,7 +58,7 @@ s:bind('127.0.0.1', 3457) s:error() s:listen(128) sevres = {} -type(require('fiber').wrap(function() s:readable() do local sc = s:accept() table.insert(sevres, sc) sc:syswrite('ok') sc:close() end end)) +type(require('fiber').create(function() s:readable() do local sc = s:accept() table.insert(sevres, sc) sc:syswrite('ok') sc:close() end end)) #sevres sc = socket('PF_INET', 'SOCK_STREAM', 'tcp') @@ -266,7 +266,7 @@ sa:readable(0) sa:writable(0) ch = fiber.channel() -f = fiber.wrap(function() s:read(12) ch:put(true) end) +f = fiber.create(function() s:read(12) ch:put(true) end) fiber.sleep(.1) s:close() ch:get(1) diff --git a/test/box/configuration.test.py b/test/box/configuration.test.py deleted file mode 100644 index d311508eb170e41b805fc1d4228516ffd6065367..0000000000000000000000000000000000000000 --- a/test/box/configuration.test.py +++ /dev/null @@ -1,142 +0,0 @@ -import os -import sys -import shutil - -# mask BFD warnings: https://bugs.launchpad.net/tarantool/+bug/1018356 -sys.stdout.push_filter("unable to read unknown load command 0x2\d+", "") - -print """ -# Bug #876541: -# Test floating point values (wal_fsync_delay) with fractional part -# (https://bugs.launchpad.net/bugs/876541) -""" -# stop current server -server.stop() - -old_cfgfile = server.cfgfile_source -server.cfgfile_source = "box/tarantool_bug876541.cfg" -server.deploy() -# check values -admin("box.cfg.wal_fsync_delay") - -server.stop() -server.cfgfile_source = old_cfgfile -old_script = server.script -server.script = "box/lua/test_init.lua" -server.deploy() -sys.stdout.push_filter("admin: .*", "admin: <uri>") -sys.stdout.push_filter("listen: .*", "listen: <uri>") -admin("print_config()") -sys.stdout.pop_filter() -sys.stdout.pop_filter() - -print """ -# Test bug #977898 -""" -# Run a dummy insert to avoid race conditions under valgrind -admin("box.space.tweedledum:insert{4, 8, 16}") - -print """ -# Test insert from init.lua -""" -admin("box.space.tweedledum:get(1)") -admin("box.space.tweedledum:get(2)") -admin("box.space.tweedledum:get(4)") - -print """ -# Test bug #1002272 -""" -admin("floor(0.5)") -admin("floor(0.9)") -admin("floor(1.1)") -server.stop() -server.script = old_script - -# Test script_dir + require -old_script = server.script -server.script = "box/lua/require_init.lua" -server.lua_libs.append("box/lua/require_mod.lua") -server.deploy() -server.lua_libs.pop() -admin("mod.test(10, 15)") -server.stop() -server.script = old_script - - -print """ -# Bug#99 Salloc initialization is not checked on startup -# (https://github.com/tarantool/tarantool/issues/99) -""" -old_cfgfile = server.cfgfile_source -server.cfgfile_source="box/tarantool_bug_gh-99.cfg" -try: - server.deploy() -except OSError as e: - print e - print("ok") -server.stop() -server.cfgfile_source = old_cfgfile - -print """ -# Bug#100 Segmentation fault if rows_per_wal = 0 -# (https://github.com/tarantool/tarantool/issues/100) -""" -old_cfgfile = server.cfgfile_source -server.cfgfile_source = "box/tarantool_bug_gh100.cfg" -try: - server.deploy() -except OSError as e: - print e - print("ok") -server.stop() -server.cfgfile_source = old_cfgfile -print """# -# Check that --background doesn't work if there is no logger -# This is a test case for -# https://bugs.launchpad.net/tarantool/+bug/750658 -# "--background neither closes nor redirects stdin/stdout/stderr" -""" -old_cfgfile = server.cfgfile_source -server.cfgfile_source = "box/tarantool_bug750658.cfg" -try: - server.deploy() -except OSError as e: - print e - print("ok") - -server.stop() -server.cfgfile_source = old_cfgfile -print """ -# A test case for Bug#726778 "Gopt broke wal_dir and snap_dir: they are no -# longer relative to work_dir". -# https://bugs.launchpad.net/tarantool/+bug/726778 -# After addition of gopt(), we started to chdir() to the working -# directory after option parsing. -# Verify that this is not the case, and snap_dir and xlog_dir -# can be relative to work_dir. -""" -import shutil -vardir = server.vardir -shutil.rmtree(os.path.join(vardir, "bug726778"), True) -os.mkdir(os.path.join(vardir, "bug726778")) -os.mkdir(os.path.join(vardir, "bug726778/snapshots")) -os.mkdir(os.path.join(vardir, "bug726778/xlogs")) - -sys.stdout.push_filter("(/\S+)+/tarantool", "tarantool") -sys.stdout.push_filter(".*(P|p)lugin.*", "") -sys.stdout.push_filter(".*shared.*", "") -old_cfgfile = server.cfgfile_source -old_logfile = server.logfile -# make sure deploy() looks for start message in a correct -# place -server.logfile = os.path.join(vardir, "bug726778/tarantool.log") -server.cfgfile_source = "box/bug726778.cfg" -server.deploy() -sys.stdout.clear_all_filters() -server.stop() -shutil.rmtree(os.path.join(vardir, "bug726778")) -server.cfgfile_source = old_cfgfile -server.logfile = old_logfile - -# restore default server -server.deploy() diff --git a/test/box/fiber.result b/test/box/fiber.result index a6f61ceb1fc557816885a27ae5dd08a148906269..7d6971c765fb9718d0b9cdd1eeacaf41163c1e66 100644 --- a/test/box/fiber.result +++ b/test/box/fiber.result @@ -215,7 +215,6 @@ space:truncate() -- test passing arguments in and out created fiber --# setopt delimiter ';' function y() - fiber.detach('started') space = box.space['tweedledum'] while true do space:replace{1953719668, os.time()} @@ -227,24 +226,17 @@ end; f = fiber.create(y); --- ... -fiber.resume(f); ---- -- started -... fiber.sleep(0.002); --- ... fiber.cancel(f); --- ... -fiber.resume(f); ---- -- error: 'fiber.resume(): the fiber is dead' -... +-- fiber garbage collection for k = 1, 1000, 1 do fiber.create( function() - fiber.detach() + fiber.sleep(0) end ) end; @@ -281,44 +273,40 @@ fiber.find('test') f = fiber.create(function() fiber.cancel(fiber.self()) end) --- ... -fiber.resume(f) ---- -- error: 'fiber.resume(): the child fiber got cancelled' -... f = nil --- ... -- https://github.com/tarantool/tarantool/issues/119 -ftest = function() fiber.sleep(0.01 * math.random() ) return true end +ftest = function() fiber.sleep(0.0001 * math.random() ) return true end --- ... --# setopt delimiter ';' +result = 0; +--- +... for i = 1, 10 do - result = {} + local res = {} for j = 1, 300 do - fiber.resume(fiber.create(function() - fiber.detach() - table.insert(result, ftest()) - end)) + fiber.create(function() table.insert(res, ftest()) end) end - while #result < 300 do fiber.sleep(0.01) end + while #res < 300 do fiber.sleep(0) end + result = result + #res end; --- ... --# setopt delimiter '' -#result +result --- -- 300 +- 3000 ... ---# setopt delimiter '' -- -- --- Test fiber.wrap() +-- Test fiber.create() -- -- This should try to infinitely create fibers, -- but hit the fiber stack size limit and fail -- with an error. -f = function() fiber.wrap(f) end +f = function() fiber.create(f) end --- ... f() @@ -327,7 +315,7 @@ f() -- -- Test argument passing -- -f = function(a, b) fiber.wrap(function(arg) result = arg end, a..b) end +f = function(a, b) fiber.create(function(arg) result = arg end, a..b) end --- ... f('hello ', 'world') @@ -347,9 +335,8 @@ result -- -- Test that the created fiber is detached -- -fiber.wrap(function() result = fiber.status() end) +local f = fiber.create(function() result = fiber.status() end) --- -- null ... result --- @@ -358,7 +345,7 @@ result -- A test case for Bug#933487 -- tarantool crashed during shutdown if non running LUA fiber -- was created -f = fiber.create(function () return true end) +f = fiber.create(function () fiber.sleep(1) return true end) --- ... box.snapshot() @@ -373,14 +360,10 @@ box.snapshot() --- - error: can't save snapshot, errno 17 (File exists) ... -fiber.resume(f) ---- -- true -- true -... -f = fiber.create(function () return true end) +f = fiber.create(function () fiber.sleep(1) end) --- ... +-- Test fiber.sleep() fiber.sleep(0) --- ... @@ -398,6 +381,7 @@ fiber.sleep(box, 0.001) --- - error: 'fiber.sleep(delay): bad arguments' ... +-- test fiber.self() fiber.cancel(fiber.self()) --- - error: 'fiber.cancel(): subject fiber does not permit cancel' @@ -431,105 +415,58 @@ f==g --- - true ... -function r() f = fiber.create(r) return (fiber.resume(f)) end ---- -... -r() ---- -- true -... +-- arguments to fiber.create f = fiber.create(print('hello')) --- -- error: '[string "f = fiber.create(print(''hello'')) "]:1: fiber.create(function): - bad arguments' -... -fiber.resume(f) ---- -- error: 'fiber.resume(): the fiber is dead' +- error: '[string "f = fiber.create(print(''hello'')) "]:1: fiber.create(function, + ...): bad arguments' ... -- test passing arguments in and out created fiber -function r(a, b) return a, b end +function r(a, b) res = { a, b } end --- ... f=fiber.create(r) --- ... -fiber.resume(f) ---- -- true -- null -- null -... -f=fiber.create(r) ---- -... -fiber.resume(f, 'hello') ---- -- true -- hello -- null -... -f=fiber.create(r) ---- -... -fiber.resume(f, 'hello', 'world') ---- -- true -- hello -- world -... -f=fiber.create(r) ---- -... -fiber.resume(f, 'hello', 'world', 'wide') ---- -- true -- hello -- world -... -function y(a, b) c=fiber.yield(a) return fiber.yield(b, c) end ---- -... -f=fiber.create(y) +while f:status() == 'running' do fiber.sleep(0) end --- ... -fiber.resume(f, 'hello', 'world') +res --- -- hello +- [] ... -fiber.resume(f, 'wide') +f=fiber.create(r, 'hello') --- -- world -- wide ... -fiber.resume(f) +while f:status() == 'running' do fiber.sleep(0) end --- -- true ... -function y() fiber.detach() while true do box.replace(0, 1953719668, os.time()) fiber.sleep(0.001) end end +res --- +- - hello ... -f = fiber.create(y) +f=fiber.create(r, 'hello, world') --- ... -fiber.resume(f) +while f:status() == 'running' do fiber.sleep(0) end --- ... -fiber.sleep(0.002) +res --- +- - hello, world ... -fiber.cancel(f) +f=fiber.create(r, 'hello', 'world', 'wide') --- -- error: 'fiber.resume(): the fiber is dead' ... -fiber.resume(f) +while f:status() == 'running' do fiber.sleep(0) end --- -- error: 'fiber.resume(): the fiber is dead' ... -f=nil +res --- +- - hello + - world ... -for k=1, 10000, 1 do fiber.create(function() fiber.detach() end) end +for k=1, 10000, 1 do fiber.create(function() fiber.sleep(0) end) end --- ... collectgarbage('collect') @@ -562,196 +499,19 @@ fiber.status(nil) --- - error: 'bad argument #1 to ''?'' (fiber expected, got nil)' ... --- A test case for Bug#911641 fiber.sleep() works incorrectly if --- a fiber is attached. -function r() return fiber.sleep(0.01) end ---- -... -f = fiber.create(r) ---- -... -fiber.resume(f) ---- -- true -... -fiber.resume(f) ---- -- error: 'fiber.resume(): the fiber is dead' -... ---# setopt delimiter ';' -function r() - fiber.yield(box.space.tweedledum:insert{0, 0, 1}) - fiber.yield(box.space.tweedledum:get{0}) - fiber.yield(box.space.tweedledum:truncate()) -end; ---- -... ---# setopt delimiter '' -f = fiber.create(r) ---- -... -fiber.resume(f) ---- -- [0, 0, 1] -... -fiber.resume(f) ---- -- [0, 0, 1] -... -fiber.resume(f) ---- -... -fiber.resume(f) ---- -- true -... -function r() return fiber.yield(fiber.create(r)) end ---- -... -f = r() ---- -... -f1 = fiber.resume(f) ---- -... -f2 = fiber.resume(f1) ---- -... -f3 = fiber.resume(f2) ---- -... -f4 = fiber.resume(f3) ---- -... -f5 = fiber.resume(f4) ---- -... -f6 = fiber.resume(f5) ---- -... -f7 = fiber.resume(f6) ---- -... -f8 = fiber.resume(f7) ---- -... -f9 = fiber.resume(f8) ---- -... -f10 = fiber.resume(f9) ---- -... -f11 = fiber.resume(f10) ---- -... -f12 = fiber.resume(f11) ---- -... -f13 = fiber.resume(f12) ---- -... -f14 = fiber.resume(f13) ---- -... -f15 = fiber.resume(f14) ---- -... -f16 = fiber.resume(f15) ---- -... -f17 = fiber.resume(f16) ---- -... -fiber.resume(f) ---- -- true -... -fiber.resume(f1) ---- -- true -... -fiber.resume(f2) ---- -- true -... -fiber.resume(f3) ---- -- true -... -fiber.resume(f4) ---- -- true -... -fiber.resume(f5) ---- -- true -... -fiber.resume(f6) ---- -- true -... -fiber.resume(f7) ---- -- true -... -fiber.resume(f8) ---- -- true -... -fiber.resume(f9) ---- -- true -... -fiber.resume(f10) ---- -- true -... -fiber.resume(f11) ---- -- true -... -fiber.resume(f12) ---- -- true -... -fiber.resume(f13) ---- -- true -... -fiber.resume(f14) ---- -- true -... -fiber.resume(f15) ---- -- true -... -fiber.resume(f16) ---- -- true -... -f17 = nil ---- -... -function r() fiber.detach() fiber.sleep(1000) end +-- test fiber.cancel +function r() fiber.sleep(1000) end --- ... f = fiber.create(r) --- ... -fiber.resume(f) ---- -... -fiber.resume(f) ---- -- error: 'fiber.resume(): can''t resume a detached fiber' -... fiber.cancel(f) --- ... -fiber.resume(f) +f:status() --- -- error: 'fiber.resume(): the fiber is dead' +- dead ... -- Test fiber.name() old_name = fiber.name() @@ -792,44 +552,36 @@ dofile("fiber.lua") -- print run fiber's test box_fiber_run_test() --- -- - 'tester: status(tester) = running' +- - 'A: odd 1' - 'tester: status(printer) = suspended' - 'count: 1' - - 'printer: tester status = normal' - - 'printer: printer status = running' - - 'A: odd 1' - 'status: suspended' - - 'count: 2' - 'B: odd 1' - 'C: event 2' + - 'count: 2' - 'status: suspended' - - 'count: 3' - 'A: odd 3' + - 'count: 3' - 'status: suspended' - - 'count: 4' - 'B: odd 3' - 'C: event 4' - 'D: event 4' - 'A: odd 5' + - 'count: 4' - 'status: suspended' - - 'count: 5' - 'B: odd 5' - - 'status: dead' ... +-- various... function testfun() while true do fiber.sleep(10) end end --- ... -f = fiber.wrap(testfun) +f = fiber.create(testfun) --- ... f:cancel() --- ... -f:resume() ---- -- error: 'fiber.resume(): the fiber is dead' -... -fib_id = fiber.wrap(testfun):id() +fib_id = fiber.create(testfun):id() --- ... fiber.find(fib_id):cancel() @@ -839,6 +591,6 @@ fiber.find(fib_id) --- - null ... -box.fiber = nil +fiber = nil --- ... diff --git a/test/box/fiber.test.lua b/test/box/fiber.test.lua index 87c8b1a03f84d3eca36ce9f838cb52365285a128..14078a4226c927d307b73c89994cb428487ed938 100644 --- a/test/box/fiber.test.lua +++ b/test/box/fiber.test.lua @@ -67,7 +67,6 @@ space:truncate() --# setopt delimiter ';' function y() - fiber.detach('started') space = box.space['tweedledum'] while true do space:replace{1953719668, os.time()} @@ -75,14 +74,13 @@ function y() end end; f = fiber.create(y); -fiber.resume(f); fiber.sleep(0.002); fiber.cancel(f); -fiber.resume(f); +-- fiber garbage collection for k = 1, 1000, 1 do fiber.create( function() - fiber.detach() + fiber.sleep(0) end ) end; @@ -98,39 +96,35 @@ fiber.find('test') -- https://github.com/tarantool/tarantool/issues/131 -- fiber.resume(fiber.cancel()) -- hang f = fiber.create(function() fiber.cancel(fiber.self()) end) -fiber.resume(f) f = nil -- https://github.com/tarantool/tarantool/issues/119 -ftest = function() fiber.sleep(0.01 * math.random() ) return true end +ftest = function() fiber.sleep(0.0001 * math.random() ) return true end --# setopt delimiter ';' +result = 0; for i = 1, 10 do - result = {} + local res = {} for j = 1, 300 do - fiber.resume(fiber.create(function() - fiber.detach() - table.insert(result, ftest()) - end)) + fiber.create(function() table.insert(res, ftest()) end) end - while #result < 300 do fiber.sleep(0.01) end + while #res < 300 do fiber.sleep(0) end + result = result + #res end; --# setopt delimiter '' -#result - ---# setopt delimiter '' +result -- -- --- Test fiber.wrap() +-- Test fiber.create() -- -- This should try to infinitely create fibers, -- but hit the fiber stack size limit and fail -- with an error. -f = function() fiber.wrap(f) end +f = function() fiber.create(f) end f() -- -- Test argument passing -- -f = function(a, b) fiber.wrap(function(arg) result = arg end, a..b) end +f = function(a, b) fiber.create(function(arg) result = arg end, a..b) end f('hello ', 'world') result f('bye ', 'world') @@ -138,23 +132,23 @@ result -- -- Test that the created fiber is detached -- -fiber.wrap(function() result = fiber.status() end) +local f = fiber.create(function() result = fiber.status() end) result -- A test case for Bug#933487 -- tarantool crashed during shutdown if non running LUA fiber -- was created -f = fiber.create(function () return true end) +f = fiber.create(function () fiber.sleep(1) return true end) box.snapshot() box.snapshot() box.snapshot() -fiber.resume(f) -f = fiber.create(function () return true end) - +f = fiber.create(function () fiber.sleep(1) end) +-- Test fiber.sleep() fiber.sleep(0) fiber.sleep(0.01) fiber.sleep(0.0001) fiber.sleep('hello') fiber.sleep(box, 0.001) +-- test fiber.self() fiber.cancel(fiber.self()) f = fiber.self() old_id = f:id() @@ -164,105 +158,37 @@ fiber.cancel(fiber.self()) fiber.self():id() - old_id < 5 g = fiber.self() f==g -function r() f = fiber.create(r) return (fiber.resume(f)) end -r() +-- arguments to fiber.create f = fiber.create(print('hello')) -fiber.resume(f) -- test passing arguments in and out created fiber -function r(a, b) return a, b end +function r(a, b) res = { a, b } end f=fiber.create(r) -fiber.resume(f) -f=fiber.create(r) -fiber.resume(f, 'hello') -f=fiber.create(r) -fiber.resume(f, 'hello', 'world') -f=fiber.create(r) -fiber.resume(f, 'hello', 'world', 'wide') -function y(a, b) c=fiber.yield(a) return fiber.yield(b, c) end -f=fiber.create(y) -fiber.resume(f, 'hello', 'world') -fiber.resume(f, 'wide') -fiber.resume(f) -function y() fiber.detach() while true do box.replace(0, 1953719668, os.time()) fiber.sleep(0.001) end end -f = fiber.create(y) -fiber.resume(f) -fiber.sleep(0.002) -fiber.cancel(f) -fiber.resume(f) -f=nil -for k=1, 10000, 1 do fiber.create(function() fiber.detach() end) end +while f:status() == 'running' do fiber.sleep(0) end +res +f=fiber.create(r, 'hello') +while f:status() == 'running' do fiber.sleep(0) end +res +f=fiber.create(r, 'hello, world') +while f:status() == 'running' do fiber.sleep(0) end +res +f=fiber.create(r, 'hello', 'world', 'wide') +while f:status() == 'running' do fiber.sleep(0) end +res +for k=1, 10000, 1 do fiber.create(function() fiber.sleep(0) end) end collectgarbage('collect') -- check that these newly created fibers are garbage collected fiber.find(9000) fiber.find(9010) fiber.find(9020) - -- test fiber.status functions: invalid arguments fiber.status(1) fiber.status('fafa-gaga') fiber.status(nil) - --- A test case for Bug#911641 fiber.sleep() works incorrectly if --- a fiber is attached. -function r() return fiber.sleep(0.01) end -f = fiber.create(r) -fiber.resume(f) -fiber.resume(f) ---# setopt delimiter ';' -function r() - fiber.yield(box.space.tweedledum:insert{0, 0, 1}) - fiber.yield(box.space.tweedledum:get{0}) - fiber.yield(box.space.tweedledum:truncate()) -end; ---# setopt delimiter '' +-- test fiber.cancel +function r() fiber.sleep(1000) end f = fiber.create(r) -fiber.resume(f) -fiber.resume(f) -fiber.resume(f) -fiber.resume(f) -function r() return fiber.yield(fiber.create(r)) end -f = r() -f1 = fiber.resume(f) -f2 = fiber.resume(f1) -f3 = fiber.resume(f2) -f4 = fiber.resume(f3) -f5 = fiber.resume(f4) -f6 = fiber.resume(f5) -f7 = fiber.resume(f6) -f8 = fiber.resume(f7) -f9 = fiber.resume(f8) -f10 = fiber.resume(f9) -f11 = fiber.resume(f10) -f12 = fiber.resume(f11) -f13 = fiber.resume(f12) -f14 = fiber.resume(f13) -f15 = fiber.resume(f14) -f16 = fiber.resume(f15) -f17 = fiber.resume(f16) -fiber.resume(f) -fiber.resume(f1) -fiber.resume(f2) -fiber.resume(f3) -fiber.resume(f4) -fiber.resume(f5) -fiber.resume(f6) -fiber.resume(f7) -fiber.resume(f8) -fiber.resume(f9) -fiber.resume(f10) -fiber.resume(f11) -fiber.resume(f12) -fiber.resume(f13) -fiber.resume(f14) -fiber.resume(f15) -fiber.resume(f16) -f17 = nil -function r() fiber.detach() fiber.sleep(1000) end -f = fiber.create(r) -fiber.resume(f) -fiber.resume(f) fiber.cancel(f) -fiber.resume(f) +f:status() -- Test fiber.name() old_name = fiber.name() fiber.name() == old_name @@ -279,12 +205,11 @@ space:drop() dofile("fiber.lua") -- print run fiber's test box_fiber_run_test() - +-- various... function testfun() while true do fiber.sleep(10) end end -f = fiber.wrap(testfun) +f = fiber.create(testfun) f:cancel() -f:resume() -fib_id = fiber.wrap(testfun):id() +fib_id = fiber.create(testfun):id() fiber.find(fib_id):cancel() fiber.find(fib_id) -box.fiber = nil +fiber = nil diff --git a/test/box/info.result b/test/box/info.result index 42cbbf4313b44108afb4d53f24fdfb96bf0bb585..a015c6c0f638c5af5ddbc55d840af997b622b31f 100644 --- a/test/box/info.result +++ b/test/box/info.result @@ -38,7 +38,7 @@ box.info.recovery_last_update ... box.info.status --- -- primary +- running ... string.len(box.info.build.target) > 0 --- diff --git a/test/box/ipc.result b/test/box/ipc.result index ff17f0048e6a83a38fd88583f08191230637b935..9f98fbb22f11ea25129ad85656cdab2583edbb46 100644 --- a/test/box/ipc.result +++ b/test/box/ipc.result @@ -56,7 +56,7 @@ buffer = {} --- ... --# setopt delimiter ';' -tfbr = fiber.wrap( +tfbr = fiber.create( function() while true do table.insert(buffer, ch:get()) @@ -171,7 +171,7 @@ ch:is_empty() - true ... --# setopt delimiter ';' -tfbr = fiber.wrap( +tfbr = fiber.create( function() while true do local v = ch:get() @@ -181,7 +181,7 @@ tfbr = fiber.wrap( ); --- ... -tfbr2 = fiber.wrap( +tfbr2 = fiber.create( function() while true do local v = ch:get() @@ -300,7 +300,7 @@ ch:is_closed() passed = false --- ... -type(fiber.wrap(function() if ch:get() == nil then passed = true end end)) +type(fiber.create(function() if ch:get() == nil then passed = true end end)) --- - userdata ... @@ -341,7 +341,7 @@ ch:is_closed() passed = false --- ... -type(fiber.wrap(function() if ch:put(true) == false then passed = true end end)) +type(fiber.create(function() if ch:put(true) == false then passed = true end end)) --- - userdata ... @@ -383,7 +383,7 @@ for i = 1, 10 do table.insert(chs, fiber.channel()) end; --- ... for i = 1, 10 do - local no = i fiber.wrap( + local no = i fiber.create( function() fiber.self():name('pusher') while true do @@ -396,7 +396,7 @@ end; --- ... for i = 1, 10 do - local no = i fiber.wrap( + local no = i fiber.create( function() fiber.self():name('receiver') while true do diff --git a/test/box/ipc.test.lua b/test/box/ipc.test.lua index 4f53a2e697239937b4fb9467eb99e29a14f148d8..fa2829234ceff1cc2e2ab755b3adb0b7211fd619 100644 --- a/test/box/ipc.test.lua +++ b/test/box/ipc.test.lua @@ -15,7 +15,7 @@ ch:is_full() ch:is_empty() buffer = {} --# setopt delimiter ';' -tfbr = fiber.wrap( +tfbr = fiber.create( function() while true do table.insert(buffer, ch:get()) @@ -48,7 +48,7 @@ ch:get() ch:is_full() ch:is_empty() --# setopt delimiter ';' -tfbr = fiber.wrap( +tfbr = fiber.create( function() while true do local v = ch:get() @@ -56,7 +56,7 @@ tfbr = fiber.wrap( end end ); -tfbr2 = fiber.wrap( +tfbr2 = fiber.create( function() while true do local v = ch:get() @@ -85,7 +85,7 @@ buffer ch = fiber.channel(1) ch:is_closed() passed = false -type(fiber.wrap(function() if ch:get() == nil then passed = true end end)) +type(fiber.create(function() if ch:get() == nil then passed = true end end)) ch:close() passed ch:get() @@ -97,7 +97,7 @@ ch = fiber.channel(1) ch:put(true) ch:is_closed() passed = false -type(fiber.wrap(function() if ch:put(true) == false then passed = true end end)) +type(fiber.create(function() if ch:put(true) == false then passed = true end end)) ch:close() passed ch:get() @@ -116,7 +116,7 @@ for i = 1, 10 do table.insert(chs, fiber.channel()) end; for i = 1, 10 do - local no = i fiber.wrap( + local no = i fiber.create( function() fiber.self():name('pusher') while true do @@ -128,7 +128,7 @@ for i = 1, 10 do end; for i = 1, 10 do - local no = i fiber.wrap( + local no = i fiber.create( function() fiber.self():name('receiver') while true do diff --git a/test/box/lua/fiber.lua b/test/box/lua/fiber.lua index adb53b8ef719ac0c3e4387182de2bfc31147f831..2bc8111f89b0e197042e9686ccffe417043f9527 100644 --- a/test/box/lua/fiber.lua +++ b/test/box/lua/fiber.lua @@ -1,3 +1,4 @@ +local fiber = require('fiber') -- -------------------------------------------------------------------------- -- -- Local functions -- -------------------------------------------------------------------------- -- @@ -18,7 +19,7 @@ local result = {} -- odd printer local function odd(x) table.insert(result,'A: odd '..tostring(x)) - fiber.yield(x) + fiber.sleep(0.0) table.insert(result,'B: odd '..tostring(x)) end @@ -26,18 +27,16 @@ end local function even(x) table.insert(result,'C: event '..tostring(x)) if x == 2 then - return x + return end table.insert(result,'D: event '..tostring(x)) end -- printer task routine main function local function printer_task_routine(x) - table.insert(result, "printer: tester status = " .. fiber.status(tester_task)) - table.insert(result, "printer: printer status = " .. fiber.status(printer_task)) for i = 1, x do if i == 3 then - fiber.yield(-1) + fiber.sleep(0) end if i % 2 == 0 then even(i) @@ -48,21 +47,20 @@ local function printer_task_routine(x) end --- -------------------------------------------------------------------------- -- +------------------------------------------------------------------------ -- tester task routines --- -------------------------------------------------------------------------- -- +------------------------------------------------------------------------ -- tester task routine main function local function tester_task_routine() - printer_task = fiber.create(printer_task_routine) - table.insert(result, "tester: status(tester) = " .. fiber.status(tester_task)) - table.insert(result, "tester: status(printer) = " .. fiber.status(printer_task)) + printer_task = fiber.create(printer_task_routine, 5) + table.insert(result, "tester: status(printer) = " .. printer_task:status()) count = 1 - while fiber.status(printer_task) ~= "dead" do + while printer_task:status() ~= "dead" do table.insert(result, "count: " .. tostring(count)) - fiber.resume(printer_task, 5) - table.insert(result, "status: " .. fiber.status(printer_task)) + table.insert(result, "status: " .. printer_task:status()) count = count + 1 + fiber.sleep(0) end end @@ -75,6 +73,8 @@ end function box_fiber_run_test() -- run tester tester_task = fiber.create(tester_task_routine) - fiber.resume(tester_task) + while tester_task:status() ~= 'dead' do + fiber.sleep(0) + end return result end diff --git a/test/box/misc.result b/test/box/misc.result index c16ca13c18ca4c32d936d5b64b186031ba68d9af..882661f71be4d36e8706ec7ef89758d6696071ed 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -16,11 +16,14 @@ t = {} for n in pairs(box) do table.insert(t, tostring(n)) end table.sort(t) ... t --- -- - cfg +- - begin + - cfg + - commit - error - index - info - raise + - rollback - schema - slab - snapshot @@ -197,12 +200,14 @@ t; - 'box.error.CREATE_USER : 43' - 'box.error.CREATE_SPACE : 9' - 'box.error.UNKNOWN_SCHEMA_OBJECT : 49' + - 'box.error.NO_ACTIVE_TRANSACTION : 80' + - 'box.error.SPLICE : 25' - 'box.error.FIELD_TYPE_MISMATCH : 24' - 'box.error.UNSUPPORTED : 5' - 'box.error.INVALID_MSGPACK : 20' - 'box.error.KEY_PART_COUNT : 31' - 'box.error.ALTER_SPACE : 12' - - 'box.error.SPLICE : 25' + - 'box.error.ACTIVE_TRANSACTION : 79' - 'box.error.NO_CONNECTION : 77' - 'box.error.DROP_SPACE : 11' - 'box.error.INVALID_XLOG_NAME : 75' diff --git a/test/box/print.result b/test/box/print.result index e4cdb1491ad3005a4d3eb49942345c7f0ff09ac2..6ab6a18f26467f9ce1003262806b3e46052cac6a 100644 --- a/test/box/print.result +++ b/test/box/print.result @@ -4,9 +4,8 @@ print("Hello, world") io = require('io') --- ... -require('fiber').wrap(function() print('Ehllo, world') io.flush() end) +local f = require('fiber').create(function() print('Ehllo, world') io.flush() end) --- -- null ... require('fiber').sleep(0.1) --- diff --git a/test/box/print.test.py b/test/box/print.test.py index 9b0f56f0ec8fea0b6a439a1c313a9cbe32df41f2..5dfdd82e5479b16dabcbd42aeef800d2a68b7382 100644 --- a/test/box/print.test.py +++ b/test/box/print.test.py @@ -12,7 +12,7 @@ log = server.logfile f = open(log, "r") f.seek(0, 2) -admin("require('fiber').wrap(function() print('Ehllo, world') io.flush() end)") +admin("local f = require('fiber').create(function() print('Ehllo, world') io.flush() end)") admin("require('fiber').sleep(0.1)") line = f.readline() print("Check log line") diff --git a/test/box/session.result b/test/box/session.result index 841cc443fd9daaf4eaf11c7f50fe876b54681e02..2c657ed58ff6af69c315f69cd7a4dffc139f9507 100644 --- a/test/box/session.result +++ b/test/box/session.result @@ -31,27 +31,16 @@ session.id() > 0 --- - true ... -f = fiber.create(function() fiber.detach() failed = session.id() == 0 end) +f = fiber.create(function() failed = session.id() == 0 end) --- ... -fiber.resume(f) +while f:status() ~= 'dead' do fiber.sleep(0) end --- ... failed --- - false ... -f1 = fiber.create(function() if session.id() == 0 then failed = true end end) ---- -... -fiber.resume(f1) ---- -- true -... -failed ---- -- false -... session.peer() == session.peer(session.id()) --- - true diff --git a/test/box/session.test.lua b/test/box/session.test.lua index 62a0435dc8ff0163ac452d0bd8462d0a3d916c65..bbd09b4e048f569c91c7749ca09039e8245697fb 100644 --- a/test/box/session.test.lua +++ b/test/box/session.test.lua @@ -11,11 +11,8 @@ session.exists(1234567890) -- check session.id() session.id() > 0 -f = fiber.create(function() fiber.detach() failed = session.id() == 0 end) -fiber.resume(f) -failed -f1 = fiber.create(function() if session.id() == 0 then failed = true end end) -fiber.resume(f1) +f = fiber.create(function() failed = session.id() == 0 end) +while f:status() ~= 'dead' do fiber.sleep(0) end failed session.peer() == session.peer(session.id()) diff --git a/test/box/socket.result b/test/box/socket.result index 56b3945ee127fa8e4dfe8d6e67c5ebe30c1889ef..01c87cd3c9f3a0509324ae6b55de053b914701fe 100644 --- a/test/box/socket.result +++ b/test/box/socket.result @@ -902,7 +902,7 @@ ping s:close() --- ... - replies = 0 packet = require('msgpack').encode({[0] = 64, [1] = 0}) packet = require('msgpack').encode(packet:len())..packet function bug1160869() local s = socket.tcp() s:connect('127.0.0.1', string.gsub(box.cfg.listen, '^.*:', '')) s:recv(128) require('fiber').wrap(function() while true do _, status = s:recv(18) if status == "eof" then error("unexpected eof") end replies = replies + 1 end end) return s:send(packet) end + replies = 0 packet = require('msgpack').encode({[0] = 64, [1] = 0}) packet = require('msgpack').encode(packet:len())..packet function bug1160869() local s = socket.tcp() s:connect('127.0.0.1', string.gsub(box.cfg.listen, '^.*:', '')) s:recv(128) require('fiber').create(function() while true do _, status = s:recv(18) if status == "eof" then error("unexpected eof") end replies = replies + 1 end end) return s:send(packet) end --- ... bug1160869() @@ -924,7 +924,7 @@ replies --- - 3 ... - s = nil syncno = 0 reps = 0 packet = require('msgpack').encode({[0] = 64, [1] = 0}) packet = require('msgpack').encode(packet:len())..packet function iostart() if s ~= nil then return end s = socket.tcp() s:connect('127.0.0.1', string.gsub(box.cfg.listen, '^.*:', '')) s:recv(128) require('fiber').wrap(function() while true do s:recv(18) if status == "eof" then error("unexpected eof") end reps = reps + 1 end end) end function iotest() iostart() syncno = syncno + 1 packet = require('msgpack').encode({[0] = 64, [1] = syncno}) packet = require('msgpack').encode(packet:len())..packet return s:send(packet) end + s = nil syncno = 0 reps = 0 packet = require('msgpack').encode({[0] = 64, [1] = 0}) packet = require('msgpack').encode(packet:len())..packet function iostart() if s ~= nil then return end s = socket.tcp() s:connect('127.0.0.1', string.gsub(box.cfg.listen, '^.*:', '')) s:recv(128) require('fiber').create(function() while true do s:recv(18) if status == "eof" then error("unexpected eof") end reps = reps + 1 end end) end function iotest() iostart() syncno = syncno + 1 packet = require('msgpack').encode({[0] = 64, [1] = syncno}) packet = require('msgpack').encode(packet:len())..packet return s:send(packet) end --- ... iotest() @@ -949,7 +949,7 @@ reps test_listen_done = false --- ... - function server() ms = socket.tcp() ms:bind('127.0.0.1', 8181) ms:listen() test_listen_done = true while true do local s = ms:accept( .5 ) if s ~= 'timeout' then print("accepted connection ", s) s:send('Hello world') s:shutdown(socket.SHUT_RDWR) end end end fbr = require('fiber').wrap(server) + function server() ms = socket.tcp() ms:bind('127.0.0.1', 8181) ms:listen() test_listen_done = true while true do local s = ms:accept( .5 ) if s ~= 'timeout' then print("accepted connection ", s) s:send('Hello world') s:shutdown(socket.SHUT_RDWR) end end end fbr = require('fiber').create(server) --- ... wait_cout = 100 while not test_listen_done and wait_cout > 0 do require('fiber').sleep(0.001) wait_cout = wait_cout - 1 end diff --git a/test/box/socket.test.py b/test/box/socket.test.py index f9b2b4b043eb8553633a3ff41594aa05c33bad8b..2085b4b1c44d8e5141094fe3dc4f64adc59b5498 100644 --- a/test/box/socket.test.py +++ b/test/box/socket.test.py @@ -518,7 +518,7 @@ function bug1160869() local s = socket.tcp() s:connect('127.0.0.1', string.gsub(box.cfg.listen, '^.*:', '')) s:recv(128) - require('fiber').wrap(function() + require('fiber').create(function() while true do _, status = s:recv(18) if status == "eof" then @@ -551,7 +551,7 @@ function iostart() s = socket.tcp() s:connect('127.0.0.1', string.gsub(box.cfg.listen, '^.*:', '')) s:recv(128) - require('fiber').wrap(function() + require('fiber').create(function() while true do s:recv(18) if status == "eof" then @@ -598,7 +598,7 @@ function server() end end -fbr = require('fiber').wrap(server) +fbr = require('fiber').create(server) """ admin("test_listen_done = false") admin(test.replace('\n', ' ')) diff --git a/test/box/suite.ini b/test/box/suite.ini index c4ed54055d84710fbd0fd00260c5e072d9c0f187..adc25b973ed65ff82f0e9d8a061d64bc6197240f 100644 --- a/test/box/suite.ini +++ b/test/box/suite.ini @@ -2,7 +2,7 @@ core = tarantool description = tarantool/box, minimal configuration script = box.lua -disabled = configuration.test.py reconfigure.test.py +disabled = valgrind_disabled = admin_coredump.test.lua release_disabled = errinj.test.lua errinj_index.test.lua lua_libs = lua/fiber.lua lua/fifo.lua diff --git a/test/box/transaction.result b/test/box/transaction.result new file mode 100644 index 0000000000000000000000000000000000000000..1cf1fa1d153845be57a6c32a1172c2ba573ba8f6 --- /dev/null +++ b/test/box/transaction.result @@ -0,0 +1,180 @@ +--# setopt delimiter ';' +-- empty transaction - ok +box.begin() box.commit(); +--- +... +-- double begin +box.begin() box.begin(); +--- +- error: 'Operation is not permitted when there is an active transaction ' +... +-- no active transaction since exception rolled it back +box.commit(); +--- +... +-- double commit - error +box.begin() box.commit() box.commit(); +--- +- error: 'Operation is not permitted when there is no active transaction ' +... +-- commit if not started - error +box.commit(); +--- +- error: 'Operation is not permitted when there is no active transaction ' +... +-- rollback if not started - ok +-- double rollback - ok +box.rollback() +box.begin() box.rollback() box.rollback(); +--- +... +-- rollback of an empty trans - ends transaction +box.begin() box.rollback(); +--- +... +-- no current transaction - error +box.commit(); +--- +- error: 'Operation is not permitted when there is no active transaction ' +... +fiber = require('fiber'); +--- +... +function sloppy() + box.begin() +end; +--- +... +f = fiber.create(sloppy); +--- +... +-- when the sloppy fiber ends, its session has an active transction +-- ensure it's rolled back automatically +fiber.sleep(0); +--- +... +fiber.sleep(0); +--- +... +-- transactions and system spaces +box.begin() box.schema.space.create('test'); +--- +- error: Space _space does not support multi-statement transactions +... +box.rollback(); +--- +... +box.begin() box.schema.func.create('test'); +--- +- error: Space _func does not support multi-statement transactions +... +box.rollback(); +--- +... +box.begin() box.schema.user.create('test'); +--- +- error: Space _user does not support multi-statement transactions +... +box.rollback(); +--- +... +box.begin() box.schema.user.grant('guest', 'read', 'universe'); +--- +- error: Space _priv does not support multi-statement transactions +... +box.rollback(); +--- +... +box.begin() box.space._schema:insert{'test'}; +--- +- error: Space _schema does not support multi-statement transactions +... +box.rollback(); +--- +... +box.begin() box.space._cluster:insert{123456789, 'abc'}; +--- +- error: Space _cluster does not support multi-statement transactions +... +box.rollback(); +--- +... +s = box.schema.space.create('test'); +--- +... +box.begin() s:create_index('primary'); +--- +- error: Space _index does not support multi-statement transactions +... +box.rollback(); +--- +... +s:create_index('primary'); +--- +... +function multi() + box.begin() + s:auto_increment{'first row'} + s:auto_increment{'second row'} + t = s:select{} + box.commit() +end; +--- +... +multi(); +--- +... +t; +--- +- - [1, 'first row'] + - [2, 'second row'] +... +s:select{}; +--- +- - [1, 'first row'] + - [2, 'second row'] +... +s:truncate(); +--- +... +function multi() + box.begin() + s:auto_increment{'first row'} + s:auto_increment{'second row'} + t = s:select{} + box.rollback() +end; +--- +... +multi(); +--- +... +t; +--- +- - [1, 'first row'] + - [2, 'second row'] +... +s:select{}; +--- +- [] +... +function multi() + box.begin() + s:insert{1, 'first row'} + pcall(s.insert, s, {1, 'duplicate'}) + t = s:select{} + box.commit() +end; +--- +... +multi(); +--- +... +t; +--- +- - [1, 'first row'] +... +s:select{}; +--- +- - [1, 'first row'] +... diff --git a/test/box/transaction.test.lua b/test/box/transaction.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..07260e2f8c93c0bdababdad96305fe90669d4161 --- /dev/null +++ b/test/box/transaction.test.lua @@ -0,0 +1,76 @@ +--# setopt delimiter ';' +-- empty transaction - ok +box.begin() box.commit(); +-- double begin +box.begin() box.begin(); +-- no active transaction since exception rolled it back +box.commit(); +-- double commit - error +box.begin() box.commit() box.commit(); +-- commit if not started - error +box.commit(); +-- rollback if not started - ok +box.rollback() +-- double rollback - ok +box.begin() box.rollback() box.rollback(); +-- rollback of an empty trans - ends transaction +box.begin() box.rollback(); +-- no current transaction - error +box.commit(); +fiber = require('fiber'); +function sloppy() + box.begin() +end; +f = fiber.create(sloppy); +-- when the sloppy fiber ends, its session has an active transction +-- ensure it's rolled back automatically +fiber.sleep(0); +fiber.sleep(0); +-- transactions and system spaces +box.begin() box.schema.space.create('test'); +box.rollback(); +box.begin() box.schema.func.create('test'); +box.rollback(); +box.begin() box.schema.user.create('test'); +box.rollback(); +box.begin() box.schema.user.grant('guest', 'read', 'universe'); +box.rollback(); +box.begin() box.space._schema:insert{'test'}; +box.rollback(); +box.begin() box.space._cluster:insert{123456789, 'abc'}; +box.rollback(); +s = box.schema.space.create('test'); +box.begin() s:create_index('primary'); +box.rollback(); +s:create_index('primary'); +function multi() + box.begin() + s:auto_increment{'first row'} + s:auto_increment{'second row'} + t = s:select{} + box.commit() +end; +multi(); +t; +s:select{}; +s:truncate(); +function multi() + box.begin() + s:auto_increment{'first row'} + s:auto_increment{'second row'} + t = s:select{} + box.rollback() +end; +multi(); +t; +s:select{}; +function multi() + box.begin() + s:insert{1, 'first row'} + pcall(s.insert, s, {1, 'duplicate'}) + t = s:select{} + box.commit() +end; +multi(); +t; +s:select{}; diff --git a/test/lib/tarantool_server.py b/test/lib/tarantool_server.py index b47d48dffa54ca79757db1a4094b4e97d54d2991..332d37cbfd996000cb9c4c950c361d366d5ffe17 100644 --- a/test/lib/tarantool_server.py +++ b/test/lib/tarantool_server.py @@ -423,10 +423,10 @@ class TarantoolServer(Server): self._admin = find_port(port) self._sql = find_port(port + 1) - def deploy(self, silent=True): + def deploy(self, silent=True, wait = True): self.install(silent) if not self._start_and_exit: - self.start(silent) + self.start(silent=silent, wait=wait) else: self.start_and_exit() @@ -451,7 +451,7 @@ class TarantoolServer(Server): self.start() self.process.wait() - def start(self, silent=True): + def start(self, silent=True, wait = True): if self.status == 'started': if not silent: color_stdout('The server is already started.\n', schema='lerror') @@ -474,7 +474,8 @@ class TarantoolServer(Server): cwd = self.vardir, stdout=self.log_des, stderr=self.log_des) - self.wait_until_started() + if wait: + self.wait_until_started() self.status = 'started' def wait_stop(self): @@ -524,7 +525,7 @@ class TarantoolServer(Server): try: temp = AdminConnection('localhost', self.admin.port) ans = yaml.load(temp.execute('box.info.status'))[0] - if ans in ('primary', 'hot_standby', 'orphan') or ans.startswith('replica'): + if ans in ('running', 'hot_standby', 'orphan'): return True else: raise Exception("Strange output for `box.info.status`: %s" % (ans)) diff --git a/test/replication/init_storage.result b/test/replication/init_storage.result index f2561c71ff9140d1c4bb6a24baa265f53a47f0d1..4b4e01d1b8939e0d54e06c5615da008c6aee96e9 100644 --- a/test/replication/init_storage.result +++ b/test/replication/init_storage.result @@ -106,3 +106,10 @@ space:get{19} --- - [19, 6859] ... +------------------------------------------------------------- +reconnect on JOIN/SUBSCRIBE +------------------------------------------------------------- +waiting reconnect on JOIN... +ok +waiting reconnect on SUBSCRIBE... +ok diff --git a/test/replication/init_storage.test.py b/test/replication/init_storage.test.py index e7446c3a67c9e160dba897bc4c08a63af0111adf..27b8b9699f3dae09ef026be25b3baa64d71043c5 100644 --- a/test/replication/init_storage.test.py +++ b/test/replication/init_storage.test.py @@ -52,6 +52,33 @@ for i in range(1, 20): replica.stop() replica.cleanup(True) +print '-------------------------------------------------------------' +print 'reconnect on JOIN/SUBSCRIBE' +print '-------------------------------------------------------------' + server.stop() -server.deploy() +replica = TarantoolServer(server.ini) +replica.script = 'replication/replica.lua' +replica.vardir = os.path.join(server.vardir, 'replica') +replica.rpl_master = master +replica.deploy(wait=False) + +print 'waiting reconnect on JOIN...' +server.start() +replica.wait_until_started() +print 'ok' +replica.stop() +server.stop() + +print 'waiting reconnect on SUBSCRIBE...' +replica.start(wait=False) +server.start() +replica.wait_until_started() +print 'ok' + +replica.stop() +replica.cleanup(True) + +server.stop() +server.deploy() diff --git a/test/replication/suite.ini b/test/replication/suite.ini index ab9af45495f3cdfe00ddc9f0dd7055b2afa2e9b1..c0a179cce971a84ef394aa6bb06c3a4b3403a547 100644 --- a/test/replication/suite.ini +++ b/test/replication/suite.ini @@ -2,4 +2,4 @@ core = tarantool script = master.lua description = tarantool/box, replication -disabled = consistent.test.lua +disabled = consistent.test.lua status.test.py diff --git a/test/replication/swap.test.py b/test/replication/swap.test.py index 451bf93a2c2cf0ff2b22d5441e5315bb3ab1f65e..9ab52906849697806133fde44f6633a9a9284ccc 100644 --- a/test/replication/swap.test.py +++ b/test/replication/swap.test.py @@ -39,12 +39,12 @@ replica.sql.py_con.authenticate(LOGIN, PASSWORD) master.admin("s = box.schema.create_space('tweedledum', {id = 0})") master.admin("s:create_index('primary', {type = 'hash'})") -## gh-343: replica.cc must not add login and password to proc title -status = replica.get_param("status") -host_port = "%s:%s" % (HOST, master.sql.port) -m = re.search(r'replica/(.*)/.*', status) -if not m or m.group(1) != host_port: - print 'invalid box.info.status', status, 'expected host:port', host_port +### gh-343: replica.cc must not add login and password to proc title +#status = replica.get_param("status") +#host_port = "%s:%s" % (HOST, master.sql.port) +#m = re.search(r'replica/(.*)/.*', status) +#if not m or m.group(1) != host_port: +# print 'invalid box.info.status', status, 'expected host:port', host_port master_id = master.get_param('node')['id'] replica_id = replica.get_param('node')['id']