Commit 6d4be662 authored by Xavier Thompson's avatar Xavier Thompson

Implement asychronous mutex in mutex.hpp

parent bcfdf8a3
#ifndef TYPON_MUTEX_HPP_INCLUDED
#define TYPON_MUTEX_HPP_INCLUDED
#include <atomic>
#include <coroutine>
#include <typon/scheduler.hpp>
#include <typon/stack.hpp>
namespace typon
{
/* An asynchronous mutex.
Based on the MCS lock, but without the spinning. This means it should be
easy to implement spinning before suspension.
Disadvantage: each lock acquisition incurs an allocation. The alternative
is to potentially spin when unlocking.
*/
struct Mutex
{
struct Node
{
std::atomic<Node *> _next { nullptr };
Stack * _stack;
};
std::atomic<Node *> _state { nullptr };
[[nodiscard]] auto lock() noexcept
{
struct awaitable
{
std::atomic<Node *> & _state;
Node * _prev;
Node * _node;
bool await_ready() noexcept
{
return !_prev;
}
void await_suspend(std::coroutine_handle<> coroutine) noexcept
{
auto stack = _node->_stack = Scheduler::suspend(coroutine);
auto ready = _prev->_next.exchange(_node);
if (ready)
{
delete _prev;
Scheduler::enable(stack);
}
}
void await_resume() noexcept {};
~awaitable()
{
Node * node = _node;
bool waiters = !_state.compare_exchange_strong(node, nullptr);
if (waiters)
{
node = _node;
auto next = node->_next.exchange(_node);
if (next)
{
delete node;
Scheduler::enable(next->_stack);
}
}
else
{
delete node;
}
}
};
auto node = new Node();
auto prev = _state.exchange(node);
return awaitable { this->_state, prev, node };
}
};
}
#endif // TYPON_MUTEX_HPP_INCLUDED
......@@ -7,6 +7,7 @@
#include <typon/forked.hpp>
#include <typon/future.hpp>
#include <typon/join.hpp>
#include <typon/mutex.hpp>
#include <typon/promise.hpp>
#include <typon/root.hpp>
#include <typon/task.hpp>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment