Skip to content

Commit

Permalink
Merge pull request #22 from petiaccja/testing
Browse files Browse the repository at this point in the history
improve test coverage
  • Loading branch information
petiaccja authored May 12, 2024
2 parents 869b4ec + 32b561b commit 2e55ee2
Show file tree
Hide file tree
Showing 13 changed files with 366 additions and 148 deletions.
28 changes: 16 additions & 12 deletions include/asyncpp/generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,33 +63,38 @@ namespace impl_generator {
explicit iterator(promise_type* promise) : m_promise(promise) {}

reference operator*() const {
assert(valid() && "iterator not dereferencable");
assert(dereferenceable() && "iterator not dereferencable");
return m_promise->get_result().get_or_throw();
}

iterator& operator++() {
assert(valid() && "iterator not incrementable");
get_handle().resume();
assert(incrementable() && "iterator not incrementable");
m_promise->get_result().clear();
if (!get_handle().done()) {
get_handle().resume();
}
return *this;
}

iterator operator++(int) {
auto copy = *this;
void operator++(int) {
++*this;
return copy;
}

bool operator==(const iterator& rhs) const noexcept {
return (m_promise == rhs.m_promise) || (!valid() && !rhs.m_promise);
return (m_promise == rhs.m_promise) || (!incrementable() && !rhs.incrementable());
}

bool operator!=(const iterator& rhs) const noexcept {
return !operator==(rhs);
}

private:
bool valid() const noexcept {
return m_promise && !get_handle().done();
bool dereferenceable() const noexcept {
return m_promise->get_result().has_value();
}

bool incrementable() const noexcept {
return m_promise && m_promise->get_result().has_value();
}

auto get_handle() const noexcept {
Expand All @@ -113,11 +118,10 @@ class [[nodiscard]] generator {

generator(promise_type* promise) : m_promise(promise) {}
generator() = default;
generator(generator&& rhs) noexcept : m_promise(rhs.m_promise) { rhs.m_promise = nullptr; }
generator(generator&& rhs) noexcept : m_promise(std::exchange(rhs.m_promise, nullptr)) {}
generator& operator=(generator&& rhs) noexcept {
release();
m_promise = rhs.m_promise;
rhs.m_promise = nullptr;
m_promise = std::exchange(rhs.m_promise, nullptr);
return *this;
}
generator(const generator&) = delete;
Expand Down
173 changes: 51 additions & 122 deletions include/asyncpp/lock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,104 +8,95 @@

namespace asyncpp {

template <class Mutex>
class locked_mutex {

template <class Mutex, bool Shared>
class basic_locked_mutex {
friend Mutex;

public:
locked_mutex(locked_mutex&&) = default;
locked_mutex& operator=(locked_mutex&&) = default;
locked_mutex(const locked_mutex&) = delete;
locked_mutex& operator=(const locked_mutex&) = delete;
basic_locked_mutex(basic_locked_mutex&&) = default;
basic_locked_mutex& operator=(basic_locked_mutex&&) = default;
basic_locked_mutex(const basic_locked_mutex&) = delete;
basic_locked_mutex& operator=(const basic_locked_mutex&) = delete;
Mutex& mutex() const noexcept {
return *m_mtx;
}

private:
locked_mutex(Mutex* mtx) : m_mtx(mtx) {}
basic_locked_mutex(Mutex* mtx) : m_mtx(mtx) {}
Mutex* m_mtx = nullptr;
};


template <class Mutex>
class locked_mutex_shared {
friend Mutex;
using exclusively_locked_mutex = basic_locked_mutex<Mutex, false>;
template <class Mutex>
using shared_locked_mutex = basic_locked_mutex<Mutex, true>;

public:
locked_mutex_shared(locked_mutex_shared&&) = default;
locked_mutex_shared& operator=(locked_mutex_shared&&) = default;
locked_mutex_shared(const locked_mutex_shared&) = delete;
locked_mutex_shared& operator=(const locked_mutex_shared&) = delete;
Mutex& mutex() const noexcept {
return *m_mtx;
}

private:
locked_mutex_shared(Mutex* mtx) : m_mtx(mtx) {}
Mutex* m_mtx = nullptr;
};
template <class Mutex, bool Shared, auto Lock, bool (Mutex::*TryLock)(), void (Mutex::*Unlock)()>
class basic_lock {
// NOTE: GCC bugs out on `auto (Mutex::*Lock)()`, that's why `Lock` is simply `auto`.

using mutex_awaitable_t = std::invoke_result_t<decltype(Lock), Mutex*>;

template <class Mutex>
class unique_lock {
using mutex_awaitable = std::invoke_result_t<decltype(&Mutex::exclusive), Mutex*>;
struct awaitable {
unique_lock* m_lock;
mutex_awaitable m_awaitable;
basic_lock* m_owner;
mutex_awaitable_t m_impl;

auto await_ready() noexcept {
return m_awaitable.await_ready();
return m_impl.await_ready();
}

template <class Promise>
auto await_suspend(std::coroutine_handle<Promise> enclosing) noexcept {
return m_awaitable.await_suspend(enclosing);
return m_impl.await_suspend(enclosing);
}

void await_resume() noexcept {
m_awaitable.await_resume();
m_lock->m_owned = true;
m_impl.await_resume();
m_owner->m_owned = true;
}
};

public:
unique_lock(Mutex& mtx, std::defer_lock_t) noexcept : m_mtx(&mtx), m_owned(false) {}
unique_lock(Mutex& mtx, std::adopt_lock_t) noexcept : m_mtx(&mtx), m_owned(true) {}
unique_lock(locked_mutex<Mutex>&& lk) noexcept : m_mtx(&lk.mutex()), m_owned(true) {}
unique_lock(unique_lock&& rhs) noexcept : m_mtx(rhs.m_mtx), m_owned(rhs.m_owned) {
basic_lock(Mutex& mtx, std::defer_lock_t) noexcept : m_mtx(&mtx), m_owned(false) {}
basic_lock(Mutex& mtx, std::adopt_lock_t) noexcept : m_mtx(&mtx), m_owned(true) {}
basic_lock(basic_locked_mutex<Mutex, Shared>&& lk) noexcept : m_mtx(&lk.mutex()), m_owned(true) {}
basic_lock(basic_lock&& rhs) noexcept : m_mtx(rhs.m_mtx), m_owned(rhs.m_owned) {
rhs.m_mtx = nullptr;
rhs.m_owned = false;
}
unique_lock& operator=(unique_lock&& rhs) noexcept {
basic_lock& operator=(basic_lock&& rhs) noexcept {
if (owns_lock()) {
m_mtx->unlock();
(m_mtx->*Unlock)();
}
m_mtx = std::exchange(rhs.m_mtx, nullptr);
m_owned = std::exchange(rhs.m_owned, false);
return *this;
}
unique_lock(const unique_lock& rhs) = delete;
unique_lock& operator=(const unique_lock& rhs) = delete;
~unique_lock() {
basic_lock(const basic_lock& rhs) = delete;
basic_lock& operator=(const basic_lock& rhs) = delete;
~basic_lock() {
if (owns_lock()) {
m_mtx->unlock();
(m_mtx->*Unlock)();
}
}

bool try_lock() noexcept {
assert(!owns_lock());
m_owned = m_mtx->try_lock();
m_owned = (m_mtx->*TryLock)();
return m_owned;
}

auto operator co_await() noexcept {
assert(!owns_lock());
return awaitable(this, m_mtx->exclusive());
return awaitable{ this, (m_mtx->*Lock)() };
}

void unlock() noexcept {
assert(owns_lock());
m_mtx->unlock();
(m_mtx->*Unlock)();
m_owned = false;
}

Expand All @@ -128,92 +119,30 @@ class unique_lock {


template <class Mutex>
unique_lock(locked_mutex<Mutex>&& lk) -> unique_lock<Mutex>;

class unique_lock : public basic_lock<Mutex, false, &Mutex::exclusive, &Mutex::try_lock, &Mutex::unlock> {
using basic_lock<Mutex, false, &Mutex::exclusive, &Mutex::try_lock, &Mutex::unlock>::basic_lock;
};

template <class Mutex>
class shared_lock {
using mutex_awaitable = std::invoke_result_t<decltype(&Mutex::shared), Mutex*>;
struct awaitable {
shared_lock* m_lock;
mutex_awaitable m_awaitable;

auto await_ready() noexcept {
return m_awaitable.await_ready();
}

template <class Promise>
auto await_suspend(std::coroutine_handle<Promise> enclosing) noexcept {
return m_awaitable.await_suspend(enclosing);
}

void await_resume() noexcept {
m_awaitable.await_resume();
m_lock->m_owned = true;
}
};

public:
shared_lock(Mutex& mtx, std::defer_lock_t) noexcept : m_mtx(&mtx), m_owned(false) {}
shared_lock(Mutex& mtx, std::adopt_lock_t) noexcept : m_mtx(&mtx), m_owned(true) {}
shared_lock(locked_mutex_shared<Mutex> lk) noexcept : m_mtx(&lk.mutex()), m_owned(true) {}
shared_lock(shared_lock&& rhs) noexcept : m_mtx(rhs.m_mtx), m_owned(rhs.m_owned) {
rhs.m_mtx = nullptr;
rhs.m_owned = false;
}
shared_lock& operator=(shared_lock&& rhs) noexcept {
if (owns_lock()) {
m_mtx->unlock_shared();
}
m_mtx = std::exchange(rhs.m_mtx, nullptr);
m_owned = std::exchange(rhs.m_owned, false);
return *this;
}
shared_lock(const shared_lock& rhs) = delete;
shared_lock& operator=(const shared_lock& rhs) = delete;
~shared_lock() {
if (owns_lock()) {
m_mtx->unlock_shared();
}
}

bool try_lock() noexcept {
assert(!owns_lock());
m_owned = m_mtx->try_lock_shared();
return m_owned;
}

auto operator co_await() noexcept {
assert(!owns_lock());
return awaitable(this, m_mtx->shared());
}

void unlock() noexcept {
assert(owns_lock());
m_mtx->unlock_shared();
m_owned = false;
}

Mutex& mutex() const noexcept {
return *m_mtx;
}

bool owns_lock() const noexcept {
return m_owned;
}
class shared_lock : public basic_lock<Mutex, true, &Mutex::shared, &Mutex::try_lock_shared, &Mutex::unlock_shared> {
using basic_lock<Mutex, true, &Mutex::shared, &Mutex::try_lock_shared, &Mutex::unlock_shared>::basic_lock;
};

operator bool() const noexcept {
return owns_lock();
}

private:
Mutex* m_mtx;
bool m_owned = false;
};
template <class Mutex>
unique_lock(exclusively_locked_mutex<Mutex>) -> unique_lock<Mutex>;
template <class Mutex>
unique_lock(Mutex&, std::adopt_lock_t) -> unique_lock<Mutex>;
template <class Mutex>
unique_lock(Mutex&, std::defer_lock_t) -> unique_lock<Mutex>;


template <class Mutex>
shared_lock(locked_mutex_shared<Mutex> lk) -> shared_lock<Mutex>;
shared_lock(shared_locked_mutex<Mutex>) -> shared_lock<Mutex>;
template <class Mutex>
shared_lock(Mutex&, std::adopt_lock_t) -> shared_lock<Mutex>;
template <class Mutex>
shared_lock(Mutex&, std::defer_lock_t) -> shared_lock<Mutex>;


} // namespace asyncpp
2 changes: 1 addition & 1 deletion include/asyncpp/mutex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class mutex {
template <std::convertible_to<const resumable_promise&> Promise>
bool await_suspend(std::coroutine_handle<Promise> enclosing) noexcept;

locked_mutex<mutex> await_resume() noexcept;
exclusively_locked_mutex<mutex> await_resume() noexcept;
};

bool add_awaiting(awaitable* waiting);
Expand Down
4 changes: 2 additions & 2 deletions include/asyncpp/shared_mutex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ class shared_mutex {
: basic_awaitable(owner, awaitable_type::exclusive) {}

bool await_ready() const noexcept;
locked_mutex<shared_mutex> await_resume() const noexcept;
exclusively_locked_mutex<shared_mutex> await_resume() const noexcept;
};

struct shared_awaitable : basic_awaitable {
explicit shared_awaitable(shared_mutex* owner = nullptr)
: basic_awaitable(owner, awaitable_type::shared) {}

bool await_ready() const noexcept;
locked_mutex_shared<shared_mutex> await_resume() const noexcept;
shared_locked_mutex<shared_mutex> await_resume() const noexcept;
};

bool add_awaiting(basic_awaitable* waiting);
Expand Down
Loading

0 comments on commit 2e55ee2

Please sign in to comment.