From 1817468030bd0f45d42296883c724e7112f61e79 Mon Sep 17 00:00:00 2001
From: Florian Schmaus <flow@cs.fau.de>
Date: Sat, 19 Feb 2022 15:43:57 +0100
Subject: [PATCH] [LinuxVersion] Use "Construct on first use" idiom and add
 mutex

Use the "Construct on first use" idom for UTS Release (aka Linux
version) initialization. The previous implementation was fragle and
could lead to segfaults like

    __s2=0x7fffffffe480 "5.16.10-arch1-1", __s1=<optimized out>)
    at /usr/include/c++/11.2.0/bits/char_traits.h:409
    __n=<optimized out>, __s=0x7fffffffe480 "5.16.10-arch1-1", __d=<optimized out>)
    at /usr/include/c++/11.2.0/bits/basic_string.h:359
    __n=<optimized out>, __s=0x7fffffffe480 "5.16.10-arch1-1", __d=<optimized out>)
    at /usr/include/c++/11.2.0/bits/basic_string.h:354
    __str=..., this=0x7ffff7fbd2a0 <emper::lib::LinuxVersion::globalVersion[abi:cxx11]>)
    at /usr/include/c++/11.2.0/bits/basic_string.h:739
    at ../emper/lib/LinuxVersion.cpp:46
    at /usr/include/c++/11.2.0/ext/new_allocator.h:79

if globalVersion's constructor was not yet called. This is the so
called "static initialization order fiasco" in C++.

While the mutex may not be strictly requires, as we do not call
LinuxVersion's non-argument constructor nor getUtsRelease()
concurrently, it can not hurt to have one.
---
 emper/lib/LinuxVersion.cpp | 32 ++++++++++++++++++++++----------
 emper/lib/LinuxVersion.hpp |  4 ++--
 2 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/emper/lib/LinuxVersion.cpp b/emper/lib/LinuxVersion.cpp
index 614407ce..4fb3c582 100644
--- a/emper/lib/LinuxVersion.cpp
+++ b/emper/lib/LinuxVersion.cpp
@@ -1,11 +1,12 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright © 2021 Florian Fischer
+// Copyright © 2021-2022 Florian Fischer, Florian Schmaus
 #include "lib/LinuxVersion.hpp"
 
 #include <sys/utsname.h>
 
 #include <cerrno>
 #include <cstdlib>
+#include <mutex>
 
 #include "Common.hpp"
 
@@ -35,19 +36,30 @@ static auto checked_strtol(const std::string& s) -> long {
 
 namespace emper::lib {
 
-std::string LinuxVersion::globalVersion;
-
-LinuxVersion::LinuxVersion() {
-	if (globalVersion.empty()) {
-		struct utsname buf;
-		if (uname(&buf)) {
-			DIE_MSG_ERRNO("Failed to invoke uname()");
+auto LinuxVersion::getUtsRelease() -> const std::string& {
+	// Use "construct (members) on first use idiom" to prevent the "static initialization order
+	// fiasco/problem".
+	static std::string utsRelease;
+	// This mutex will become necessary once we call getUtsRelease(),
+	// and by implication LinuxVersion's constructor, concurrently. So
+	// it can not hurt to have it right now.
+	static std::mutex utsReleaseMutex;
+
+	{
+		const std::lock_guard<std::mutex> lock(utsReleaseMutex);
+		if (utsRelease.empty()) {
+			struct utsname buf;
+			if (uname(&buf)) {
+				DIE_MSG_ERRNO("Failed to invoke uname()");
+			}
+			utsRelease = std::string(buf.release);
 		}
-		globalVersion = std::string(buf.release);
 	}
-	version = globalVersion;
+	return utsRelease;
 }
 
+LinuxVersion::LinuxVersion() { version = getUtsRelease(); }
+
 // Returns 1 if s is smaller, -1 if this is smaller, 0 if equal
 auto LinuxVersion::compare(const std::string& s) -> int {
 	size_t dot_pos, dot_pos2;
diff --git a/emper/lib/LinuxVersion.hpp b/emper/lib/LinuxVersion.hpp
index 2d3b5713..42eb611e 100644
--- a/emper/lib/LinuxVersion.hpp
+++ b/emper/lib/LinuxVersion.hpp
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright © 2021 Florian Fischer
+// Copyright © 2021-2022 Florian Fischer, Florian Schmaus
 #pragma once
 
 #include <string>
@@ -9,7 +9,7 @@ class TestLinuxVersion;
 namespace emper::lib {
 class LinuxVersion {
 	friend class ::TestLinuxVersion;
-	static std::string globalVersion;
+	static auto getUtsRelease() -> const std::string&;
 	std::string version;
 
 	auto compare(const std::string& s) -> int;
-- 
GitLab