From 1d5fc6d3e335e0f97a1208c9626d38b30e57c7d0 Mon Sep 17 00:00:00 2001
From: Florian Schmaus <flow@cs.fau.de>
Date: Thu, 3 Aug 2017 19:26:23 +0200
Subject: [PATCH] Add postRetrieve hook to UnboundedBlockingMpscQueue

and use that hook in Actor.

This fixes a race condition in the actor loop between 'queue.get()'
and 'setState(Running)' racing against waitUntilIdle(long)'s check if

queue.size() == 0 && state == Retrieving

Because right after the get() operation is finished, the queue size is
0, but the state is not yet at Running, while the Actor actually is
not idle.

Thanks Raim! Best rubber duck ever!
---
 emper/Actor.hpp                      |  5 +++--
 emper/UnboundedBlockingMpscQueue.hpp | 12 +++++++-----
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/emper/Actor.hpp b/emper/Actor.hpp
index e4cc4296..9810fe61 100644
--- a/emper/Actor.hpp
+++ b/emper/Actor.hpp
@@ -32,9 +32,10 @@ private:
 
 		while (state == Running) {
 			setState(Retrieving);
-			T t = queue.get();
+			T t = queue.get([this] {
+					setState(Running);
+				});
 
-			setState(Running);
 			receive(t);
 		}
 
diff --git a/emper/UnboundedBlockingMpscQueue.hpp b/emper/UnboundedBlockingMpscQueue.hpp
index 293c78ab..c28449ab 100644
--- a/emper/UnboundedBlockingMpscQueue.hpp
+++ b/emper/UnboundedBlockingMpscQueue.hpp
@@ -26,12 +26,13 @@ private:
 		}
 	}
 
-	void tryToGetElement() {
+	void tryToGetElement(std::function<void(void)> postRetrieve) {
 		std::lock_guard<std::mutex> lock(queueMutex);
 		if (!mpscQueue.empty()) {
 			t = mpscQueue.front();
 			mpscQueue.pop();
 			tPopped = true;
+			postRetrieve();
 		}
 	}
 
@@ -57,16 +58,16 @@ public:
 		}
 	}
 
-	T get() {
+	T get(std::function<void(void)> postRetrieve) {
 		tPopped = false;
-		tryToGetElement();
+		tryToGetElement(postRetrieve);
 
 		if (!tPopped) {
 			Context* context = Context::getCurrentContext();
-			block([this, context] {
+			block([this, context, postRetrieve] {
 					blockedContext = context;
 
-					tryToGetElement();
+					tryToGetElement(postRetrieve);
 					if (tPopped) {
 						tryToWakeupBlockedContext();
 					}
@@ -79,6 +80,7 @@ public:
 				assert(!mpscQueue.empty());
 				t = mpscQueue.front();
 				mpscQueue.pop();
+				postRetrieve();
 			}
 		}
 		return t;
-- 
GitLab