185 lines
6.2 KiB
Plaintext
185 lines
6.2 KiB
Plaintext
|
/* -*-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 <http://www.gnu.org/licenses/>
|
||
|
*/
|
||
|
#pragma once
|
||
|
#include <osgEarth/Export>
|
||
|
#include <vector>
|
||
|
#include <shared_mutex>
|
||
|
|
||
|
// bring in weejobs in the jobs namespace
|
||
|
#define WEEJOBS_EXPORT OSGEARTH_EXPORT
|
||
|
#include <osgEarth/weejobs.h>
|
||
|
|
||
|
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<std::mutex>;
|
||
|
using ScopedRecursiveMutexLock = std::lock_guard<RecursiveMutex>;
|
||
|
|
||
|
template<typename T> using Future = typename WEEJOBS_NAMESPACE::future<T>;
|
||
|
|
||
|
using Event = jobs::detail::event;
|
||
|
|
||
|
using ReadWriteMutex = std::shared_timed_mutex; // C++14
|
||
|
using ScopedReadLock = std::shared_lock<ReadWriteMutex>;
|
||
|
using ScopedWriteLock = std::unique_lock<ReadWriteMutex>;
|
||
|
|
||
|
/**
|
||
|
* Mutex that locks on a per-object basis
|
||
|
*/
|
||
|
template<typename T>
|
||
|
class Gate
|
||
|
{
|
||
|
public:
|
||
|
Gate() = default;
|
||
|
|
||
|
//! Lock key's gate
|
||
|
inline void lock(const T& key)
|
||
|
{
|
||
|
std::unique_lock<std::mutex> lock(_m);
|
||
|
for (;;) {
|
||
|
if (emplace(key))
|
||
|
return;
|
||
|
_block.wait(lock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! Unlock the key's gate
|
||
|
inline void unlock(const T& key)
|
||
|
{
|
||
|
std::unique_lock<std::mutex> 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<T, std::thread::id>;
|
||
|
std::vector<entry_t> _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<typename T>
|
||
|
struct ScopedGate
|
||
|
{
|
||
|
public:
|
||
|
//! Lock a gate based on key "key"
|
||
|
ScopedGate(Gate<T>& 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<T>& 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<T>& _gate;
|
||
|
T _key;
|
||
|
bool _active;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Simple convenience construct to make another type "lockable"
|
||
|
* as long as it has a default constructor
|
||
|
*/
|
||
|
template<typename T, typename MUTEX = std::mutex>
|
||
|
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<void()> func) { lock(); func(); unlock(); }
|
||
|
void scoped_lock(std::function<void()> 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<typename basic_lockable = std::mutex>
|
||
|
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<std::mutex>;
|
||
|
}
|
||
|
}
|