mirror of https://github.com/axmolengine/axmol.git
264 lines
7.2 KiB
C++
264 lines
7.2 KiB
C++
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// A multi-platform support c++11 library with focus on asynchronous socket I/O for any
|
|
// client application.
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
The MIT License (MIT)
|
|
|
|
Copyright (c) 2012-2021 HALX99
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#ifndef YASIO__SHARED_MUTEX_HPP
|
|
#define YASIO__SHARED_MUTEX_HPP
|
|
|
|
#include "yasio/compiler/feature_test.hpp"
|
|
|
|
/// The shared_mutex workaround on c++11
|
|
#if YASIO__HAS_CXX17 && !defined(__APPLE__)
|
|
# include <shared_mutex>
|
|
#else
|
|
# include <system_error>
|
|
# include <mutex>
|
|
# include <condition_variable>
|
|
# define yaso__throw_error(e) YASIO__THROW0(std::system_error(std::make_error_code(e), ""))
|
|
namespace cxx17
|
|
{
|
|
// CLASS TEMPLATE shared_mutex, copy from: https://github.com/boostorg/thread/blob/develop/include/boost/thread/v2/shared_mutex.hpp
|
|
class shared_mutex {
|
|
typedef std::mutex mutex_t;
|
|
typedef std::condition_variable cond_t;
|
|
typedef unsigned count_t;
|
|
|
|
mutex_t mut_;
|
|
cond_t gate1_;
|
|
// the gate2_ condition variable is only used by functions that
|
|
// have taken write_entered_ but are waiting for no_readers()
|
|
cond_t gate2_;
|
|
count_t state_;
|
|
|
|
static const count_t write_entered_ = 1U << (sizeof(count_t) * CHAR_BIT - 1);
|
|
static const count_t n_readers_ = ~write_entered_;
|
|
|
|
bool no_writer() const { return (state_ & write_entered_) == 0; }
|
|
|
|
bool one_writer() const { return (state_ & write_entered_) != 0; }
|
|
|
|
bool no_writer_no_readers() const
|
|
{
|
|
// return (state_ & write_entered_) == 0 &&
|
|
// (state_ & n_readers_) == 0;
|
|
return state_ == 0;
|
|
}
|
|
|
|
bool no_writer_no_max_readers() const { return (state_ & write_entered_) == 0 && (state_ & n_readers_) != n_readers_; }
|
|
|
|
bool no_readers() const { return (state_ & n_readers_) == 0; }
|
|
|
|
bool one_or_more_readers() const { return (state_ & n_readers_) > 0; }
|
|
|
|
shared_mutex(shared_mutex const&) = delete;
|
|
shared_mutex& operator=(shared_mutex const&) = delete;
|
|
|
|
public:
|
|
shared_mutex() : state_(0) {}
|
|
~shared_mutex() { std::lock_guard<mutex_t> _(mut_); }
|
|
|
|
// Exclusive ownership
|
|
|
|
void lock()
|
|
{
|
|
std::unique_lock<mutex_t> lk(mut_);
|
|
gate1_.wait(lk, std::bind(&shared_mutex::no_writer, std::ref(*this)));
|
|
state_ |= write_entered_;
|
|
gate2_.wait(lk, std::bind(&shared_mutex::no_readers, std::ref(*this)));
|
|
}
|
|
bool try_lock()
|
|
{
|
|
std::unique_lock<mutex_t> lk(mut_);
|
|
if (!no_writer_no_readers())
|
|
{
|
|
return false;
|
|
}
|
|
state_ = write_entered_;
|
|
return true;
|
|
}
|
|
void unlock()
|
|
{
|
|
std::lock_guard<mutex_t> _(mut_);
|
|
assert(one_writer());
|
|
assert(no_readers());
|
|
state_ = 0;
|
|
// notify all since multiple *lock_shared*() calls may be able
|
|
// to proceed in response to this notification
|
|
gate1_.notify_all();
|
|
}
|
|
|
|
// Shared ownership
|
|
|
|
void lock_shared()
|
|
{
|
|
std::unique_lock<mutex_t> lk(mut_);
|
|
gate1_.wait(lk, std::bind(&shared_mutex::no_writer_no_max_readers, std::ref(*this)));
|
|
count_t num_readers = (state_ & n_readers_) + 1;
|
|
state_ &= ~n_readers_;
|
|
state_ |= num_readers;
|
|
}
|
|
bool try_lock_shared()
|
|
{
|
|
std::unique_lock<mutex_t> lk(mut_);
|
|
if (!no_writer_no_max_readers())
|
|
{
|
|
return false;
|
|
}
|
|
count_t num_readers = (state_ & n_readers_) + 1;
|
|
state_ &= ~n_readers_;
|
|
state_ |= num_readers;
|
|
return true;
|
|
}
|
|
void unlock_shared()
|
|
{
|
|
std::lock_guard<mutex_t> _(mut_);
|
|
assert(one_or_more_readers());
|
|
count_t num_readers = (state_ & n_readers_) - 1;
|
|
state_ &= ~n_readers_;
|
|
state_ |= num_readers;
|
|
if (no_writer())
|
|
{
|
|
if (num_readers == n_readers_ - 1)
|
|
gate1_.notify_one();
|
|
}
|
|
else
|
|
{
|
|
if (num_readers == 0)
|
|
gate2_.notify_one();
|
|
}
|
|
}
|
|
};
|
|
|
|
// CLASS TEMPLATE shared_lock
|
|
template <class _Mutex>
|
|
class shared_lock {
|
|
public:
|
|
using mutex_type = _Mutex;
|
|
|
|
shared_lock() : _Pmtx(nullptr), _Owns(false) {}
|
|
|
|
explicit shared_lock(mutex_type& _Mtx) : _Pmtx(YASIO__STD addressof(_Mtx)), _Owns(true)
|
|
{ // construct with mutex and lock shared
|
|
_Mtx.lock_shared();
|
|
}
|
|
|
|
explicit shared_lock(mutex_type& _Mtx, YASIO__STD defer_lock_t) : _Pmtx(YASIO__STD addressof(_Mtx)), _Owns(false) {} // // construct with unlocked mutex
|
|
|
|
explicit shared_lock(mutex_type& _Mtx, YASIO__STD try_to_lock_t)
|
|
: _Pmtx(YASIO__STD addressof(_Mtx)), _Owns(_Mtx.try_lock_shared()) {} // construct with mutex and try to lock shared
|
|
|
|
explicit shared_lock(mutex_type& _Mtx, YASIO__STD adopt_lock_t) : _Pmtx(YASIO__STD addressof(_Mtx)), _Owns(true) {} // construct with mutex and adopt owership
|
|
|
|
~shared_lock()
|
|
{
|
|
if (_Owns)
|
|
_Pmtx->unlock_shared();
|
|
}
|
|
|
|
shared_lock(shared_lock&& _Other) : _Pmtx(_Other._Pmtx), _Owns(_Other._Owns)
|
|
{
|
|
_Other._Pmtx = nullptr;
|
|
_Other._Owns = false;
|
|
}
|
|
|
|
shared_lock& operator=(shared_lock&& _Right)
|
|
{
|
|
if (_Owns)
|
|
_Pmtx->unlock_shared();
|
|
|
|
_Pmtx = _Right._Pmtx;
|
|
_Owns = _Right._Owns;
|
|
_Right._Pmtx = nullptr;
|
|
_Right._Owns = false;
|
|
return *this;
|
|
}
|
|
|
|
shared_lock(const shared_lock&) = delete;
|
|
shared_lock& operator=(const shared_lock&) = delete;
|
|
|
|
void lock()
|
|
{ // lock the mutex
|
|
_Validate();
|
|
_Pmtx->lock_shared();
|
|
_Owns = true;
|
|
}
|
|
|
|
bool try_lock()
|
|
{ // try to lock the mutex
|
|
_Validate();
|
|
_Owns = _Pmtx->try_lock_shared();
|
|
return _Owns;
|
|
}
|
|
|
|
void unlock()
|
|
{ // try to unlock the mutex
|
|
if (!_Pmtx || !_Owns)
|
|
yaso__throw_error(std::errc::operation_not_permitted);
|
|
|
|
_Pmtx->unlock_shared();
|
|
_Owns = false;
|
|
}
|
|
|
|
// MUTATE
|
|
void swap(shared_lock& _Right)
|
|
{
|
|
YASIO__STD swap(_Pmtx, _Right._Pmtx);
|
|
YASIO__STD swap(_Owns, _Right._Owns);
|
|
}
|
|
|
|
mutex_type* release()
|
|
{
|
|
_Mutex* _Res = _Pmtx;
|
|
_Pmtx = nullptr;
|
|
_Owns = false;
|
|
return _Res;
|
|
}
|
|
|
|
// OBSERVE
|
|
bool owns_lock() const { return _Owns; }
|
|
|
|
explicit operator bool() const { return _Owns; }
|
|
|
|
mutex_type* mutex() const { return _Pmtx; }
|
|
|
|
private:
|
|
_Mutex* _Pmtx;
|
|
bool _Owns;
|
|
|
|
void _Validate() const
|
|
{ // check if the mutex can be locked
|
|
if (!_Pmtx)
|
|
yaso__throw_error(std::errc::operation_not_permitted);
|
|
|
|
if (_Owns)
|
|
yaso__throw_error(std::errc::resource_deadlock_would_occur);
|
|
}
|
|
};
|
|
} // namespace cxx17
|
|
#endif
|
|
|
|
#endif
|