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