// This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2017, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #ifndef CHAISCRIPT_THREADING_HPP_ #define CHAISCRIPT_THREADING_HPP_ #include #ifndef CHAISCRIPT_NO_THREADS #include #include #else #ifndef CHAISCRIPT_NO_THREADS_WARNING #pragma message ("ChaiScript is compiling without thread safety.") #endif #endif #include "chaiscript_defines.hpp" /// \file /// /// This file contains code necessary for thread support in ChaiScript. /// If the compiler definition CHAISCRIPT_NO_THREADS is defined then thread safety /// is disabled in ChaiScript. This has the result that some code is faster, because mutex locks are not required. /// It also has the side effect that the chaiscript::ChaiScript object may not be accessed from more than /// one thread simultaneously. namespace chaiscript { namespace detail { /// If threading is enabled, then this namespace contains std thread classes. /// If threading is not enabled, then stubbed in wrappers that do nothing are provided. /// This allows us to avoid \#ifdef code in the sections that need thread safety. namespace threading { #ifndef CHAISCRIPT_NO_THREADS template using unique_lock = std::unique_lock; template using shared_lock = std::unique_lock; template using lock_guard = std::lock_guard; using shared_mutex = std::mutex; using std::mutex; using std::recursive_mutex; /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from. template class Thread_Storage { public: Thread_Storage() = default; Thread_Storage(const Thread_Storage &) = delete; Thread_Storage(Thread_Storage &&) = delete; Thread_Storage &operator=(const Thread_Storage &) = delete; Thread_Storage &operator=(Thread_Storage &&) = delete; ~Thread_Storage() { if (!destroyed) { t().erase(this); } } inline const T *operator->() const { return &(t()[const_cast(this)]); } inline const T &operator*() const { return t()[const_cast(this)]; } inline T *operator->() { return &(t()[this]); } inline T &operator*() { return t()[this]; } private: struct Map_Holder { std::unordered_map *, T> map; Map_Holder() = default; Map_Holder(const Map_Holder &) = delete; Map_Holder(Map_Holder &&) = delete; Map_Holder& operator=(Map_Holder &&) = delete; Map_Holder& operator=(const Map_Holder &&) = delete; ~Map_Holder() { // here is the theory: // * If the Map_Holder is destroyed before the Thread_Storage, a flag will get set // * If destroyed after the Thread_Storage, the * will have been removed from `map` and nothing will happen for(auto &elem : map) { elem.first->destroyed = true; } } }; static std::unordered_map *, T> &t() { thread_local Map_Holder my_map; return my_map.map; } bool destroyed{false}; }; #else // threading disabled template class unique_lock { public: explicit unique_lock(T &) {} void lock() {} void unlock() {} }; template class shared_lock { public: explicit shared_lock(T &) {} void lock() {} void unlock() {} }; template class lock_guard { public: explicit lock_guard(T &) {} }; class shared_mutex { }; class recursive_mutex {}; template class Thread_Storage { public: explicit Thread_Storage(void *) { } inline T *operator->() const { return &obj; } inline T &operator*() const { return obj; } private: mutable T obj; }; #endif } } } #endif