From 4fe852430f8f78265be1bbc01e897f0d889f6d7e Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@tarantool.org>
Date: Fri, 20 Mar 2015 17:05:15 +0300
Subject: [PATCH] fiber scheduling: schedule ready fibers more aggressively

If a fiber becomes ready on a user-defined event, schedule it
in the same event loop, not in the next loop, to save one epoll_wait()
invocation in cases when something is added to a fiber channel,
and there is a fiber ready for execution waiting on the channel.

Add a micro-bench to the unit test suite.
---
 src/fiber.cc             | 18 ++++++-------
 test/unit/CMakeLists.txt |  3 +++
 test/unit/ipc.cc         | 55 ++++++++++++++++++++++++++++++++++++++++
 test/unit/ipc.result     |  3 +++
 4 files changed, 69 insertions(+), 10 deletions(-)
 create mode 100644 test/unit/ipc.cc
 create mode 100644 test/unit/ipc.result

diff --git a/src/fiber.cc b/src/fiber.cc
index ff02fb3132..111aaca7da 100644
--- a/src/fiber.cc
+++ b/src/fiber.cc
@@ -105,12 +105,12 @@ fiber_wakeup(struct fiber *f)
 	if (rlist_empty(&cord->ready)) {
 		/*
 		 * ev_feed_event() is possibly faster,
-		 * but custom event gets scheduled in the
+		 * but EV_CUSTOM event gets scheduled in the
 		 * same event loop iteration, which can
-		 * produce unfair scheduling (see the case of
+		 * produce unfair scheduling, (see the case of
 		 * fiber_sleep(0))
 		 */
-		ev_async_send(cord->loop, &cord->wakeup_event);
+		ev_feed_event(cord->loop, &cord->wakeup_event, EV_CUSTOM);
 	}
 	rlist_move_tail_entry(&cord->ready, f, state);
 }
@@ -293,13 +293,11 @@ fiber_yield_timeout(ev_tstamp delay)
 void
 fiber_sleep(ev_tstamp delay)
 {
-	if (delay == 0) {
-		/* Faster than starting and stopping a timer. */
-		fiber_wakeup(fiber());
-		fiber_yield();
-	} else {
-		fiber_yield_timeout(delay);
-	}
+	/*
+	 * We don't use fiber_wakeup() here to ensure there is
+	 * no infinite wakeup loop in case of fiber_sleep(0).
+	 */
+	fiber_yield_timeout(delay);
 	fiber_testcancel();
 }
 
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 1b8d8c0088..4833461371 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -65,6 +65,9 @@ target_link_libraries(fiber.test core)
 add_executable(fiber_stress.test fiber_stress.cc)
 target_link_libraries(fiber_stress.test core)
 
+add_executable(ipc.test ipc.cc ${CMAKE_SOURCE_DIR}/src/ipc.cc)
+target_link_libraries(ipc.test core)
+
 add_executable(coio.test coio.cc unit.c
         ${CMAKE_SOURCE_DIR}/src/sio.cc
         ${CMAKE_SOURCE_DIR}/src/evio.cc
diff --git a/test/unit/ipc.cc b/test/unit/ipc.cc
new file mode 100644
index 0000000000..c5812bbf90
--- /dev/null
+++ b/test/unit/ipc.cc
@@ -0,0 +1,55 @@
+#include "memory.h"
+#include "fiber.h"
+#include "ipc.h"
+#include "unit.h"
+
+enum {
+	ITERATIONS = 100000,
+};
+
+void
+push_f(va_list ap)
+{
+	struct ipc_channel *channel = va_arg(ap, struct ipc_channel *);
+
+	for (int i = 0; i < ITERATIONS; i++)
+		ipc_channel_put(channel, NULL);
+}
+
+void
+pop_f(va_list ap)
+{
+	struct ipc_channel *channel = va_arg(ap, struct ipc_channel *);
+
+	for (int i = 0; i < ITERATIONS; i++)
+		(void) ipc_channel_get(channel);
+}
+
+void main_f(va_list ap)
+{
+	header();
+	struct fiber *push = fiber_new("push_f", push_f);
+	fiber_set_joinable(push, true);
+	struct fiber *pop = fiber_new("pop_f", pop_f);
+	fiber_set_joinable(pop, true);
+	struct ipc_channel *channel = ipc_channel_new(1);
+	fiber_start(push, channel);
+	fiber_start(pop, channel);
+	fiber_join(push);
+	fiber_join(pop);
+	ipc_channel_delete(channel);
+	ev_break(loop(), EVBREAK_ALL);
+	footer();
+}
+
+int main()
+{
+	memory_init();
+	fiber_init();
+	struct fiber *main= fiber_new("main", main_f);
+	fiber_wakeup(main);
+	ev_run(loop(), 0);
+	fiber_free();
+	memory_free();
+	return 0;
+}
diff --git a/test/unit/ipc.result b/test/unit/ipc.result
new file mode 100644
index 0000000000..f3398fb9c7
--- /dev/null
+++ b/test/unit/ipc.result
@@ -0,0 +1,3 @@
+	*** main_f ***
+	*** main_f: done ***
+ 
\ No newline at end of file
-- 
GitLab