From 0858590216ba36cd5e4f69be84f643fd00a85e87 Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@tarantool.org>
Date: Mon, 2 Feb 2015 15:16:51 +0300
Subject: [PATCH] Add a fiber benchmark.

Remove explicit fiber call stack from the cord.

Fix backtrace() to work correclty with an optimized (-O2) build.
---
 src/fiber.cc       | 15 ++++++++-------
 src/fiber.h        | 14 +++++++-------
 src/util.cc        | 17 ++++++++++++++---
 test/unit/fiber.cc | 32 ++++++++++++++++++++++++++++++++
 4 files changed, 61 insertions(+), 17 deletions(-)

diff --git a/src/fiber.cc b/src/fiber.cc
index d1d64f6d1e..c396d240e5 100644
--- a/src/fiber.cc
+++ b/src/fiber.cc
@@ -58,16 +58,16 @@ fiber_call(struct fiber *callee)
 	struct fiber *caller = fiber();
 	struct cord *cord = cord();
 
-	assert(cord->sp + 1 - cord->stack < FIBER_CALL_STACK);
+	assert(cord->call_stack_depth < FIBER_CALL_STACK);
 	assert(caller);
 
+	cord->call_stack_depth++;
 	cord->fiber = callee;
-	*cord->sp++ = caller;
+	callee->caller = caller;
 
 	update_last_stack_frame(caller);
 
 	callee->csw++;
-
 	coro_transfer(&caller->coro.ctx, &callee->coro.ctx);
 }
 
@@ -82,8 +82,7 @@ fiber_start(struct fiber *callee, ...)
 bool
 fiber_checkstack()
 {
-	struct cord *cord = cord();
-	if (cord->sp + 1 - cord->stack >= FIBER_CALL_STACK)
+	if (cord()->call_stack_depth + 1 >= FIBER_CALL_STACK)
 		return true;
 	return false;
 }
@@ -192,8 +191,10 @@ void
 fiber_yield(void)
 {
 	struct cord *cord = cord();
-	struct fiber *callee = *(--cord->sp);
 	struct fiber *caller = cord->fiber;
+	struct fiber *callee = caller->caller;
+	cord->call_stack_depth--;
+	caller->caller = &cord->sched;
 
 	/** By convention, these triggers must not throw. */
 	if (! rlist_empty(&caller->on_yield))
@@ -413,6 +414,7 @@ fiber_new(const char *name, void (*f) (va_list))
 		region_create(&fiber->gc, &cord->slabc);
 
 		rlist_add_entry(&cord->fibers, fiber, link);
+		fiber->caller = &cord->sched;
 	}
 
 	fiber->f = f;
@@ -483,7 +485,6 @@ cord_create(struct cord *cord, const char *name)
 	region_create(&cord->sched.gc, &cord->slabc);
 	fiber_set_name(&cord->sched, "sched");
 
-	cord->sp = cord->stack;
 	cord->max_fid = 100;
 	Exception::init(cord);
 
