/* -*-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;
}
}