#pragma once | #pragma once | ||||
#define LIB_RESUMEF_VERSION 20803 // 2.8.3 | |||||
#define LIB_RESUMEF_VERSION 20900 // 2.9.0 | |||||
#if defined(RESUMEF_MODULE_EXPORT) | #if defined(RESUMEF_MODULE_EXPORT) | ||||
#define RESUMEF_NS export namespace resumef | #define RESUMEF_NS export namespace resumef |
inline namespace mutex_v2 | inline namespace mutex_v2 | ||||
{ | { | ||||
struct [[nodiscard]] scoped_lock_mutex_t; | struct [[nodiscard]] scoped_lock_mutex_t; | ||||
struct [[nodiscard]] scoped_unlock_range_t; | |||||
//支持递归的锁 | //支持递归的锁 | ||||
struct mutex_t | struct mutex_t | ||||
bool try_lock_until(const std::chrono::time_point<_Rep, _Period>& tp, void* unique_address); | bool try_lock_until(const std::chrono::time_point<_Rep, _Period>& tp, void* unique_address); | ||||
void unlock(void* unique_address) const; | void unlock(void* unique_address) const; | ||||
struct _MutexAwaitAssembleT; | |||||
template<class... _Mtxs | |||||
, typename = std::enable_if_t<std::conjunction_v<std::is_same<std::remove_cvref_t<_Mtxs>, mutex_t>...>> | |||||
> | |||||
static future_t<scoped_unlock_range_t> lock(_Mtxs&... mtxs); | |||||
template<class... _Mtxs | |||||
, typename = std::enable_if_t<std::conjunction_v<std::is_same<std::remove_cvref_t<_Mtxs>, mutex_t>...>> | |||||
> | |||||
static scoped_unlock_range_t lock(void* unique_address, _Mtxs&... mtxs); | |||||
template<class... _Mtxs | |||||
, typename = std::enable_if_t<std::conjunction_v<std::is_same<std::remove_cvref_t<_Mtxs>, mutex_t>...>> | |||||
> | |||||
static void unlock(void* unique_address, _Mtxs&... mtxs) | |||||
{ | |||||
unlock_address(unique_address, mtxs...); | |||||
} | |||||
mutex_t(const mutex_t&) = default; | mutex_t(const mutex_t&) = default; | ||||
mutex_t(mutex_t&&) = default; | mutex_t(mutex_t&&) = default; | ||||
mutex_t& operator = (const mutex_t&) = default; | mutex_t& operator = (const mutex_t&) = default; | ||||
private: | private: | ||||
friend struct scoped_lock_mutex_t; | friend struct scoped_lock_mutex_t; | ||||
mutex_impl_ptr _mutex; | mutex_impl_ptr _mutex; | ||||
template<class... _Mtxs | |||||
, typename = std::enable_if_t<std::conjunction_v<std::is_same<std::remove_cvref_t<_Mtxs>, mutex_t>...>> | |||||
> | |||||
static void unlock_address(void* unique_address, mutex_t& _First, _Mtxs&... _Rest); | |||||
static void unlock_address(void*) {} | |||||
}; | }; | ||||
} | } | ||||
} | } |
mutex_v2_impl& operator=(const mutex_v2_impl&) = delete; | mutex_v2_impl& operator=(const mutex_v2_impl&) = delete; | ||||
mutex_v2_impl& operator=(mutex_v2_impl&&) = delete; | mutex_v2_impl& operator=(mutex_v2_impl&&) = delete; | ||||
}; | }; | ||||
struct _MutexAddressAssembleT | |||||
{ | |||||
private: | |||||
void* _Address; | |||||
public: | |||||
std::vector<mutex_t> _Lks; | |||||
template<class... _Mtxs> | |||||
_MutexAddressAssembleT(void* unique_address, _Mtxs&... mtxs) | |||||
: _Address(unique_address) | |||||
, _Lks({ mtxs... }) | |||||
{} | |||||
size_t size() const | |||||
{ | |||||
return _Lks.size(); | |||||
} | |||||
mutex_t& operator[](int _Idx) | |||||
{ | |||||
return _Lks[_Idx]; | |||||
} | |||||
void _Lock_ref(mutex_t& _LkN) const | |||||
{ | |||||
return _LkN.lock(_Address); | |||||
} | |||||
bool _Try_lock_ref(mutex_t& _LkN) const | |||||
{ | |||||
return _LkN.try_lock(_Address); | |||||
} | |||||
void _Unlock_ref(mutex_t& _LkN) const | |||||
{ | |||||
_LkN.unlock(_Address); | |||||
} | |||||
void _Yield() const | |||||
{ | |||||
std::this_thread::yield(); | |||||
} | |||||
void _ReturnValue() const noexcept {} | |||||
template<class U> | |||||
U _ReturnValue(U v) const noexcept | |||||
{ | |||||
return v; | |||||
} | |||||
}; | |||||
#define LOCK_ASSEMBLE_NAME(fnName) mutex_lock_await_##fnName | |||||
#define LOCK_ASSEMBLE_AWAIT(a) co_await (a) | |||||
#define LOCK_ASSEMBLE_RETURN(a) co_return (a) | |||||
#include "without_deadlock_assemble.inl" | |||||
#undef LOCK_ASSEMBLE_NAME | |||||
#undef LOCK_ASSEMBLE_AWAIT | |||||
#undef LOCK_ASSEMBLE_RETURN | |||||
} | } | ||||
inline namespace mutex_v2 | inline namespace mutex_v2 | ||||
{ | { | ||||
_mutex->unlock(unique_address); | _mutex->unlock(unique_address); | ||||
} | } | ||||
struct [[nodiscard]] scoped_unlock_range_t | |||||
{ | |||||
//此函数,应该在try_lock()获得锁后使用 | |||||
//或者在协程里,由awaiter使用 | |||||
scoped_unlock_range_t(std::vector<mutex_t>&& mtxs, void* sch) | |||||
: _mutex(std::move(mtxs)) | |||||
, _owner(sch) | |||||
{} | |||||
~scoped_unlock_range_t() | |||||
{ | |||||
if (_owner != nullptr) | |||||
{ | |||||
for(mutex_t& mtx : _mutex) | |||||
mtx.unlock(_owner); | |||||
} | |||||
} | |||||
inline void unlock() noexcept | |||||
{ | |||||
if (_owner != nullptr) | |||||
{ | |||||
for (mutex_t& mtx : _mutex) | |||||
mtx.unlock(_owner); | |||||
_owner = nullptr; | |||||
} | |||||
} | |||||
scoped_unlock_range_t(const scoped_unlock_range_t&) = delete; | |||||
scoped_unlock_range_t& operator = (const scoped_unlock_range_t&) = delete; | |||||
scoped_unlock_range_t(scoped_unlock_range_t&&) = default; | |||||
scoped_unlock_range_t& operator = (scoped_unlock_range_t&&) = default; | |||||
private: | |||||
std::vector<mutex_t> _mutex; | |||||
void* _owner; | |||||
}; | |||||
struct mutex_t::_MutexAwaitAssembleT | |||||
{ | |||||
private: | |||||
void* _Address; | |||||
public: | |||||
std::vector<mutex_t> _Lks; | |||||
template<class... _Mtxs> | |||||
_MutexAwaitAssembleT(void* unique_address, _Mtxs&... mtxs) | |||||
: _Address(unique_address) | |||||
, _Lks({ mtxs... }) | |||||
{} | |||||
size_t size() const | |||||
{ | |||||
return _Lks.size(); | |||||
} | |||||
mutex_t& operator[](int _Idx) | |||||
{ | |||||
return _Lks[_Idx]; | |||||
} | |||||
auto _Lock_ref(mutex_t& _LkN) const | |||||
{ | |||||
return _LkN.lock(); | |||||
} | |||||
auto _Try_lock_ref(mutex_t& _LkN) const | |||||
{ | |||||
return _LkN.try_lock(); | |||||
} | |||||
void _Unlock_ref(mutex_t& _LkN) const | |||||
{ | |||||
_LkN.unlock(_Address); | |||||
} | |||||
future_t<> _Yield() const | |||||
{ | |||||
for (int cnt = rand() % (1 + _Lks.size()); cnt >= 0; --cnt) | |||||
co_await ::resumef::yield(); | |||||
} | |||||
future_t<> _ReturnValue() const; | |||||
template<class U> | |||||
future_t<U> _ReturnValue(U v) const; | |||||
}; | |||||
template<class... _Mtxs, typename> | |||||
inline future_t<scoped_unlock_range_t> mutex_t::lock(_Mtxs&... mtxs) | |||||
{ | |||||
auto* root = root_state(); | |||||
_MutexAwaitAssembleT MAA(root, mtxs...); | |||||
co_await detail::mutex_lock_await_lock_impl::_Lock_range(MAA); | |||||
co_return scoped_unlock_range_t{ std::move(MAA._Lks), root }; | |||||
} | |||||
template<class... _Mtxs, typename> | |||||
inline scoped_unlock_range_t mutex_t::lock(void* unique_address, _Mtxs&... mtxs) | |||||
{ | |||||
detail::_MutexAddressAssembleT MAA(unique_address, mtxs...); | |||||
detail::scoped_lock_range_lock_impl::_Lock_range(MAA); | |||||
return scoped_unlock_range_t{ std::move(MAA._Lks), unique_address }; | |||||
} | |||||
template<class... _Mtxs, typename> | |||||
inline void mutex_t::unlock_address(void* unique_address, mutex_t& _First, _Mtxs&... _Rest) | |||||
{ | |||||
_First.unlock(unique_address); | |||||
unlock_address(unique_address, _Rest...); | |||||
} | |||||
} | } | ||||
} | } | ||||
namespace detail | namespace detail | ||||
{ | { | ||||
#if RESUMEF_ENABLE_CONCEPT | |||||
template<typename T> | |||||
concept _LockAssembleT = requires(T && v) | |||||
{ | |||||
{ v.size() }; | |||||
{ v[0] }; | |||||
{ v._Lock_ref(v[0]) } ->void; | |||||
{ v._Try_lock_ref(v[0]) } ->bool; | |||||
{ v._Unlock_ref(v[0]) } ->void; | |||||
{ v._Yield() }; | |||||
{ v._ReturnValue() }; | |||||
{ v._ReturnValue(0) }; | |||||
requires std::is_integral_v<decltype(v.size())>; | |||||
}; | |||||
#else | |||||
#define _LockAssembleT typename | |||||
#endif | |||||
template<class _Ty> | |||||
template<class _Ty, class _Cont = std::vector<_Ty>> | |||||
struct _LockVectorAssembleT | struct _LockVectorAssembleT | ||||
{ | { | ||||
private: | private: | ||||
std::vector<_Ty>& _Lks; | |||||
_Cont& _Lks; | |||||
public: | public: | ||||
_LockVectorAssembleT(std::vector<_Ty>& _LkN) | |||||
_LockVectorAssembleT(_Cont& _LkN) | |||||
: _Lks(_LkN) | : _Lks(_LkN) | ||||
{} | {} | ||||
size_t size() const | size_t size() const | ||||
{ | { | ||||
std::this_thread::yield(); | std::this_thread::yield(); | ||||
} | } | ||||
void _ReturnValue() const noexcept {} | |||||
void _ReturnValue() const; | |||||
template<class U> | template<class U> | ||||
U _ReturnValue(U v) const noexcept | |||||
{ | |||||
return v; | |||||
} | |||||
U _ReturnValue(U v) const; | |||||
}; | }; | ||||
template<class _Ty> | |||||
struct _LockVectorAssembleT<std::reference_wrapper<_Ty>> | |||||
template<class _Ty, class _Cont> | |||||
struct _LockVectorAssembleT<std::reference_wrapper<_Ty>, _Cont> | |||||
{ | { | ||||
private: | private: | ||||
std::vector<std::reference_wrapper<_Ty>>& _Lks; | |||||
_Cont& _Lks; | |||||
public: | public: | ||||
_LockVectorAssembleT(std::vector<std::reference_wrapper<_Ty>>& _LkN) | |||||
_LockVectorAssembleT(_Cont& _LkN) | |||||
: _Lks(_LkN) | : _Lks(_LkN) | ||||
{} | {} | ||||
size_t size() const | size_t size() const | ||||
{ | { | ||||
std::this_thread::yield(); | std::this_thread::yield(); | ||||
} | } | ||||
void _ReturnValue() const noexcept {} | |||||
void _ReturnValue() const; | |||||
template<class U> | template<class U> | ||||
U _ReturnValue(U v) const noexcept | |||||
{ | |||||
return v; | |||||
} | |||||
U _ReturnValue(U v) const; | |||||
}; | }; | ||||
} | |||||
} | |||||
#define LOCK_ASSEMBLE_NAME(fnName) scoped_lock_range_##fnName | |||||
#define LOCK_ASSEMBLE_AWAIT(a) (a) | #define LOCK_ASSEMBLE_AWAIT(a) (a) | ||||
#define LOCK_ASSEMBLE_RETURN(a) return (a) | #define LOCK_ASSEMBLE_RETURN(a) return (a) | ||||
#include "without_deadlock_assemble.inl" | #include "without_deadlock_assemble.inl" | ||||
#undef LOCK_ASSEMBLE_NAME | |||||
#undef LOCK_ASSEMBLE_AWAIT | #undef LOCK_ASSEMBLE_AWAIT | ||||
#undef LOCK_ASSEMBLE_RETURN | #undef LOCK_ASSEMBLE_RETURN | ||||
} | |||||
RESUMEF_NS | |||||
{ | |||||
template<class _Ty> | |||||
template<class _Ty, class _Cont = std::vector<_Ty>, class _Assemble = detail::_LockVectorAssembleT<_Ty, _Cont>> | |||||
class scoped_lock_range { // class with destructor that unlocks mutexes | class scoped_lock_range { // class with destructor that unlocks mutexes | ||||
public: | public: | ||||
explicit scoped_lock_range(std::vector<_Ty>& locks_) | |||||
: _LkN(locks_) | |||||
explicit scoped_lock_range(_Cont& locks_) | |||||
: _LkN(&locks_) | |||||
, _LA(*_LkN) | |||||
{ | |||||
detail::scoped_lock_range_lock_impl::_Lock_range(_LA); | |||||
} | |||||
explicit scoped_lock_range(_Cont& locks_, _Assemble& la_) | |||||
: _LkN(&locks_) | |||||
, _LA(la_) | |||||
{ | { | ||||
detail::_LockVectorAssembleT<_Ty> LA{ _LkN }; | |||||
detail::_Lock_range(LA); | |||||
detail::scoped_lock_range_lock_impl::_Lock_range(_LA); | |||||
} | } | ||||
explicit scoped_lock_range(std::adopt_lock_t, std::vector<_Ty>& locks_) | |||||
: _LkN(locks_) | |||||
explicit scoped_lock_range(std::adopt_lock_t, _Cont& locks_) | |||||
: _LkN(&locks_) | |||||
, _LA(*_LkN) | |||||
{ // construct but don't lock | |||||
} | |||||
explicit scoped_lock_range(std::adopt_lock_t, _Cont& locks_, _Assemble& la_) | |||||
: _LkN(&locks_) | |||||
, _LA(la_) | |||||
{ // construct but don't lock | { // construct but don't lock | ||||
} | } | ||||
~scoped_lock_range() noexcept | ~scoped_lock_range() noexcept | ||||
{ | { | ||||
detail::_LockVectorAssembleT<_Ty> LA{ _LkN }; | |||||
detail::_Unlock_locks(0, (int)_LkN.size(), LA); | |||||
if (_LkN != nullptr) | |||||
detail::scoped_lock_range_lock_impl::_Unlock_locks(0, (int)_LA.size(), _LA); | |||||
} | |||||
void unlock() | |||||
{ | |||||
if (_LkN != nullptr) | |||||
{ | |||||
_LkN = nullptr; | |||||
detail::scoped_lock_range_lock_impl::_Unlock_locks(0, (int)_LA.size(), _LA); | |||||
} | |||||
} | } | ||||
scoped_lock_range(const scoped_lock_range&) = delete; | scoped_lock_range(const scoped_lock_range&) = delete; | ||||
scoped_lock_range& operator=(const scoped_lock_range&) = delete; | scoped_lock_range& operator=(const scoped_lock_range&) = delete; | ||||
scoped_lock_range(scoped_lock_range&& _Right) | |||||
: _LkN(_Right._LkN) | |||||
, _LA(std::move(_Right._LA)) | |||||
{ | |||||
_Right._LkN = nullptr; | |||||
} | |||||
scoped_lock_range& operator=(scoped_lock_range&& _Right) | |||||
{ | |||||
if (this != &_Right) | |||||
{ | |||||
_LkN = _Right._LkN; | |||||
_Right._LkN = nullptr; | |||||
_LA = std::move(_Right._LA); | |||||
} | |||||
} | |||||
private: | private: | ||||
std::vector<_Ty>& _LkN; | |||||
_Cont* _LkN; | |||||
_Assemble _LA; | |||||
}; | }; | ||||
} | } |
#ifndef RESUMEF_ENABLE_CONCEPT | #ifndef RESUMEF_ENABLE_CONCEPT | ||||
#ifdef __cpp_lib_concepts | #ifdef __cpp_lib_concepts | ||||
#define RESUMEF_ENABLE_CONCEPT 1 | |||||
#define RESUMEF_ENABLE_CONCEPT 0 | |||||
#else | #else | ||||
#define RESUMEF_ENABLE_CONCEPT 1 | |||||
#define RESUMEF_ENABLE_CONCEPT 0 | |||||
#endif //#ifdef __cpp_lib_concepts | #endif //#ifdef __cpp_lib_concepts | ||||
#endif //#ifndef RESUMEF_ENABLE_CONCEPT | #endif //#ifndef RESUMEF_ENABLE_CONCEPT | ||||
#endif | #endif | ||||
#if RESUMEF_ENABLE_CONCEPT | |||||
template<typename T> | |||||
concept _LockAssembleT = requires(T && v) | |||||
{ | |||||
{ v.size() }; | |||||
{ v[0] }; | |||||
{ v._Lock_ref(v[0]) }; | |||||
{ v._Try_lock_ref(v[0]) }; | |||||
{ v._Unlock_ref(v[0]) } ->void; | |||||
{ v._Yield() }; | |||||
{ v._ReturnValue() }; | |||||
{ v._ReturnValue(0) }; | |||||
requires std::is_integral_v<decltype(v.size())>; | |||||
}; | |||||
#else | |||||
#define _LockAssembleT typename | |||||
#endif | |||||
} | } |
| | ||||
RESUMEF_NS | |||||
struct LOCK_ASSEMBLE_NAME(lock_impl) | |||||
{ | { | ||||
namespace detail | |||||
// FUNCTION TEMPLATE _Unlock_locks | |||||
template<_LockAssembleT _LA> | |||||
static void _Unlock_locks(int _First, int _Last, _LA& _LkN) noexcept /* terminates */ | |||||
{ | { | ||||
// FUNCTION TEMPLATE _Unlock_locks | |||||
template<_LockAssembleT _LA> | |||||
auto _Unlock_locks(int _First, int _Last, _LA& _LkN) noexcept /* terminates */ | |||||
->decltype(_LkN._ReturnValue()) | |||||
{ | |||||
for (; _First != _Last; ++_First) { | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Unlock_ref(_LkN[_First])); | |||||
} | |||||
for (; _First != _Last; ++_First) { | |||||
_LkN._Unlock_ref(_LkN[_First]); | |||||
} | } | ||||
} | |||||
// FUNCTION TEMPLATE try_lock | |||||
template<_LockAssembleT _LA> | |||||
auto _Try_lock_range(const int _First, const int _Last, _LA& _LkN) | |||||
->decltype(_LkN._ReturnValue<int>(0)) | |||||
{ | |||||
int _Next = _First; | |||||
try { | |||||
for (; _Next != _Last; ++_Next) | |||||
{ | |||||
if (!LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Next]))) | |||||
{ // try_lock failed, backout | |||||
LOCK_ASSEMBLE_AWAIT(_Unlock_locks(_First, _Next, _LkN)); | |||||
LOCK_ASSEMBLE_RETURN(_Next); | |||||
} | |||||
// FUNCTION TEMPLATE try_lock | |||||
template<_LockAssembleT _LA> | |||||
static auto _Try_lock_range(const int _First, const int _Last, _LA& _LkN) | |||||
->decltype(_LkN._ReturnValue<int>(0)) | |||||
{ | |||||
int _Next = _First; | |||||
try { | |||||
for (; _Next != _Last; ++_Next) | |||||
{ | |||||
if (!LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Next]))) | |||||
{ // try_lock failed, backout | |||||
_Unlock_locks(_First, _Next, _LkN); | |||||
LOCK_ASSEMBLE_RETURN(_Next); | |||||
} | } | ||||
} | } | ||||
catch (...) { | |||||
LOCK_ASSEMBLE_AWAIT(_Unlock_locks(_First, _Next, _LkN)); | |||||
throw; | |||||
} | |||||
LOCK_ASSEMBLE_RETURN(-1); | |||||
} | |||||
catch (...) { | |||||
_Unlock_locks(_First, _Next, _LkN); | |||||
throw; | |||||
} | } | ||||
// FUNCTION TEMPLATE lock | |||||
template<_LockAssembleT _LA> | |||||
auto _Lock_attempt(const int _Hard_lock, _LA& _LkN) | |||||
->decltype(_LkN._ReturnValue<int>(0)) | |||||
{ | |||||
// attempt to lock 3 or more locks, starting by locking _LkN[_Hard_lock] and trying to lock the rest | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Lock_ref(_LkN[_Hard_lock])); | |||||
int _Failed = -1; | |||||
int _Backout_start = _Hard_lock; // that is, unlock _Hard_lock | |||||
LOCK_ASSEMBLE_RETURN(-1); | |||||
} | |||||
// FUNCTION TEMPLATE lock | |||||
template<_LockAssembleT _LA> | |||||
static auto _Lock_attempt(const int _Hard_lock, _LA& _LkN) | |||||
->decltype(_LkN._ReturnValue<int>(0)) | |||||
{ | |||||
// attempt to lock 3 or more locks, starting by locking _LkN[_Hard_lock] and trying to lock the rest | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Lock_ref(_LkN[_Hard_lock])); | |||||
int _Failed = -1; | |||||
int _Backout_start = _Hard_lock; // that is, unlock _Hard_lock | |||||
try { | |||||
_Failed = LOCK_ASSEMBLE_AWAIT(_Try_lock_range(0, _Hard_lock, _LkN)); | |||||
if (_Failed == -1) | |||||
{ | |||||
_Backout_start = 0; // that is, unlock [0, _Hard_lock] if the next throws | |||||
_Failed = LOCK_ASSEMBLE_AWAIT(_Try_lock_range(_Hard_lock + 1, (int)_LkN.size(), _LkN)); | |||||
if (_Failed == -1) { // we got all the locks | |||||
LOCK_ASSEMBLE_RETURN(-1); | |||||
} | |||||
try { | |||||
_Failed = LOCK_ASSEMBLE_AWAIT(_Try_lock_range(0, _Hard_lock, _LkN)); | |||||
if (_Failed == -1) | |||||
{ | |||||
_Backout_start = 0; // that is, unlock [0, _Hard_lock] if the next throws | |||||
_Failed = LOCK_ASSEMBLE_AWAIT(_Try_lock_range(_Hard_lock + 1, (int)_LkN.size(), _LkN)); | |||||
if (_Failed == -1) { // we got all the locks | |||||
LOCK_ASSEMBLE_RETURN(-1); | |||||
} | } | ||||
} | } | ||||
catch (...) { | |||||
LOCK_ASSEMBLE_AWAIT(_Unlock_locks(_Backout_start, _Hard_lock + 1, _LkN)); | |||||
throw; | |||||
} | |||||
} | |||||
catch (...) { | |||||
_Unlock_locks(_Backout_start, _Hard_lock + 1, _LkN); | |||||
throw; | |||||
} | |||||
// we didn't get all the locks, backout | |||||
LOCK_ASSEMBLE_AWAIT(_Unlock_locks(_Backout_start, _Hard_lock + 1, _LkN)); | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Yield()); | |||||
// we didn't get all the locks, backout | |||||
_Unlock_locks(_Backout_start, _Hard_lock + 1, _LkN); | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Yield()); | |||||
LOCK_ASSEMBLE_RETURN(_Failed); | |||||
} | |||||
LOCK_ASSEMBLE_RETURN(_Failed); | |||||
} | |||||
template<_LockAssembleT _LA> | |||||
auto _Lock_nonmember3(_LA& _LkN) ->decltype(_LkN._ReturnValue()) | |||||
{ | |||||
// lock 3 or more locks, without deadlock | |||||
int _Hard_lock = 0; | |||||
while (_Hard_lock != -1) { | |||||
_Hard_lock = LOCK_ASSEMBLE_AWAIT(_Lock_attempt(_Hard_lock, _LkN)); | |||||
} | |||||
template<_LockAssembleT _LA> | |||||
static auto _Lock_nonmember3(_LA& _LkN) ->decltype(_LkN._ReturnValue()) | |||||
{ | |||||
// lock 3 or more locks, without deadlock | |||||
int _Hard_lock = 0; | |||||
while (_Hard_lock != -1) { | |||||
_Hard_lock = LOCK_ASSEMBLE_AWAIT(_Lock_attempt(_Hard_lock, _LkN)); | |||||
} | } | ||||
} | |||||
template<_LockAssembleT _LA> | |||||
auto _Lock_attempt_small2(_LA& _LkN, const int _Idx0, const int _Idx1) | |||||
->decltype(_LkN._ReturnValue<bool>(false)) | |||||
{ | |||||
// attempt to lock 2 locks, by first locking _Lk0, and then trying to lock _Lk1 returns whether to try again | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Lock_ref(_LkN[_Idx0])); | |||||
try { | |||||
if (LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Idx1]))) | |||||
LOCK_ASSEMBLE_RETURN(false); | |||||
} | |||||
catch (...) { | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Unlock_ref(_LkN[_Idx0])); | |||||
throw; | |||||
} | |||||
template<_LockAssembleT _LA> | |||||
static auto _Lock_attempt_small2(_LA& _LkN, const int _Idx0, const int _Idx1) | |||||
->decltype(_LkN._ReturnValue<bool>(false)) | |||||
{ | |||||
// attempt to lock 2 locks, by first locking _Lk0, and then trying to lock _Lk1 returns whether to try again | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Lock_ref(_LkN[_Idx0])); | |||||
try { | |||||
if (LOCK_ASSEMBLE_AWAIT(_LkN._Try_lock_ref(_LkN[_Idx1]))) | |||||
LOCK_ASSEMBLE_RETURN(false); | |||||
} | |||||
catch (...) { | |||||
_LkN._Unlock_ref(_LkN[_Idx0]); | |||||
throw; | |||||
} | |||||
_LkN._Unlock_ref(_LkN[_Idx0]); | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Yield()); | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Unlock_ref(_LkN[_Idx0])); | |||||
LOCK_ASSEMBLE_AWAIT(_LkN._Yield()); | |||||
LOCK_ASSEMBLE_RETURN(true); | |||||
} | |||||
LOCK_ASSEMBLE_RETURN(true); | |||||
template<_LockAssembleT _LA> | |||||
static auto _Lock_nonmember2(_LA& _LkN) ->decltype(_LkN._ReturnValue()) | |||||
{ | |||||
// lock 2 locks, without deadlock, special case for better codegen and reduced metaprogramming for common case | |||||
while (LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 0, 1)) && | |||||
LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 1, 0))) | |||||
{ // keep trying | |||||
} | } | ||||
} | |||||
template<_LockAssembleT _LA> | |||||
auto _Lock_nonmember2(_LA& _LkN) ->decltype(_LkN._ReturnValue()) | |||||
template<_LockAssembleT _LA> | |||||
static auto _Lock_range(_LA& lockes) ->decltype(lockes._ReturnValue()) | |||||
{ | |||||
if (lockes.size() == 0) | |||||
{ | { | ||||
// lock 2 locks, without deadlock, special case for better codegen and reduced metaprogramming for common case | |||||
while (LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 0, 1)) && | |||||
LOCK_ASSEMBLE_AWAIT(_Lock_attempt_small2(_LkN, 1, 0))) | |||||
{ // keep trying | |||||
} | |||||
} | } | ||||
template<_LockAssembleT _LA> | |||||
auto _Lock_range(_LA& lockes) ->decltype(lockes._ReturnValue()) | |||||
else if (lockes.size() == 1) | |||||
{ | { | ||||
if (lockes.size() == 0) | |||||
{ | |||||
} | |||||
else if (lockes.size() == 1) | |||||
{ | |||||
LOCK_ASSEMBLE_AWAIT(lockes._Lock_ref(lockes[0])); | |||||
} | |||||
else if (lockes.size() == 2) | |||||
{ | |||||
LOCK_ASSEMBLE_AWAIT(_Lock_nonmember2(lockes)); | |||||
} | |||||
else | |||||
{ | |||||
LOCK_ASSEMBLE_AWAIT(_Lock_nonmember3(lockes)); | |||||
} | |||||
LOCK_ASSEMBLE_AWAIT(lockes._Lock_ref(lockes[0])); | |||||
} | |||||
else if (lockes.size() == 2) | |||||
{ | |||||
LOCK_ASSEMBLE_AWAIT(_Lock_nonmember2(lockes)); | |||||
} | |||||
else | |||||
{ | |||||
LOCK_ASSEMBLE_AWAIT(_Lock_nonmember3(lockes)); | |||||
} | } | ||||
} | } | ||||
} | |||||
}; | |||||
std::cout << "result:" << g_counter << std::endl; | std::cout << "result:" << g_counter << std::endl; | ||||
} | } | ||||
static future_t<> resumable_mutex_range_push(size_t idx, mutex_t a, mutex_t b, mutex_t c) | |||||
{ | |||||
for (int i = 0; i < 10; ++i) | |||||
{ | |||||
auto __lockers = mutex_t::lock(a, b, c); | |||||
++g_counter; | |||||
std::cout << "push:" << g_counter << " on " << idx << std::endl; | |||||
co_await 5ms; | |||||
} | |||||
} | |||||
static future_t<> resumable_mutex_range_pop(size_t idx, mutex_t a, mutex_t b, mutex_t c) | |||||
{ | |||||
for (int i = 0; i < 10; ++i) | |||||
{ | |||||
auto __lockers = mutex_t::lock(a, b, c); | |||||
--g_counter; | |||||
std::cout << "pop :" << g_counter << " on " << idx << std::endl; | |||||
co_await 5ms; | |||||
} | |||||
} | |||||
static void resumable_mutex_lock_range() | |||||
{ | |||||
mutex_t mtxA, mtxB, mtxC; | |||||
//不同的线程里加锁也需要是线程安全的 | |||||
std::thread push_th([&] | |||||
{ | |||||
local_scheduler __ls__; | |||||
go resumable_mutex_range_push(10, mtxA, mtxB, mtxC); | |||||
go resumable_mutex_range_push(11, mtxA, mtxC, mtxB); | |||||
this_scheduler()->run_until_notask(); | |||||
}); | |||||
go resumable_mutex_range_pop(12, mtxC, mtxB, mtxA); | |||||
go resumable_mutex_range_pop(13, mtxB, mtxA, mtxC); | |||||
this_scheduler()->run_until_notask(); | |||||
push_th.join(); | |||||
std::cout << "result:" << g_counter << std::endl; | |||||
} | |||||
void resumable_main_mutex() | void resumable_main_mutex() | ||||
{ | { | ||||
resumable_mutex_synch(); | resumable_mutex_synch(); | ||||
std::cout << std::endl; | std::cout << std::endl; | ||||
resumable_mutex_async(); | resumable_mutex_async(); | ||||
std::cout << std::endl; | |||||
resumable_mutex_lock_range(); | |||||
} | } |
//test_ring_queue<resumef::ring_queue_spinlock<int, false, uint32_t>>(); | //test_ring_queue<resumef::ring_queue_spinlock<int, false, uint32_t>>(); | ||||
//test_ring_queue<resumef::ring_queue_lockfree<int, uint64_t>>(); | //test_ring_queue<resumef::ring_queue_lockfree<int, uint64_t>>(); | ||||
resumable_main_event(); | |||||
resumable_main_mutex(); | resumable_main_mutex(); | ||||
return 0; | return 0; | ||||