diff --git a/src/fiber.h b/src/fiber.h
index ecb332166b..d78dcbaac8 100644
--- a/src/fiber.h
+++ b/src/fiber.h
@@ -101,6 +101,11 @@ struct fiber {
 #ifdef ENABLE_BACKTRACE
 	void *last_stack_frame;
 #endif
+	/**
+	 * The fiber which should be scheduled when
+	 * this fiber yields.
+	 */
+	struct fiber *caller;
 	int csw;
 	struct tarantool_coro coro;
 	/* A garbage-collected memory pool. */
@@ -145,13 +150,8 @@ struct cord {
 	/** The "main" fiber of this cord, the scheduler. */
 	struct fiber sched;
 	struct ev_loop *loop;
-	/** Call stack - in case one fiber decides to call
-	 * another with fiber_call(). This is not used
-	 * currently, all fibers are called by the sched
-         */
-	struct fiber *stack[FIBER_CALL_STACK];
-	/** Stack pointer in fiber call stack. */
-	struct fiber **sp;
+	/** Depth of the fiber call stack. */
+	int call_stack_depth;
 	/**
 	 * Every new fiber gets a new monotonic id. Ids 1-100 are
 	 * reserved.
diff --git a/src/util.cc b/src/util.cc
index 84c021b48b..0e2165f650 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -231,7 +231,8 @@ fmemopen(void *buf, size_t size, const char *mode)
 static char backtrace_buf[4096 * 4];
 
 /*
- * note, stack unwinding code assumes that binary is compiled with frame pointers
+ * note, stack unwinding code assumes that binary is compiled with
+ * frame pointers
  */
 
 struct frame {
@@ -250,6 +251,11 @@ backtrace(void *frame_, void *stack, size_t stack_size)
 	char *end = p + sizeof(backtrace_buf) - 1;
 	int frameno = 0;
 	while (stack_bottom <= (void *)frame && (void *)frame < stack_top) {
+		/**
+		 * The stack may be overwritten by the callback
+		 * in case of optimized builds.
+		 */
+		struct frame *prev_frame = frame->rbp;
 		p += snprintf(p, end - p, "#%-2d %p in ", frameno++, frame->ret);
 
 		if (p >= end)
@@ -275,7 +281,7 @@ backtrace(void *frame_, void *stack, size_t stack_size)
 			break;
 
 #endif
-		frame = frame->rbp;
+		frame = prev_frame;
 	}
 out:
 	*p = '\0';
@@ -293,6 +299,11 @@ backtrace_foreach(backtrace_cb cb, void *frame_, void *stack, size_t stack_size,
 	const char *sym = NULL;
 	size_t offset = 0;
 	while (stack_bottom <= (void *)frame && (void *)frame < stack_top) {
+		/**
+		 * The stack may be overwritten by the callback
+		 * in case of optimized builds.
+		 */
+		struct frame *prev_frame = frame->rbp;
 #ifdef HAVE_BFD
 		struct symbol *s = addr2symbol(frame->ret);
 		if (s != NULL) {
@@ -307,7 +318,7 @@ backtrace_foreach(backtrace_cb cb, void *frame_, void *stack, size_t stack_size,
 		int rc = cb(frameno, frame->ret, sym, offset, cb_ctx);
 		if (rc != 0)
 			return;
-		frame = frame->rbp;
+		frame = prev_frame;
 		sym = NULL;
 		offset = 0;
 		frameno++;
diff --git a/test/unit/fiber.cc b/test/unit/fiber.cc
index d851007323..1672b3be54 100644
--- a/test/unit/fiber.cc
+++ b/test/unit/fiber.cc
@@ -1,9 +1,41 @@
 #include "memory.h"
 #include "fiber.h"
+
+enum {
+	ITERATIONS = 5000,
+	FIBERS = 10
+};
+
+void yield_f(va_list ap)
+{
+	for (int i = 0; i < ITERATIONS; i++) {
+		fiber_wakeup(fiber());
+		fiber_yield();
+	}
+}
+
+void benchmark_f(va_list ap)
+{
+	struct fiber *fibers[FIBERS];
+	for (int i = 0; i < FIBERS; i++) {
+		fibers[i] = fiber_new("yield-wielder", yield_f);
+		fiber_wakeup(fibers[i]);
+	}
+	/** Wait for fibers to die. */
+	for (int i = 0; i < FIBERS; i++) {
+		while (fibers[i]->fid > 0)
+			fiber_sleep(0.001);
+	}
+	ev_break(loop(), EVBREAK_ALL);
+}
+
 int main()
 {
 	memory_init();
 	fiber_init();
+	struct fiber *benchmark = fiber_new("benchmark", benchmark_f);
+	fiber_wakeup(benchmark);
+	ev_run(loop(), 0);
 	fiber_free();
 	memory_free();
 	return 0;
-- 
GitLab