From 0bf062abb9704f20cab7a99e9e7c4b170e01c7c1 Mon Sep 17 00:00:00 2001
From: Florian Fischer <florian.fischer@muhq.space>
Date: Wed, 28 Apr 2021 22:00:43 +0200
Subject: [PATCH] [Runtime] override pinning behavior by setting
 EMPER_PIN_WORKERS environment variable

---
 emper/Runtime.cpp | 22 +++++++++++++++-------
 emper/Runtime.hpp |  8 +++++++-
 emper/lib/env.hpp | 30 ++++++++++++++++++++++++++++++
 3 files changed, 52 insertions(+), 8 deletions(-)
 create mode 100644 emper/lib/env.hpp

diff --git a/emper/Runtime.cpp b/emper/Runtime.cpp
index 8798bc84..cadd6ed4 100644
--- a/emper/Runtime.cpp
+++ b/emper/Runtime.cpp
@@ -102,10 +102,16 @@ Runtime::Runtime(workerid_t workerCount, RuntimeStrategyFactory& strategyFactory
 		}
 	}
 
+	bool pinWorkers = shouldPinWorkers();
+
 	// Core id we start the worker pinning
 	workerid_t pinningOffset = 0;
 	char* pinningOffsetEnv = std::getenv("EMPER_PINNING_OFFSET");
 	if (pinningOffsetEnv) {
+		if (!pinWorkers) {
+			DIE_MSG("EMPER_PIN_WORKERS=false and EMPER_PINNING_OFFSET are mutual exclusive");
+		}
+
 		int pinningOffsetInt = std::stoi(pinningOffsetEnv);
 		if (pinningOffsetInt > UINT8_MAX) {
 			DIE_MSG("Pinning offset " << pinningOffsetInt << " to big for its datatype");
@@ -118,14 +124,16 @@ Runtime::Runtime(workerid_t workerCount, RuntimeStrategyFactory& strategyFactory
 		errno = pthread_attr_init(&attr);
 		if (errno) DIE;
 
-		// Start non portable.
-		cpu_set_t cpuset;
-		CPU_ZERO(&cpuset);
-		CPU_SET((i + pinningOffset) % nprocs, &cpuset);
+		if (pinWorkers) {
+			// Start non portable.
+			cpu_set_t cpuset;
+			CPU_ZERO(&cpuset);
+			CPU_SET((i + pinningOffset) % nprocs, &cpuset);
 
-		errno = pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset);
-		if (errno) DIE_MSG_ERRNO("pthread_attr_setaffinity_np() failed");
-		// End non portable.
+			errno = pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset);
+			if (errno) DIE_MSG_ERRNO("pthread_attr_setaffinity_np() failed");
+			// End non portable.
+		}
 
 		// This is not done by each thread individually because the randomEngine is
 		// mutated and therefore produces data races.
diff --git a/emper/Runtime.hpp b/emper/Runtime.hpp
index 0837ec35..1fc424eb 100644
--- a/emper/Runtime.hpp
+++ b/emper/Runtime.hpp
@@ -11,6 +11,7 @@
 #include <functional>	 // for function
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <random>
 #include <vector>	 // for vector
 
@@ -20,7 +21,8 @@
 #include "NextFiberResult.hpp"
 #include "Scheduler.hpp"	// for Scheduler
 #include "Worker.hpp"
-#include "emper-common.h"			 // for workerid_t
+#include "emper-common.h"	 // for workerid_t
+#include "lib/env.hpp"
 #include "lib/sync/Latch.hpp"	 // for Latch
 #include "lib/sync/Semaphore.hpp"
 #include "sleep_strategy/WorkerSleepStrategy.hpp"
@@ -83,6 +85,10 @@ class Runtime : public Logger<LogSubsystem::RUNTI> {
 
 	static auto getDefaultWorkerCount() -> workerid_t;
 
+	static auto shouldPinWorkers() -> bool {
+		return emper::lib::env::getBoolFromEnv("EMPER_PIN_WORKERS").value_or(true);
+	}
+
  protected:
 	void addNewWorkerHook(const std::function<void(workerid_t)>& hook) {
 		newWorkerHooks.push_back(hook);
diff --git a/emper/lib/env.hpp b/emper/lib/env.hpp
new file mode 100644
index 00000000..65b6c4fc
--- /dev/null
+++ b/emper/lib/env.hpp
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright © 2021 Florian Fischer
+#pragma once
+
+#include <string>
+
+#include "Debug.hpp"
+
+namespace emper::lib::env {
+
+static auto getBoolFromEnv(const std::string&& key) -> std::optional<bool> {
+	DBG("parse " << key << " environment variable");
+	char* envVar = std::getenv(key.c_str());
+	if (!envVar) {
+		return std::nullopt;
+	}
+
+	std::string envStr(envVar);
+	if (envStr == "true") {
+		return true;
+	}
+
+	if (envStr == "false") {
+		return false;
+	}
+
+	DIE_MSG(key << " has invalid value: " << envStr << " (expected true or false)");
+}
+
+}	 // namespace emper::lib::env
-- 
GitLab