diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c index 7ebd51617311722466f354071cdca9bf394a0ba5..37fda82c5895e0317d6116f56ce9b2e9d63ba1b0 100644 --- a/src/lib/core/fiber.c +++ b/src/lib/core/fiber.c @@ -627,43 +627,37 @@ fiber_join(struct fiber *fiber) return fiber_join_timeout(fiber, TIMEOUT_INFINITY); } +bool +fiber_wait_on_deadline(struct fiber *fiber, double deadline) +{ + rlist_add_tail_entry(&fiber->wake, fiber(), state); + + return fiber_yield_deadline(deadline); +} + int fiber_join_timeout(struct fiber *fiber, double timeout) { if ((fiber->flags & FIBER_IS_JOINABLE) == 0) panic("the fiber is not joinable"); - if (! fiber_is_dead(fiber)) { - bool exceeded = false; - do { + if (!fiber_is_dead(fiber)) { + double deadline = fiber_clock() + timeout; + while (!fiber_wait_on_deadline(fiber, deadline) && + !fiber_is_dead(fiber)) { + } + if (!fiber_is_dead(fiber)) { /* - * In case fiber is cancelled during yield - * it will be removed from wake queue by a - * wakeup following the cancel, so we have - * to put it back in. + * Not exactly the right error message for this place. + * Error message is generated based on the ETIMEDOUT + * code, that is used for network timeouts in linux. But + * in other places, this type of error is always used + * when the timeout expires, regardless of whether it is + * related to the network (see cbus_call for example). */ - rlist_add_tail_entry(&fiber->wake, fiber(), state); - if (timeout != TIMEOUT_INFINITY) { - double time = fiber_clock(); - exceeded = fiber_yield_timeout(timeout); - timeout -= (fiber_clock() - time); - } else { - fiber_yield(); - } - } while (! fiber_is_dead(fiber) && ! exceeded && timeout > 0); - } - - if (! fiber_is_dead(fiber)) { - /* - * Not exactly the right error message for this place. Error - * message is generated based on the ETIMEDOUT code, that is - * used for network timeouts in linux. But in other places, - * this type of error is always used when the timeout expires, - * regardless of whether it is related to the network (see - * cbus_call for example). - */ - diag_set(TimedOut); - return -1; + diag_set(TimedOut); + return -1; + } } assert((fiber->flags & FIBER_IS_RUNNING) == 0); assert((fiber->flags & FIBER_IS_JOINABLE) != 0); diff --git a/src/lib/core/fiber.h b/src/lib/core/fiber.h index 93f42002a5bbb79fd9141caa1b498a9f5ea652c4..821376edf0515a788542139b7a7bced553c3b25c 100644 --- a/src/lib/core/fiber.h +++ b/src/lib/core/fiber.h @@ -999,6 +999,13 @@ fiber_yield_timeout(ev_tstamp delay); bool fiber_yield_deadline(ev_tstamp deadline); +/** + * Add current fiber to f->wake and yield. + * Return true if deadline exceeded. + */ +bool +fiber_wait_on_deadline(struct fiber *f, double deadline); + void fiber_destroy_all(struct cord *cord); diff --git a/test/unit/fiber.cc b/test/unit/fiber.cc index 09ef30cf1e985207994375259a24f4a8b4001cec..e65490d129ca0b17f9ef72be1047497eb2d34158 100644 --- a/test/unit/fiber.cc +++ b/test/unit/fiber.cc @@ -329,6 +329,31 @@ fiber_flags_respect_test(void) footer(); } +static void +fiber_wait_on_deadline_test() +{ + header(); + + struct fiber *fiber = fiber_new_xc("noop", noop_f); + fiber_set_joinable(fiber, true); + fiber_wakeup(fiber); + bool exceeded = fiber_wait_on_deadline(fiber, fiber_clock() + 100.0); + fail_if(exceeded); + fail_if(!fiber_is_dead(fiber)); + fiber_join(fiber); + + fiber = fiber_new_xc("cancel", cancel_f); + fiber_set_joinable(fiber, true); + fiber_wakeup(fiber); + exceeded = fiber_wait_on_deadline(fiber, fiber_clock() + 0.001); + fail_if(!exceeded); + fail_if(fiber_is_dead(fiber)); + fiber_cancel(fiber); + fiber_join(fiber); + + footer(); +} + static void cord_cojoin_test(void) { @@ -361,6 +386,7 @@ main_f(va_list ap) fiber_wakeup_dead_test(); fiber_dead_while_in_cache_test(); fiber_flags_respect_test(); + fiber_wait_on_deadline_test(); cord_cojoin_test(); ev_break(loop(), EVBREAK_ALL); return 0; diff --git a/test/unit/fiber.result b/test/unit/fiber.result index 61f5f05dfb862039b28858889d9487de11ddb40e..71dce6437fc78ba7f4a381841dbf646909429f2f 100644 --- a/test/unit/fiber.result +++ b/test/unit/fiber.result @@ -25,5 +25,7 @@ OutOfMemory: Failed to allocate 42 bytes in allocator for exception *** fiber_dead_while_in_cache_test: done *** *** fiber_flags_respect_test *** *** fiber_flags_respect_test: done *** + *** fiber_wait_on_deadline_test *** + *** fiber_wait_on_deadline_test: done *** *** cord_cojoin_test *** *** cord_cojoin_test: done ***