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 ***