From 925c6af8f19e8bc28068bd5ee0fd25480d01440c Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@tarantool.org>
Date: Wed, 9 Sep 2015 21:51:07 +0300
Subject: [PATCH] ipc: add a simple unit test

Correct return value of ipc_channel_put(), since
it may return an error if the channel is closed.

Make get/put functions inline.

Remove a public declaration for ipc_channel_is_readonly() (towards
removing the concept of readonly altogether).
---
 src/ipc.cc                  | 16 ++-----
 src/ipc.h                   | 78 +++++++++++++++++---------------
 test/unit/CMakeLists.txt    |  5 ++-
 test/unit/ipc.cc            | 88 ++++++++++++++++++++++++++-----------
 test/unit/ipc.result        | 25 ++++++++++-
 test/unit/ipc_stress.cc     | 55 +++++++++++++++++++++++
 test/unit/ipc_stress.result |  3 ++
 7 files changed, 194 insertions(+), 76 deletions(-)
 create mode 100644 test/unit/ipc_stress.cc
 create mode 100644 test/unit/ipc_stress.result

diff --git a/src/ipc.cc b/src/ipc.cc
index 89e23dfb33..5b253111d1 100644
--- a/src/ipc.cc
+++ b/src/ipc.cc
@@ -172,12 +172,6 @@ ipc_channel_get_timeout(struct ipc_channel *ch, ev_tstamp timeout)
 	return res;
 }
 
