/* -*-c++-*- */ /* osgEarth - Geospatial SDK for OpenSceneGraph * Copyright 2020 Pelican Mapping * http://osgearth.org * * osgEarth is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ #pragma once #include #include #include // bring in weejobs in the jobs namespace #define WEEJOBS_EXPORT OSGEARTH_EXPORT #include namespace osgEarth { /** * Pure interface for an object that can be canceled. */ using Cancelable = WEEJOBS_NAMESPACE::cancelable; //! Sets the name of the curent thread extern OSGEARTH_EXPORT void setThreadName(const std::string& name); namespace Threading { // backwards compatibility typedefs. using Mutex = std::mutex; using RecursiveMutex = std::recursive_mutex; using ScopedMutexLock = std::lock_guard; using ScopedRecursiveMutexLock = std::lock_guard; template using Future = typename WEEJOBS_NAMESPACE::future; using Event = jobs::detail::event; using ReadWriteMutex = std::shared_timed_mutex; // C++14 using ScopedReadLock = std::shared_lock; using ScopedWriteLock = std::unique_lock; /** * Mutex that locks on a per-object basis */ template class Gate { public: Gate() = default; //! Lock key's gate inline void lock(const T& key) { std::unique_lock lock(_m); for (;;) { if (emplace(key)) return; _block.wait(lock); } } //! Unlock the key's gate inline void unlock(const T& key) { std::unique_lock lock(_m); // only remove first occurance since recursive locks mean the same // key/threadid pair can appear multiple times for (unsigned i = 0; i < _keys.size(); ++i) { if (_keys[i].first == key) { std::swap(_keys[i], _keys.back()); _keys.resize(_keys.size() - 1); break; } } _block.notify_all(); } private: std::mutex _m; std::condition_variable_any _block; using entry_t = std::pair; std::vector _keys; // return true is lock is granted. inline bool emplace(const T& key) { for (auto& k : _keys) { if (k.first == key && k.second != std::this_thread::get_id()) { // fail if the key is already locked by another thread return false; } } // nb: same key can appear multiple times for the same thread _keys.push_back(std::make_pair(key, std::this_thread::get_id())); return true; } }; //! Gate the locks for the duration of this object's scope template struct ScopedGate { public: //! Lock a gate based on key "key" ScopedGate(Gate& gate, const T& key) : _gate(gate), _key(key), _active(true) { _gate.lock(key); } //! Lock a gate based on key "key" IFF the predicate is true, //! else it's a nop. ScopedGate(Gate& gate, const T& key, bool pred) : _gate(gate), _key(key), _active(pred) { if (_active) _gate.lock(_key); } //! End-of-scope destructor unlocks the gate ~ScopedGate() { if (_active) _gate.unlock(_key); } private: Gate& _gate; T _key; bool _active; }; /** * Simple convenience construct to make another type "lockable" * as long as it has a default constructor */ template struct Mutexed : public T { Mutexed() : T() { } void lock() { _lockable_mutex.lock(); } void lock() const { _lockable_mutex.lock(); } void unlock() { _lockable_mutex.unlock(); } void unlock() const { _lockable_mutex.unlock(); } void lock(std::function func) { lock(); func(); unlock(); } void scoped_lock(std::function func) { lock(); func(); unlock(); } MUTEX& mutex() const { return _lockable_mutex; } T& operator = (const T& rhs) { return T::operator=(rhs); } T& operator = (const T&& rhs) { return T::operator=(rhs); } private: mutable MUTEX _lockable_mutex; }; template struct scoped_lock_if_base { scoped_lock_if_base(basic_lockable& lock, bool condition) : _lock(lock), _condition(condition) { if (_condition) _lock.lock(); } ~scoped_lock_if_base() { if (_condition) _lock.unlock(); } basic_lockable& _lock; bool _condition; }; using scoped_lock_if = scoped_lock_if_base; } }