Browse Source

整理代码,以便于控制doxygen的结果

tags/v2.9.7
tearshark 4 years ago
parent
commit
7d40fa6d5e

+ 5
- 1
Doxyfile View File

@@ -905,7 +905,11 @@ RECURSIVE = NO
EXCLUDE = channel_v1.h \
event_v1.h \
mutex_v1.h
mutex_v1.h \
ring_queue.h \
ring_queue_lockfree.h \
ring_queue_spinlock.h \
intrusive_link_queue.h
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded

+ 48
- 8
librf/src/event_v2.h View File

@@ -45,14 +45,24 @@ RESUMEF_NS
struct [[nodiscard]] any_awaiter;

template<class _Iter
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_iterator_of_v<_Iter, event_t>)
> RESUMEF_REQUIRES(_IteratorOfT<_Iter, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
>
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(_IteratorOfT<_Iter, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
static auto wait_any(_Iter begin_, _Iter end_)
->any_awaiter<_Iter>;

template<class _Cont
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_container_of_v<_Cont, event_t>)
> RESUMEF_REQUIRES(_ContainerOfT<_Cont, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
>
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(_ContainerOfT<_Cont, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
static auto wait_any(_Cont& cnt_)
->any_awaiter<decltype(std::begin(cnt_))>;

@@ -60,14 +70,24 @@ RESUMEF_NS
struct [[nodiscard]] timeout_any_awaiter;

template<class _Rep, class _Period, class _Iter
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_iterator_of_v<_Iter, event_t>)
> RESUMEF_REQUIRES(_IteratorOfT<_Iter, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
>
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(_IteratorOfT<_Iter, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
static auto wait_any_for(const std::chrono::duration<_Rep, _Period>& dt, _Iter begin_, _Iter end_)
->timeout_any_awaiter<_Iter>;

template<class _Rep, class _Period, class _Cont
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_container_of_v<_Cont, event_t>)
> RESUMEF_REQUIRES(_ContainerOfT<_Cont, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
>
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(_ContainerOfT<_Cont, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
static auto wait_any_for(const std::chrono::duration<_Rep, _Period>& dt, _Cont& cnt_)
->timeout_any_awaiter<decltype(std::begin(cnt_))>;

@@ -77,14 +97,24 @@ RESUMEF_NS
struct [[nodiscard]] all_awaiter;

template<class _Iter
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_iterator_of_v<_Iter, event_t>)
> RESUMEF_REQUIRES(_IteratorOfT<_Iter, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
>
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(_IteratorOfT<_Iter, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
static auto wait_all(_Iter begin_, _Iter end_)
->all_awaiter<_Iter>;

template<class _Cont
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_container_of_v<_Cont, event_t>)
> RESUMEF_REQUIRES(_ContainerOfT<_Cont, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
>
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(_ContainerOfT<_Cont, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
static auto wait_all(_Cont& cnt_)
->all_awaiter<decltype(std::begin(cnt_))>;

@@ -92,14 +122,24 @@ RESUMEF_NS
struct [[nodiscard]] timeout_all_awaiter;

template<class _Rep, class _Period, class _Iter
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_iterator_of_v<_Iter, event_t>)
> RESUMEF_REQUIRES(_IteratorOfT<_Iter, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
>
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(_IteratorOfT<_Iter, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
static auto wait_all_for(const std::chrono::duration<_Rep, _Period>& dt, _Iter begin_, _Iter end_)
->timeout_all_awaiter<_Iter>;

template<class _Rep, class _Period, class _Cont
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_container_of_v<_Cont, event_t>)
> RESUMEF_REQUIRES(_ContainerOfT<_Cont, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
>
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(_ContainerOfT<_Cont, event_t>)
#endif //DOXYGEN_SKIP_PROPERTY
static auto wait_all_for(const std::chrono::duration<_Rep, _Period>& dt, _Cont& cnt_)
->timeout_all_awaiter<decltype(std::begin(cnt_))>;


+ 2
- 2
librf/src/event_v2.inl View File

@@ -273,7 +273,7 @@ RESUMEF_NS
lockes.emplace_back(std::ref(evt->_lock));
}
scoped_lock_range<ref_lock_type> lock_(lockes);
batch_lock_t<ref_lock_type> lock_(lockes);
for (auto iter = _begin; iter != _end; ++iter)
{
@@ -404,7 +404,7 @@ RESUMEF_NS
(void)_state->on_await_suspend(handler);
cb();
scoped_lock_range<ref_lock_type> lock_(lockes);
batch_lock_t<ref_lock_type> lock_(lockes);
for (auto iter = _begin; iter != _end; ++iter)
{

+ 1
- 1
librf/src/generator.h View File

@@ -256,7 +256,7 @@ RESUMEF_NS
};
#endif //DOXYGEN_SKIP_PROPERTY
typedef generator_iterator<_Ty, promise_type> iterator;
using iterator = generator_iterator<_Ty, promise_type>;
iterator begin()
{

+ 12
- 12
librf/src/mutex_v2.h View File

@@ -12,12 +12,12 @@ RESUMEF_NS
{
#endif //DOXYGEN_SKIP_PROPERTY
/**
* @brief 提示手工解锁,故相关的lock()函数不再返回scoped_unlock_t。
* @brief 提示手工解锁,故相关的lock()函数不再返回batch_unlock_t。
*/
struct adopt_manual_unlock_t{};

/**
* @brief 提示手工解锁,故相关的lock()函数不再返回scoped_unlock_t。
* @brief 提示手工解锁,故相关的lock()函数不再返回batch_unlock_t。
*/
constexpr adopt_manual_unlock_t adopt_manual_unlock;

@@ -25,7 +25,7 @@ RESUMEF_NS
* @brief 在析构的时候自动解锁mutex_t的辅助类。
*/
template<class... _Mtxs>
struct [[nodiscard]] scoped_unlock_t;
struct [[nodiscard]] batch_unlock_t;

/**
* @brief 支持递归的锁。
@@ -41,15 +41,15 @@ RESUMEF_NS

/**
* @brief 在协程中加锁,如果不能立即获得锁,则阻塞当前协程。但不会阻塞当前线程。
* @return [co_await] scoped_unlock_t
* @return [co_await] batch_unlock_t
*/
awaiter/*scoped_unlock_t*/ lock() const noexcept;
awaiter/*batch_unlock_t*/ lock() const noexcept;
/**
* @brief 等同调用 co_await lock()。
* @return [co_await] scoped_unlock_t
* @return [co_await] batch_unlock_t
*/
awaiter/*scoped_unlock_t*/ operator co_await() const noexcept;
awaiter/*batch_unlock_t*/ operator co_await() const noexcept;

/**
* @brief 在协程中加锁,如果不能立即获得锁,则阻塞当前协程。但不会阻塞当前线程。
@@ -137,14 +137,14 @@ RESUMEF_NS
/**
* @brief 在协程中,无死锁的批量加锁。不会阻塞当前线程。直到获得所有锁之前,会阻塞当前协程。
* @param mtxs... 需要获得的锁列表。
* @return [co_await] scoped_unlock_t
* @return [co_await] batch_unlock_t
*/
template<class... _Mtxs
#ifndef DOXYGEN_SKIP_PROPERTY
, typename = std::enable_if_t<std::conjunction_v<std::is_same<remove_cvref_t<_Mtxs>, mutex_t>...>>
#endif //DOXYGEN_SKIP_PROPERTY
>
static future_t<scoped_unlock_t<_Mtxs...>> lock(_Mtxs&... mtxs);
static future_t<batch_unlock_t<_Mtxs...>> lock(_Mtxs&... mtxs);

/**
* @brief 在协程中,无死锁的批量加锁。不会阻塞当前线程。直到获得所有锁之前,会阻塞当前协程。
@@ -176,14 +176,14 @@ RESUMEF_NS
* @brief 在非协程中,无死锁的批量加锁。会阻塞当前线程,直到获得所有锁为止。
* @param unique_address 代表获得锁的拥有者。
* @param mtxs... 需要获得的锁列表。
* @return scoped_unlock_t
* @return batch_unlock_t
*/
template<class... _Mtxs
#ifndef DOXYGEN_SKIP_PROPERTY
, typename = std::enable_if_t<std::conjunction_v<std::is_same<remove_cvref_t<_Mtxs>, mutex_t>...>>
#endif //DOXYGEN_SKIP_PROPERTY
>
static scoped_unlock_t<_Mtxs...> lock(void* unique_address, _Mtxs&... mtxs);
static batch_unlock_t<_Mtxs...> lock(void* unique_address, _Mtxs&... mtxs);

/**
* @brief 在非协程中,无死锁的批量加锁。会阻塞当前线程,直到获得所有锁为止。
@@ -229,7 +229,7 @@ RESUMEF_NS
private:
struct _MutexAwaitAssembleT;

template<class... _Mtxs> friend struct scoped_unlock_t;
template<class... _Mtxs> friend struct batch_unlock_t;

mutex_impl_ptr _mutex;
#endif //DOXYGEN_SKIP_PROPERTY

+ 30
- 28
librf/src/mutex_v2.inl View File

@@ -127,23 +127,28 @@ RESUMEF_NS
inline namespace mutex_v2
{
template<>
struct [[nodiscard]] scoped_unlock_t<mutex_t>
struct [[nodiscard]] batch_unlock_t<mutex_t>
{
typedef std::shared_ptr<detail::mutex_v2_impl> mutex_impl_ptr;

scoped_unlock_t()
batch_unlock_t()
: _owner(nullptr)
{}

//此函数,应该在try_lock()获得锁后使用
//或者在协程里,由awaiter使用
scoped_unlock_t(std::adopt_lock_t, void* sch, mutex_impl_ptr mtx)
batch_unlock_t(std::adopt_lock_t, void* sch, mutex_impl_ptr mtx)
: _mutex(std::move(mtx))
, _owner(sch)
{}

batch_unlock_t(std::adopt_lock_t, void* sch, const mutex_t& mtx)
: batch_unlock_t(std::adopt_lock, sch, mtx._mutex)
{}

/*
//此函数,适合在非协程里使用
scoped_unlock_t(void* sch, mutex_impl_ptr mtx)
batch_unlock_t(void* sch, mutex_impl_ptr mtx)
: _mutex(std::move(mtx))
, _owner(sch)
{
@@ -151,15 +156,12 @@ RESUMEF_NS
_mutex->lock_until_succeed(sch);
}


scoped_unlock_t(std::adopt_lock_t, void* sch, const mutex_t& mtx)
: scoped_unlock_t(std::adopt_lock, sch, mtx._mutex)
{}
scoped_unlock_t(void* sch, const mutex_t& mtx)
: scoped_unlock_t(sch, mtx._mutex)
batch_unlock_t(void* sch, const mutex_t& mtx)
: batch_unlock_t(sch, mtx._mutex)
{}
*/

~scoped_unlock_t()
~batch_unlock_t()
{
if (_mutex != nullptr)
_mutex->unlock(_owner);
@@ -174,10 +176,10 @@ RESUMEF_NS
}
}

scoped_unlock_t(const scoped_unlock_t&) = delete;
scoped_unlock_t& operator = (const scoped_unlock_t&) = delete;
scoped_unlock_t(scoped_unlock_t&& _Right) = default;
scoped_unlock_t& operator = (scoped_unlock_t&& _Right) = default;
batch_unlock_t(const batch_unlock_t&) = delete;
batch_unlock_t& operator = (const batch_unlock_t&) = delete;
batch_unlock_t(batch_unlock_t&& _Right) = default;
batch_unlock_t& operator = (batch_unlock_t&& _Right) = default;
private:
mutex_impl_ptr _mutex;
void* _owner;
@@ -241,7 +243,7 @@ RESUMEF_NS
{
return await_suspend2(handler, []{});
}
scoped_unlock_t<mutex_t> await_resume() noexcept
batch_unlock_t<mutex_t> await_resume() noexcept
{
mutex_impl_ptr mtx = _mutex ? _mutex->shared_from_this() : nullptr;
_mutex = nullptr;
@@ -487,16 +489,16 @@ RESUMEF_NS
};

template<class... _Mtxs>
struct [[nodiscard]] scoped_unlock_t
struct [[nodiscard]] batch_unlock_t
{
mutex_t::_MutexAwaitAssembleT _MAA;

template<class... U>
scoped_unlock_t(std::adopt_lock_t, void* sch, U&&... mtxs)
batch_unlock_t(std::adopt_lock_t, void* sch, U&&... mtxs)
: _MAA(sch, std::forward<U>(mtxs)...)
{}

~scoped_unlock_t()
~batch_unlock_t()
{
if (_MAA._owner != nullptr)
{
@@ -515,19 +517,19 @@ RESUMEF_NS
}
}

scoped_unlock_t(const scoped_unlock_t&) = delete;
scoped_unlock_t& operator = (const scoped_unlock_t&) = delete;
scoped_unlock_t(scoped_unlock_t&& _Right) = default;
scoped_unlock_t& operator = (scoped_unlock_t&& _Right) = default;
batch_unlock_t(const batch_unlock_t&) = delete;
batch_unlock_t& operator = (const batch_unlock_t&) = delete;
batch_unlock_t(batch_unlock_t&& _Right) = default;
batch_unlock_t& operator = (batch_unlock_t&& _Right) = default;
};

template<class... _Mtxs>
scoped_unlock_t()->scoped_unlock_t<_Mtxs...>;
batch_unlock_t()->batch_unlock_t<_Mtxs...>;

template<class... _Mtxs, typename>
inline future_t<scoped_unlock_t<_Mtxs...>> mutex_t::lock(_Mtxs&... mtxs)
inline future_t<batch_unlock_t<_Mtxs...>> mutex_t::lock(_Mtxs&... mtxs)
{
scoped_unlock_t<_Mtxs...> unlock_guard{ std::adopt_lock, root_state(), mtxs... };
batch_unlock_t<_Mtxs...> unlock_guard{ std::adopt_lock, root_state(), mtxs... };
co_await detail::mutex_lock_await_lock_impl::_Lock_range(unlock_guard._MAA);
co_return std::move(unlock_guard);
}
@@ -548,14 +550,14 @@ RESUMEF_NS
}

template<class... _Mtxs, typename>
inline scoped_unlock_t<_Mtxs...> mutex_t::lock(void* unique_address, _Mtxs&... mtxs)
inline batch_unlock_t<_Mtxs...> mutex_t::lock(void* unique_address, _Mtxs&... mtxs)
{
assert(unique_address != nullptr);

detail::_MutexAddressAssembleT _MAA{ unique_address, mtxs... };
detail::scoped_lock_range_lock_impl::_Lock_range(_MAA);
scoped_unlock_t<_Mtxs...> su{ std::adopt_lock, unique_address };
batch_unlock_t<_Mtxs...> su{ std::adopt_lock, unique_address };
su._MAA._mutex = std::move(_MAA._mutex);
return su;
}

+ 16
- 10
librf/src/scheduler.h View File

@@ -1,15 +1,17 @@
#pragma once
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_NS
{
struct local_scheduler;
#endif //DOXYGEN_SKIP_PROPERTY
struct scheduler_t : public std::enable_shared_from_this<scheduler_t>
{
private:
using state_sptr = counted_ptr<state_base_t>;
using state_vector = std::vector<state_sptr>;
private:
using lock_type = std::recursive_mutex;
using lock_type = spinlock;
using task_dictionary_type = std::unordered_map<state_base_t*, std::unique_ptr<task_base_t>>;
mutable spinlock _lock_running;
@@ -23,7 +25,6 @@ RESUMEF_NS
void new_task(task_base_t* task);
//void cancel_all_task_();
public:
void run_one_batch();
void run_until_notask();
@@ -31,10 +32,14 @@ RESUMEF_NS
//void break_all();
template<class _Ty
#ifndef DOXYGEN_SKIP_PROPERTY
COMMA_RESUMEF_ENABLE_IF(traits::is_callable_v<_Ty> || traits::is_future_v<_Ty> || traits::is_generator_v<_Ty>)
#endif //DOXYGEN_SKIP_PROPERTY
>
RESUMEF_REQUIRES(traits::is_callable_v<_Ty> || traits::is_future_v<_Ty> || traits::is_generator_v<_Ty>)
inline void operator + (_Ty&& t_)
#ifndef DOXYGEN_SKIP_PROPERTY
RESUMEF_REQUIRES(traits::is_callable_v<_Ty> || traits::is_future_v<_Ty> || traits::is_generator_v<_Ty>)
#endif //DOXYGEN_SKIP_PROPERTY
void operator + (_Ty&& t_)
{
if constexpr (traits::is_callable_v<_Ty>)
new_task(new ctx_task_t<_Ty>(t_));
@@ -42,17 +47,18 @@ RESUMEF_NS
new_task(new task_t<_Ty>(t_));
}
inline bool empty() const
bool empty() const
{
scoped_lock<spinlock, spinlock> __guard(_lock_ready, _lock_running);
return _ready_task.empty() && _runing_states.empty() && _timer->empty();
}
inline timer_manager* timer() const noexcept
timer_manager* timer() const noexcept
{
return _timer.get();
}
#ifndef DOXYGEN_SKIP_PROPERTY
void add_generator(state_base_t* sptr);
void del_final(state_base_t* sptr);
std::unique_ptr<task_base_t> del_switch(state_base_t* sptr);
@@ -70,6 +76,7 @@ RESUMEF_NS
scheduler_t& operator = (const scheduler_t&) = delete;
static scheduler_t g_scheduler;
#endif //DOXYGEN_SKIP_PROPERTY
};
struct local_scheduler
@@ -87,7 +94,8 @@ RESUMEF_NS
scheduler_t* _scheduler_ptr;
#endif
};
//--------------------------------------------------------------------------------------------------
#if !RESUMEF_ENABLE_MULT_SCHEDULER
//获得当前线程下的调度器
inline scheduler_t* this_scheduler()
@@ -95,6 +103,4 @@ RESUMEF_NS
return &scheduler_t::g_scheduler;
}
#endif
//--------------------------------------------------------------------------------------------------
}

+ 12
- 10
librf/src/spinlock.h View File

@@ -178,34 +178,36 @@ RESUMEF_NS
}
#endif //DOXYGEN_SKIP_PROPERTY
// class with destructor that unlocks mutexes
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 batch_lock_t
{
public:
explicit scoped_lock_range(_Cont& locks_)
explicit batch_lock_t(_Cont& locks_)
: _LkN(&locks_)
, _LA(*_LkN)
{
detail::scoped_lock_range_lock_impl::_Lock_range(_LA);
}
explicit scoped_lock_range(_Cont& locks_, _Assemble& la_)
explicit batch_lock_t(_Cont& locks_, _Assemble& la_)
: _LkN(&locks_)
, _LA(la_)
{
detail::scoped_lock_range_lock_impl::_Lock_range(_LA);
}
explicit scoped_lock_range(std::adopt_lock_t, _Cont& locks_)
explicit batch_lock_t(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_)
explicit batch_lock_t(std::adopt_lock_t, _Cont& locks_, _Assemble& la_)
: _LkN(&locks_)
, _LA(la_)
{ // construct but don't lock
}
~scoped_lock_range() noexcept
~batch_lock_t() noexcept
{
if (_LkN != nullptr)
detail::scoped_lock_range_lock_impl::_Unlock_locks(0, (int)_LA.size(), _LA);
@@ -220,16 +222,16 @@ RESUMEF_NS
}
}
scoped_lock_range(const scoped_lock_range&) = delete;
scoped_lock_range& operator=(const scoped_lock_range&) = delete;
batch_lock_t(const batch_lock_t&) = delete;
batch_lock_t& operator=(const batch_lock_t&) = delete;
scoped_lock_range(scoped_lock_range&& _Right)
batch_lock_t(batch_lock_t&& _Right)
: _LkN(_Right._LkN)
, _LA(std::move(_Right._LA))
{
_Right._LkN = nullptr;
}
scoped_lock_range& operator=(scoped_lock_range&& _Right)
batch_lock_t& operator=(batch_lock_t&& _Right)
{
if (this != &_Right)
{

+ 5
- 5
tutorial/test_async_mutex.cpp View File

@@ -21,14 +21,14 @@ static future_t<> test_mutex_pop(size_t idx)
for (size_t i = 0; i < N / 2; ++i)
{
{
scoped_unlock_t _locker = co_await g_lock.lock(); //_locker析构后,会调用对应的unlock()函数。
batch_unlock_t _locker = co_await g_lock.lock(); //_locker析构后,会调用对应的unlock()函数。
--g_counter;
std::cout << "pop :" << g_counter << " on " << idx << std::endl;
co_await 50ms;
scoped_unlock_t _locker_2 = co_await g_lock;
batch_unlock_t _locker_2 = co_await g_lock;
--g_counter;
std::cout << "pop :" << g_counter << " on " << idx << std::endl;
@@ -45,7 +45,7 @@ static future_t<> test_mutex_push(size_t idx)
for (size_t i = 0; i < N; ++i)
{
{
scoped_unlock_t _locker = co_await g_lock.lock();
batch_unlock_t _locker = co_await g_lock.lock();
++g_counter;
std::cout << "push:" << g_counter << " on " << idx << std::endl;
@@ -103,7 +103,7 @@ static std::thread test_mutex_async_push(size_t idx)
{
if (g_lock.try_lock_for(500ms, &provide_unique_address))
{
scoped_unlock_t _locker(std::adopt_lock, &provide_unique_address, g_lock);
batch_unlock_t _locker(std::adopt_lock, &provide_unique_address, g_lock);
++g_counter;
std::cout << "push:" << g_counter << " on " << idx << std::endl;
@@ -143,7 +143,7 @@ static future_t<> resumable_mutex_range_push(size_t idx, mutex_t a, mutex_t b, m
{
for (int i = 0; i < 10000; ++i)
{
scoped_unlock_t __lockers = co_await mutex_t::lock(a, b, c);
batch_unlock_t __lockers = co_await mutex_t::lock(a, b, c);
assert(a.is_locked());
assert(b.is_locked());
assert(c.is_locked());

Loading…
Cancel
Save