-void *
-ipc_channel_get(struct ipc_channel *ch)
-{
-	return ipc_channel_get_timeout(ch, TIMEOUT_INFINITY);
-}
-
 static void
 ipc_channel_close_waiter(struct ipc_channel *ch, struct fiber *f)
 {
@@ -217,13 +211,15 @@ ipc_channel_close(struct ipc_channel *ch)
 {
 	if (ch->closed)
 		return;
-	assert(ch->readonly);
+	if (!ch->readonly)
+		ipc_channel_shutdown(ch);
 	assert(ch->count == 0);
 	assert(rlist_empty(&ch->readers));
 	assert(rlist_empty(&ch->writers));
 	assert(ch->bcast == NULL);
 	ch->closed = true;
 }
+
 int
 ipc_channel_put_timeout(struct ipc_channel *ch, void *data,
 			ev_tstamp timeout)
@@ -291,12 +287,6 @@ ipc_channel_put_timeout(struct ipc_channel *ch, void *data,
 	return res;
 }
 
-void
-ipc_channel_put(struct ipc_channel *ch, void *data)
-{
-	ipc_channel_put_timeout(ch, data, TIMEOUT_INFINITY);
-}
-
 int
 ipc_channel_broadcast(struct ipc_channel *ch, void *data)
 {
diff --git a/src/ipc.h b/src/ipc.h
index 39bd4c4c0e..312d51c8bc 100644
--- a/src/ipc.h
+++ b/src/ipc.h
@@ -39,10 +39,19 @@
  */
 
 struct ipc_channel {
-	struct rlist readers, writers;
+	/**
+	 * Readers blocked waiting for messages while the channel
+	 * is empty.
+	 */
+	struct rlist readers;
+	/**
+	 * Writers blocked waiting for empty space while
+	 * the channel is full.
+	 */
+	struct rlist writers;
 	struct fiber *bcast;		/* broadcast waiter */
 	struct fiber *close;		/* close waiter */
-	bool readonly;			/* channel is for read only */
+	bool readonly;			/* channel is read only */
 	bool closed;			/* channel is closed */
 	unsigned size;
 	unsigned beg;
@@ -89,30 +98,6 @@ ipc_channel_new(unsigned size);
 void
 ipc_channel_delete(struct ipc_channel *ch);
 
-/**
- * @brief Put data into a channel.
- * @detail Yield current fiber if the channel is full.
- * @param channel
- * @param data
- * @code
- *	ipc_channel_put(ch, "message");
- * @endcode
- */
-void
-ipc_channel_put(struct ipc_channel *ch, void *data);
-
-/**
- * @brief Get data from a channel.
- * @detail Yield current fiber if the channel is empty.
- * @param channel
- * @return data that was put into channel by ipc_channel_put
- * @code
- *	char *msg = ipc_channel_get(ch);
- * @endcode
- */
-void *
-ipc_channel_get(struct ipc_channel *ch);
-
 /**
  * @brief Wake up all fibers that sleep in ipc_channel_get and
  * send a message to them.
@@ -173,6 +158,22 @@ int
 ipc_channel_put_timeout(struct ipc_channel *ch,	void *data,
 			ev_tstamp timeout);
 
+/**
+ * @brief Put data into a channel.
+ * @detail Yield current fiber if the channel is full.
+ * @param channel
+ * @param data
+ * @code
+ *	ipc_channel_put(ch, "message");
+ * @endcode
+ * @return  -1 if the channel is closed
+ */
+static inline int
+ipc_channel_put(struct ipc_channel *ch, void *data)
+{
+	return ipc_channel_put_timeout(ch, data, TIMEOUT_INFINITY);
+}
+
 /**
  * @brief Get data from a channel with a timeout
  * @param channel
@@ -190,6 +191,22 @@ ipc_channel_put_timeout(struct ipc_channel *ch,	void *data,
 void *
 ipc_channel_get_timeout(struct ipc_channel *ch, ev_tstamp timeout);
 
+/**
+ * @brief Get data from a channel.
+ * @detail Yield current fiber if the channel is empty.
+ * @param channel
+ * @return data that was put into channel by ipc_channel_put
+ * @code
+ *	char *msg = ipc_channel_get(ch);
+ * @endcode
+ */
+static inline void *
+ipc_channel_get(struct ipc_channel *ch)
+{
+	return ipc_channel_get_timeout(ch, TIMEOUT_INFINITY);
+}
+
+
 /**
  * @brief return true if channel has reader fibers that wait data
  * @param channel
@@ -237,15 +254,6 @@ ipc_channel_count(struct ipc_channel *ch)
 void
 ipc_channel_shutdown(struct ipc_channel *ch);
 
-/**
- * @brief return true if the channel is closed for writing
- */
-static inline bool
-ipc_channel_is_readonly(struct ipc_channel *ch)
-{
-	return ch->readonly;
-}
-
 /**
  * @brief close the channel.
  * @pre ipc_channel_is_readonly(ch) && ipc_channel_is_empty(ch)
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index c86a062ad7..f6f356182d 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -72,9 +72,12 @@ 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)
+add_executable(ipc.test ipc.cc unit.c ${CMAKE_SOURCE_DIR}/src/ipc.cc)
 target_link_libraries(ipc.test core)
 
+add_executable(ipc_stress.test ipc_stress.cc ${CMAKE_SOURCE_DIR}/src/ipc.cc)
+target_link_libraries(ipc_stress.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
index c5812bbf90..c475e84cad 100644
--- a/test/unit/ipc.cc
+++ b/test/unit/ipc.cc
@@ -3,43 +3,81 @@
 #include "ipc.h"
 #include "unit.h"
 
-enum {
-	ITERATIONS = 100000,
-};
+int status;
 
 void
-push_f(va_list ap)
+ipc_basic()
 {
-	struct ipc_channel *channel = va_arg(ap, struct ipc_channel *);
 
-	for (int i = 0; i < ITERATIONS; i++)
-		ipc_channel_put(channel, NULL);
-}
+	header();
+	plan(10);
 
-void
-pop_f(va_list ap)
-{
-	struct ipc_channel *channel = va_arg(ap, struct ipc_channel *);
+	struct ipc_channel *channel = ipc_channel_new(1);
+	ok(channel != NULL, "ipc_channel_new()");
+
+	ok(ipc_channel_size(channel) == 1, "ipc_channel_size()");
+
+	ok(ipc_channel_count(channel) == 0, "ipc_channel_count()");
+
+	ok(ipc_channel_is_full(channel) == false, "ipc_channel_is_full()");
+
+	ok(ipc_channel_is_empty(channel) == true, "ipc_channel_is_empty()");
+
+	char dummy;
+
+	ipc_channel_put(channel, &dummy);
+
+	ok(ipc_channel_size(channel) == 1, "ipc_channel_size(1)");
+
+	ok(ipc_channel_count(channel) == 1, "ipc_channel_count(1)");
+
+	ok(ipc_channel_is_full(channel) == true, "ipc_channel_is_full(1)");
+
+	ok(ipc_channel_is_empty(channel) == false, "ipc_channel_is_empty(1)");
+
+	ok(ipc_channel_get(channel) == &dummy, "ipc_channel_get()");
 
-	for (int i = 0; i < ITERATIONS; i++)
-		(void) ipc_channel_get(channel);
+	ipc_channel_delete(channel);
+
+	footer();
+	status = check_plan();
 }
 
-void main_f(va_list ap)
+void
+ipc_get()
 {
 	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);
+	plan(7);
+
 	struct ipc_channel *channel = ipc_channel_new(1);
-	fiber_start(push, channel);
-	fiber_start(pop, channel);
-	fiber_join(push);
-	fiber_join(pop);
+
+	char dummy;
+	ok(ipc_channel_put_timeout(channel, &dummy, 0) == 0,
+	   "ipc_channel_put(0)");
+	ok(ipc_channel_put_timeout(channel, &dummy, 0) == -1,
+	   "ipc_channel_put_timeout(0)");
+	ok(ipc_channel_get(channel) == &dummy, "ipc_channel_get(0)");
+	ok(ipc_channel_put_timeout(channel, &dummy, 0.01) == 0,
+	   "ipc_channel_put_timeout(1)");
+	ok(ipc_channel_get(channel) == &dummy, "ipc_channel_get(1)");
+
+	ipc_channel_close(channel);
+
+	ok(ipc_channel_put(channel, &dummy) == -1, "ipc_channel_put(closed)");
+
+	ok(ipc_channel_get(channel) == NULL, "ipc_channel_get(closed)");
+
 	ipc_channel_delete(channel);
-	ev_break(loop(), EVBREAK_ALL);
+
 	footer();
+	status = check_plan();
+}
+
+void main_f(va_list /* ap */)
+{
+	ipc_basic();
+	ipc_get();
+	ev_break(loop(), EVBREAK_ALL);
 }
 
 int main()
@@ -51,5 +89,5 @@ int main()
 	ev_run(loop(), 0);
 	fiber_free();
 	memory_free();
-	return 0;
+	return status;
 }
diff --git a/test/unit/ipc.result b/test/unit/ipc.result
index f3398fb9c7..a09ea9d85f 100644
--- a/test/unit/ipc.result
+++ b/test/unit/ipc.result
@@ -1,3 +1,24 @@
-	*** main_f ***
-	*** main_f: done ***
+	*** ipc_basic ***
+1..10
+ok 1 - ipc_channel_new()
+ok 2 - ipc_channel_size()
+ok 3 - ipc_channel_count()
+ok 4 - ipc_channel_is_full()
+ok 5 - ipc_channel_is_empty()
+ok 6 - ipc_channel_size(1)
+ok 7 - ipc_channel_count(1)
+ok 8 - ipc_channel_is_full(1)
+ok 9 - ipc_channel_is_empty(1)
+ok 10 - ipc_channel_get()
+	*** ipc_basic: done ***
+ 	*** ipc_get ***
+1..7
+ok 1 - ipc_channel_put(0)
+ok 2 - ipc_channel_put_timeout(0)
+ok 3 - ipc_channel_get(0)
+ok 4 - ipc_channel_put_timeout(1)
+ok 5 - ipc_channel_get(1)
+ok 6 - ipc_channel_put(closed)
+ok 7 - ipc_channel_get(closed)
+	*** ipc_get: done ***
  
\ No newline at end of file
diff --git a/test/unit/ipc_stress.cc b/test/unit/ipc_stress.cc
new file mode 100644
index 0000000000..c5812bbf90
--- /dev/null
+++ b/test/unit/ipc_stress.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_stress.result b/test/unit/ipc_stress.result
new file mode 100644
index 0000000000..f3398fb9c7
--- /dev/null
+++ b/test/unit/ipc_stress.result
@@ -0,0 +1,3 @@
+	*** main_f ***
+	*** main_f: done ***
+ 
\ No newline at end of file
-- 
GitLab