From c33af2888ae57d942227219ae3c9a196bb4ecea9 Mon Sep 17 00:00:00 2001
From: Georgy Kirichenko <georgy@tarantool.org>
Date: Sun, 16 Jul 2017 11:02:32 +0300
Subject: [PATCH] Handle coio_call spurious wakeups

coio_call() must not release stack until all work is done.
Repeat yielding until task is completed in a worker thread.

Fixes #2604
---
 src/coio_task.c       | 11 +++--------
 test/unit/coio.cc     | 27 +++++++++++++++++++++++++++
 test/unit/coio.result |  3 +++
 3 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/src/coio_task.c b/src/coio_task.c
index 081b1e4793..0ba83a5a32 100644
--- a/src/coio_task.c
+++ b/src/coio_task.c
@@ -274,19 +274,14 @@ coio_call(ssize_t (*func)(va_list ap), ...)
 	task->complete = 0;
 	diag_create(&task->diag);
 
-	bool cancellable = fiber_set_cancellable(false);
-
 	va_start(task->ap, func);
 	eio_submit(&task->base);
 
-	fiber_yield();
-	/* Spurious wakeup indicates a severe BUG, fail early. */
-	if (task->complete == 0)
-		panic("Wrong fiber woken");
+	do {
+		fiber_yield();
+	} while (task->complete == 0);
 	va_end(task->ap);
 
-	fiber_set_cancellable(cancellable);
-
 	ssize_t result = task->base.result;
 	int save_errno = errno;
 	if (result)
diff --git a/test/unit/coio.cc b/test/unit/coio.cc
index 9813988ea0..d1e7445087 100644
--- a/test/unit/coio.cc
+++ b/test/unit/coio.cc
@@ -1,6 +1,7 @@
 #include "memory.h"
 #include "fiber.h"
 #include "coio.h"
+#include "coio_task.h"
 #include "fio.h"
 #include "unit.h"
 #include "unit.h"
@@ -50,6 +51,23 @@ stat_timeout_test(const char *filename)
 	footer();
 }
 
+static ssize_t
+coio_test_wakeup(va_list ap)
+{
+	usleep(1000);
+	return 0;
+}
+
+static int
+test_call_f(va_list ap)
+{
+	header();
+	int res = coio_call(coio_test_wakeup);
+	note("call done with res %i", res);
+	footer();
+	return res;
+}
+
 static int
 main_f(va_list ap)
 {
@@ -59,6 +77,15 @@ main_f(va_list ap)
 	stat_notify_test(f, filename);
 	fclose(f);
 	(void) remove(filename);
+
+	coio_enable();
+	struct fiber *call_fiber = fiber_new_xc("coio_call wakeup", test_call_f);
+	fiber_set_joinable(call_fiber, true);
+	fiber_start(call_fiber);
+	fiber_wakeup(call_fiber);
+	fiber_cancel(call_fiber);
+	fiber_join(call_fiber);
+
 	ev_break(loop(), EVBREAK_ALL);
 	return 0;
 }
diff --git a/test/unit/coio.result b/test/unit/coio.result
index b8391a50c6..7d74779797 100644
--- a/test/unit/coio.result
+++ b/test/unit/coio.result
@@ -3,3 +3,6 @@
 	*** stat_notify_test ***
 # filename: 1.out
 	*** stat_notify_test: done ***
+	*** test_call_f ***
+# call done with res 0
+	*** test_call_f: done ***
-- 
GitLab