From 18462a6bdb7edcc44b108c95da2675327a1a348a Mon Sep 17 00:00:00 2001 From: Konstantin Osipov <kostja@tarantool.org> Date: Wed, 18 Mar 2015 15:09:34 +0300 Subject: [PATCH] fiber scheduler: implement more fair scheduler of "ready" fibers Change fiber_sleep(0) implementation to use fiber_wakeup(). Implement a more fair scheduling for "ready" fibers: they are scheduled in the next event loop iteration, not in the same. --- src/fiber.cc | 70 +++++++++++++++++++++------------------ test/app/console.test.lua | 2 +- test/unit/fiber_stress.cc | 4 +-- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/fiber.cc b/src/fiber.cc index 9779f2ce33..ff02fb3132 100644 --- a/src/fiber.cc +++ b/src/fiber.cc @@ -102,8 +102,16 @@ fiber_wakeup(struct fiber *f) /** Remove the fiber from whatever wait list it is on. */ rlist_del(&f->state); struct cord *cord = cord(); - if (rlist_empty(&cord->ready)) - ev_feed_event(cord->loop, &cord->wakeup_event, EV_CUSTOM); + if (rlist_empty(&cord->ready)) { + /* + * ev_feed_event() is possibly faster, + * but custom event gets scheduled in the + * same event loop iteration, which can + * produce unfair scheduling (see the case of + * fiber_sleep(0)) + */ + ev_async_send(cord->loop, &cord->wakeup_event); + } rlist_move_tail_entry(&cord->ready, f, state); } @@ -203,21 +211,15 @@ void fiber_join(struct fiber *fiber) { assert(fiber->flags & FIBER_IS_JOINABLE); - if (fiber->flags & FIBER_IS_DEAD) { - /* The fiber is already dead. */ - fiber_recycle(fiber); - } else { + + if (! (fiber->flags & FIBER_IS_DEAD)) { rlist_add_tail_entry(&fiber->wake, fiber(), state); fiber_yield(); - /* @todo: make this branch async-cancel-safe */ - assert(! (fiber->flags & FIBER_IS_DEAD)); - /* - * Let the fiber recycle. - * This can't be done here since there may be other - * waiters in fiber->wake list, which must run first. - */ - fiber_set_joinable(fiber, false); } + assert(fiber->flags & FIBER_IS_DEAD); + /* The fiber is already dead. */ + fiber_recycle(fiber); + Exception::move(&fiber->exception, &fiber()->exception); if (fiber()->exception && typeid(*fiber()->exception) != typeid(FiberCancelException)) { @@ -291,7 +293,13 @@ fiber_yield_timeout(ev_tstamp delay) void fiber_sleep(ev_tstamp delay) { - fiber_yield_timeout(delay); + if (delay == 0) { + /* Faster than starting and stopping a timer. */ + fiber_wakeup(fiber()); + fiber_yield(); + } else { + fiber_yield_timeout(delay); + } fiber_testcancel(); } @@ -305,9 +313,13 @@ fiber_schedule_cb(ev_loop * /* loop */, ev_watcher *watcher, int /* revents */) static inline void fiber_schedule_list(struct rlist *list) { - while (! rlist_empty(list)) { + /** Don't schedule both lists at the same time. */ + struct rlist copy; + rlist_create(©); + rlist_swap(list, ©); + while (! rlist_empty(©)) { struct fiber *f; - f = rlist_shift_entry(list, struct fiber, state); + f = rlist_shift_entry(©, struct fiber, state); fiber_call(f); } } @@ -407,25 +419,17 @@ fiber_loop(void *data __attribute__((unused))) fiber_name(fiber)); panic("fiber `%s': exiting", fiber_name(fiber)); } - fiber_schedule_list(&fiber->wake); - /** - * By convention, these triggers must not throw. - * Call triggers after scheduling, since an - * on_stop trigger of the first fiber may - * break the event loop. - */ + fiber->flags |= FIBER_IS_DEAD; + while (! rlist_empty(&fiber->wake)) { + struct fiber *f; + f = rlist_shift_entry(&fiber->wake, struct fiber, + state); + fiber_wakeup(f); + } if (! rlist_empty(&fiber->on_stop)) trigger_run(&fiber->on_stop, fiber); - if (fiber->flags & FIBER_IS_JOINABLE) { - /* - * The fiber needs to be joined, - * and the joiner has not shown up yet, - * wait. - */ - fiber->flags |= FIBER_IS_DEAD; - } else { + if (! (fiber->flags & FIBER_IS_JOINABLE)) fiber_recycle(fiber); - } fiber_yield(); /* give control back to scheduler */ } } diff --git a/test/app/console.test.lua b/test/app/console.test.lua index d62f9a59b6..4bbb9ec570 100755 --- a/test/app/console.test.lua +++ b/test/app/console.test.lua @@ -129,7 +129,7 @@ client:close() server:shutdown() server:close() fiber.sleep(0) -- workaround for gh-712: console.test.lua fails in Fedora --- Check that admon console has been stopped +-- Check that admin console has been stopped test:isnil(socket.tcp_connect("unix/", CONSOLE_SOCKET), "console.listen stopped") -- Stop iproto diff --git a/test/unit/fiber_stress.cc b/test/unit/fiber_stress.cc index 1672b3be54..2afcb382f3 100644 --- a/test/unit/fiber_stress.cc +++ b/test/unit/fiber_stress.cc @@ -2,8 +2,8 @@ #include "fiber.h" enum { - ITERATIONS = 5000, - FIBERS = 10 + ITERATIONS = 50000, + FIBERS = 100 }; void yield_f(va_list ap) -- GitLab