From 5395f540f8f016397872d8b12c066e2930d439ef Mon Sep 17 00:00:00 2001
From: Dmitry Simonenko <pmwkaa@gmail.com>
Date: Wed, 27 Mar 2013 16:32:20 +0400
Subject: [PATCH] Bug1160869: incorrect fiber call order.
 (https://bugs.launchpad.net/tarantool/+bug/1160869)

Lua socket object creates coio object that bind to a current
fiber, after yield it will resume it in a different fiber
context after a complete io operation.

Fix this situation by updating current coio fiber in most
lua socket operations.
---
 include/coio.h         |  3 +++
 src/coio.m             |  6 ++++++
 src/lua/lua_socket.m   | 10 ++++++++++
 test/box/socket.result | 19 +++++++++++++++++++
 test/box/socket.test   | 24 ++++++++++++++++++++++++
 5 files changed, 62 insertions(+)

diff --git a/include/coio.h b/include/coio.h
index da9814f2fe..11ad141763 100644
--- a/include/coio.h
+++ b/include/coio.h
@@ -65,6 +65,9 @@ coio_accept(struct ev_io *coio, struct sockaddr_in *addr, socklen_t addrlen,
 void
 coio_init(struct ev_io *coio);
 
+void
+coio_update_fiber(struct ev_io *coio);
+
 ssize_t
 coio_read_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz,
 		        ev_tstamp timeout);
diff --git a/src/coio.m b/src/coio.m
index c27da2523f..461f70c15d 100644
--- a/src/coio.m
+++ b/src/coio.m
@@ -46,6 +46,12 @@ coio_init(struct ev_io *coio)
 	coio->fd = -1;
 }
 
+void
+coio_update_fiber(struct ev_io *coio)
+{
+	coio->data = fiber;
+}
+
 /**
  * Connect to a host.
  */
diff --git a/src/lua/lua_socket.m b/src/lua/lua_socket.m
index e49e7b6397..337f5506b5 100644
--- a/src/lua/lua_socket.m
+++ b/src/lua/lua_socket.m
@@ -257,6 +257,7 @@ lbox_socket_shutdown(struct lua_State *L)
 {
 	struct bio_socket *s = bio_checkactivesocket(L, -1);
 	int how = luaL_checkint(L, 2);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 	if (shutdown(s->coio.fd, how))
 		return bio_pushsockerror(L, s, errno);
@@ -297,6 +298,7 @@ lbox_socket_connect(struct lua_State *L)
 		timeout = luaL_checknumber(L, 4);
 	if (evio_is_active(&s->coio))
 		return bio_pushsockerror(L, s, EALREADY);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 
 	/* try to resolve a hostname */
@@ -347,6 +349,7 @@ lbox_socket_send(struct lua_State *L)
 		timeout = luaL_checknumber(L, 3);
 	if (s->iob == NULL)
 		return bio_pushsenderror(L, s, 0, ENOTCONN);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 	@try {
 		ssize_t nwr = coio_write_timeout(&s->coio, buf, buf_size,
@@ -384,6 +387,7 @@ lbox_socket_recv(struct lua_State *L)
 		timeout = luaL_checknumber(L, 3);
 	if (s->iob == NULL)
 		return bio_pushrecverror(L, s, ENOTCONN);
+	coio_update_fiber(&s->coio);
 	/* Clear possible old timeout status. */
 	bio_clearerr(s);
 	/*
@@ -565,6 +569,7 @@ lbox_socket_readline(struct lua_State *L)
 	struct bio_socket *s = bio_checkactivesocket(L, 1);
 	if (s->iob == NULL)
 		return bio_pushrecverror(L, s, ENOTCONN);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 
 	unsigned int limit = UINT_MAX;
@@ -653,6 +658,7 @@ lbox_socket_bind(struct lua_State *L)
 		timeout = luaL_checknumber(L, 4);
 	if (evio_is_active(&s->coio))
 		return bio_pusherror(L, s, EALREADY);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 	/* try to resolve a hostname */
 	struct addrinfo *ai = coeio_resolve(s->socktype, host, port, timeout);
@@ -684,6 +690,7 @@ static int
 lbox_socket_listen(struct lua_State *L)
 {
 	struct bio_socket *s = bio_checkactivesocket(L, 1);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 	if (listen(s->coio.fd, sio_listen_backlog()))
 		return bio_pusherror(L, s, errno);
@@ -708,6 +715,7 @@ lbox_socket_accept(struct lua_State *L)
 	double timeout = TIMEOUT_INFINITY;
 	if (lua_gettop(L) == 2)
 		timeout = luaL_checknumber(L, 2);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 
 	struct sockaddr_storage addr;
@@ -757,6 +765,7 @@ lbox_socket_sendto(struct lua_State *L)
 	double timeout = TIMEOUT_INFINITY;
 	if (lua_gettop(L) == 5)
 		timeout = luaL_checknumber(L, 5);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 
 	/* try to resolve a hostname */
@@ -821,6 +830,7 @@ lbox_socket_recvfrom(struct lua_State *L)
 	double timeout = TIMEOUT_INFINITY;
 	if (lua_gettop(L) == 3)
 		timeout = luaL_checknumber(L, 3);
+	coio_update_fiber(&s->coio);
 	bio_clearerr(s);
 
 	/* Maybe initialize the buffer, can throw ER_MEMORY_ISSUE. */
diff --git a/test/box/socket.result b/test/box/socket.result
index befd43b982..61e50ff231 100644
--- a/test/box/socket.result
+++ b/test/box/socket.result
@@ -907,3 +907,22 @@ ping
 lua s:close()
 ---
 ...
+lua  reps = 0 function bug1160869() 	local s = box.socket.tcp() 	s:connect('127.0.0.1', box.cfg.primary_port) 	box.fiber.resume( box.fiber.create(function() 		box.fiber.detach() 		while true do 			s:recv(12) 			reps = reps + 1 		end 	end) ) 	return s:send(box.pack('iii', 65280, 0, 1)) end 
+---
+...
+lua bug1160869()
+---
+ - 12
+...
+lua bug1160869()
+---
+ - 12
+...
+lua bug1160869()
+---
+ - 12
+...
+lua reps
+---
+ - 3
+...
diff --git a/test/box/socket.test b/test/box/socket.test
index 180927a61c..0d64817b3b 100644
--- a/test/box/socket.test
+++ b/test/box/socket.test
@@ -509,3 +509,27 @@ print(data)
 s.close()
 
 exec admin "lua s:close()"
+
+# Bug #1160869: incorrect fiber call order.
+# (https://bugs.launchpad.net/tarantool/+bug/1160869)
+#
+test="""
+reps = 0
+function bug1160869()
+	local s = box.socket.tcp()
+	s:connect('127.0.0.1', box.cfg.primary_port)
+	box.fiber.resume( box.fiber.create(function()
+		box.fiber.detach()
+		while true do
+			s:recv(12)
+			reps = reps + 1
+		end
+	end) )
+	return s:send(box.pack('iii', 65280, 0, 1))
+end
+"""
+exec admin "lua " + test.replace('\n', ' ')
+exec admin "lua bug1160869()"
+exec admin "lua bug1160869()"
+exec admin "lua bug1160869()"
+exec admin "lua reps"
-- 
GitLab