diff --git a/src/env_base.cpp b/src/env_base.cpp index 826565b..453904f 100644 --- a/src/env_base.cpp +++ b/src/env_base.cpp @@ -17,8 +17,56 @@ #include "env_base.hpp" #include "string_view_cat.hpp" +#include "safe_print.hpp" +#include +#include namespace duck { +namespace { + template ()())> + R call_noexcept (F func, std::string_view err_location) noexcept { + typedef R return_type; + + try { + return func(); + } + catch (const std::runtime_error& err) { + safe_print_error("Unexpected exception in "); + safe_print_error(err_location); + safe_print_error(": "); + safe_print_error_ln(err.what()); + } + catch (...) { + safe_print_error("Unknown exception in "); + safe_print_error_ln(err_location); + } + + if constexpr (not std::is_same::value) + return return_type{}; + } +} //unnamed namespace + +class StdMutex : public EnvBase::Mutex { +public: + StdMutex (std::mutex& m) noexcept : + m_mutex(m) + { + } + virtual void lock() override { m_mutex.lock(); } + virtual void try_lock() override {m_mutex.try_lock(); } + virtual void unlock() noexcept override {m_mutex.unlock(); } +private: + std::mutex& m_mutex; +}; + +class EmptyMutex : public EnvBase::Mutex { +public: + EmptyMutex() noexcept = default; + virtual void lock() override { } + virtual void try_lock() override { } + virtual void unlock() noexcept override { } +}; + bool EnvBase::empty() const noexcept { return 0 == this->size(); } @@ -38,4 +86,26 @@ detail::EnvVarProxy EnvBase::operator[] (std::string&& name) noexce detail::EnvVarProxy EnvBase::operator[] (const std::string& name) noexcept { return {to_string(name), this}; } + +void EnvBase::set (std::string_view name, std::string_view value) { + StdMutex m(this->mutex()); + this->set_implem(name, value, m); +} + +void EnvBase::set_nolock (EnvBase& obj, std::string_view name, std::string_view value) { + EmptyMutex m; + obj.set_implem(name, value, m); +} + +void EnvBase::clear() noexcept { + StdMutex m(this->mutex()); + call_noexcept([this,&m](){this->clear_implem(m);}, to_string_view(__func__)); + return; +} + +void EnvBase::clear_nolock (EnvBase& obj) noexcept { + EmptyMutex m; + call_noexcept([&](){obj.clear_implem(m);}, to_string_view(__func__)); + return; +} } //namespace duck diff --git a/src/env_base.hpp b/src/env_base.hpp index b5bc0e9..eeeeba0 100644 --- a/src/env_base.hpp +++ b/src/env_base.hpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace duck { class EnvBase; @@ -47,6 +48,13 @@ namespace duck { typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; + struct Mutex { + virtual ~Mutex() noexcept = default; + virtual void lock() = 0; + virtual void try_lock() = 0; + virtual void unlock() noexcept = 0; + }; + EnvBase() = default; EnvBase (const EnvBase&) = delete; EnvBase (EnvBase&&) = delete; @@ -59,12 +67,22 @@ namespace duck { virtual std::optional get (std::string_view name) const noexcept = 0; virtual std::string_view get (std::string_view name, std::string_view def) const noexcept = 0; - virtual void set (std::string_view name, std::string_view value) = 0; + void set (std::string_view name, std::string_view value); virtual void unset (std::string_view name) noexcept = 0; virtual bool is_set (std::string_view name) const noexcept = 0; virtual size_type size() const noexcept = 0; virtual bool empty() const noexcept; - virtual void clear() noexcept = 0; + void clear() noexcept; + + virtual std::mutex& mutex() noexcept = 0; + + protected: + virtual void set_all_into (EnvBase& other) const = 0; + static void set_all_into (const EnvBase& obj, EnvBase& other) { obj.set_all_into(other); } + virtual void clear_implem(Mutex& m) = 0; + static void clear_nolock (EnvBase& obj) noexcept; + virtual void set_implem (std::string_view name, std::string_view value, Mutex& m) = 0; + static void set_nolock (EnvBase& obj, std::string_view name, std::string_view value); }; namespace detail { diff --git a/src/env_fake.cpp b/src/env_fake.cpp index e3574f1..05b85e8 100644 --- a/src/env_fake.cpp +++ b/src/env_fake.cpp @@ -87,7 +87,7 @@ std::string_view EnvFake::get (std::string_view name, std::string_view def) cons return maybe_ret; } -void EnvFake::set (std::string_view name, std::string_view value) { +void EnvFake::set_implem (std::string_view name, std::string_view value, Mutex& m) { assert(not name.empty()); if (name.empty()) return; @@ -102,10 +102,13 @@ void EnvFake::set (std::string_view name, std::string_view value) { const std::string_view new_value(buff.get() + name.size(), value.size()); assert(new_value == value); - m_map_strong_cpy.insert_or_assign(std::string_view(new_name), std::move(buff)); - m_map.insert_or_assign(std::string_view(new_name), std::string_view(new_value)); - assert(m_map.at(name) == value); - assert(m_map.find(name)->first.data() == new_name.data()); + { + std::lock_guard lock(m); + m_map_strong_cpy.insert_or_assign(std::string_view(new_name), std::move(buff)); + m_map.insert_or_assign(std::string_view(new_name), std::string_view(new_value)); + assert(m_map.at(name) == value); + assert(m_map.find(name)->first.data() == new_name.data()); + } } void EnvFake::unset (std::string_view name) noexcept { @@ -121,7 +124,8 @@ auto EnvFake::size() const noexcept -> size_type { return m_map.size(); } -void EnvFake::clear() noexcept { +void EnvFake::clear_implem(Mutex& m) { + std::lock_guard lock(m); m_map.clear(); m_map_strong_cpy.clear(); } @@ -141,4 +145,22 @@ auto EnvFake::end() const -> const_iterator { auto EnvFake::cend() const -> const_iterator { return m_map.cend(); } + +std::mutex& EnvFake::mutex() noexcept { + return m_mutex; +} + +void EnvFake::set_all_into (EnvBase& other) const { + std::scoped_lock lock(m_mutex, other.mutex()); + + EnvBase::clear_nolock(other); + for (const auto& keyval : *this) { + EnvBase::set_nolock(other, keyval.first, keyval.second); + } +} + +EnvFake& EnvFake::operator= (const EnvBase& other) { + EnvBase::set_all_into(other, *this); + return *this; +} } //namespace duck diff --git a/src/env_fake.hpp b/src/env_fake.hpp index 40eb56d..236004d 100644 --- a/src/env_fake.hpp +++ b/src/env_fake.hpp @@ -20,6 +20,7 @@ #include "env_base.hpp" #include #include +#include namespace duck { class EnvFake : public EnvBase { @@ -30,21 +31,29 @@ namespace duck { explicit EnvFake (bool starts_empty=false); ~EnvFake() noexcept = default; + EnvFake& operator= (const EnvBase& other); + virtual std::optional get (std::string_view name) const noexcept override; virtual std::string_view get (std::string_view name, std::string_view def) const noexcept override; - virtual void set (std::string_view name, std::string_view value) override; virtual void unset (std::string_view name) noexcept override; virtual bool is_set (std::string_view name) const noexcept override; virtual size_type size() const noexcept override; - virtual void clear() noexcept override; const_iterator begin() const; const_iterator cbegin() const; const_iterator end() const; const_iterator cend() const; + virtual std::mutex& mutex() noexcept override; + + protected: + virtual void set_all_into (EnvBase& other) const override; + virtual void clear_implem(Mutex& m) override; + virtual void set_implem (std::string_view name, std::string_view value, Mutex& m) override; + private: std::unordered_map> m_map_strong_cpy; map_type m_map; + mutable std::mutex m_mutex; }; } //namespace duck diff --git a/src/env_real.cpp b/src/env_real.cpp index 094cfb2..1bfcfdd 100644 --- a/src/env_real.cpp +++ b/src/env_real.cpp @@ -40,10 +40,10 @@ namespace { const constexpr std::size_t g_name_buff_len = 128; - PointerMap& pointer_map(); + PointerMap& pointer_map() noexcept; class PointerMap { - friend PointerMap& pointer_map(); + friend PointerMap& pointer_map() noexcept; public: PointerMap (const PointerMap&) = delete; PointerMap (PointerMap&&) = delete; @@ -70,7 +70,7 @@ namespace { static void value() noexcept {} }; - PointerMap& pointer_map() { + PointerMap& pointer_map() noexcept { static PointerMap pm; return pm; } @@ -204,6 +204,11 @@ EnvReal::EnvReal() : { } +EnvReal& EnvReal::operator= (const EnvBase& other) { + EnvBase::set_all_into(other, *this); + return *this; +} + std::optional EnvReal::get (std::string_view name) const noexcept { const char* const ret = this->raw_fetch_env(name); if (ret) @@ -220,7 +225,7 @@ std::string_view EnvReal::get (std::string_view name, std::string_view def) cons return def; } -void EnvReal::set (std::string_view name, std::string_view value) { +void EnvReal::set_implem (std::string_view name, std::string_view value, Mutex& m) { assert(not name.empty()); if (name.empty()) return; @@ -236,7 +241,7 @@ void EnvReal::set (std::string_view name, std::string_view value) { { auto& pm = pointer_map(); - std::lock_guard lock(pm.env_mutex()); + std::lock_guard lock(m); putenv(pm.register_pointer(std::move(new_var))); } } @@ -272,9 +277,9 @@ auto EnvReal::size() const noexcept -> size_type { return z; } -void EnvReal::clear() noexcept { +void EnvReal::clear_implem(Mutex& m) { auto& pm = pointer_map(); - std::lock_guard lock(pm.env_mutex()); + std::lock_guard lock(m); const int r = clearenv(); if (not r) { pm.clear(); @@ -315,4 +320,18 @@ auto EnvReal::cend() const -> const_iterator { assert(*(::environ + this->size()) == nullptr); return {::environ + this->size()}; } + +std::mutex& EnvReal::mutex() noexcept { + return pointer_map().env_mutex(); +} + +void EnvReal::set_all_into (EnvBase& other) const { + auto& pm = pointer_map(); + std::scoped_lock lock(pm.env_mutex(), other.mutex()); + + EnvBase::clear_nolock(other); + for (const auto& keyval : *this) { + EnvBase::set_nolock(other, keyval.first, keyval.second); + } +} } //namespace duck diff --git a/src/env_real.hpp b/src/env_real.hpp index e11c3dd..72ff5d3 100644 --- a/src/env_real.hpp +++ b/src/env_real.hpp @@ -52,19 +52,26 @@ namespace duck { EnvReal(); virtual ~EnvReal() noexcept = default; + EnvReal& operator= (const EnvBase& other); + virtual std::optional get (std::string_view name) const noexcept override; virtual std::string_view get (std::string_view name, std::string_view def) const noexcept override; - virtual void set (std::string_view name, std::string_view value) override; virtual void unset (std::string_view name) noexcept override; virtual bool is_set (std::string_view name) const noexcept override; virtual size_type size() const noexcept override; - virtual void clear() noexcept override; const_iterator begin() const; const_iterator cbegin() const; const_iterator end() const; const_iterator cend() const; + virtual std::mutex& mutex() noexcept override; + + protected: + virtual void set_all_into (EnvBase& other) const override; + virtual void clear_implem(Mutex& m) override; + virtual void set_implem (std::string_view name, std::string_view value, Mutex& m) override; + private: const char* raw_fetch_env (std::string_view name) const noexcept; diff --git a/src/safe_print.cpp b/src/safe_print.cpp index 685f8a1..3e7d4a3 100644 --- a/src/safe_print.cpp +++ b/src/safe_print.cpp @@ -21,13 +21,29 @@ namespace duck { void safe_print (std::string_view msg) noexcept { try { - std::cout << msg << std::endl; + std::cout << msg; } catch (...) { } } void safe_print_error (std::string_view msg) noexcept { + try { + std::cerr << msg; + } + catch (...) { + } +} + +void safe_print_ln (std::string_view msg) noexcept { + try { + std::cout << msg << std::endl; + } + catch (...) { + } +} + +void safe_print_error_ln (std::string_view msg) noexcept { try { std::cerr << msg << std::endl; } diff --git a/src/safe_print.hpp b/src/safe_print.hpp index 1d1d78b..49cc7b5 100644 --- a/src/safe_print.hpp +++ b/src/safe_print.hpp @@ -22,4 +22,7 @@ namespace duck { void safe_print (std::string_view msg) noexcept; void safe_print_error (std::string_view msg) noexcept; + + void safe_print_ln (std::string_view msg) noexcept; + void safe_print_error_ln (std::string_view msg) noexcept; } //namespace duck diff --git a/src/string_view_cat.hpp b/src/string_view_cat.hpp index 3ca1b96..ce17aa7 100644 --- a/src/string_view_cat.hpp +++ b/src/string_view_cat.hpp @@ -37,6 +37,11 @@ namespace duck { return (str ? std::string_view{str} : std::string_view{""}); } + template + inline std::string_view to_string_view (const char (&str)[S]) noexcept { + return std::string_view(str, S); + } + inline std::string operator+ (std::string_view a, std::string_view b) { std::string ret(to_string(a)); ret += b;