• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jbaldwin / libcoro / 15455559001

05 Jun 2025 12:27AM UTC coverage: 87.595%. First build
15455559001

Pull #336

github

web-flow
Merge 02c038965 into f0cccaaf4
Pull Request #336: coro::ring_buffer use coro::mutex instead of std::mutex

106 of 120 new or added lines in 5 files covered. (88.33%)

1610 of 1838 relevant lines covered (87.6%)

5644127.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.44
/src/mutex.cpp
1
#include "coro/detail/awaiter_list.hpp"
2
#include "coro/mutex.hpp"
3

4
#include <iostream>
5

6
namespace coro
7
{
8
namespace detail
9
{
10
auto lock_operation_base::await_ready() const noexcept -> bool
49,003,382✔
11
{
12
    return m_mutex.try_lock();
49,003,382✔
13
}
14

15
auto lock_operation_base::await_suspend(std::coroutine_handle<> awaiting_coroutine) noexcept -> bool
3,042,189✔
16
{
17
    m_awaiting_coroutine = awaiting_coroutine;
3,042,189✔
18
    auto& state = m_mutex.m_state;
3,042,189✔
19
    void* current = state.load(std::memory_order::acquire);
3,042,189✔
20
    const void* unlocked_value = m_mutex.unlocked_value();
3,068,209✔
21
    do
22
    {
23
        // While trying to suspend the lock can become available, if so attempt to grab it and then don't suspend.
24
        // If the lock never becomes available then we place ourself at the head of the waiter list and suspend.
25

26
        if (current == unlocked_value)
3,993,315✔
27
        {
28
            // The lock has become available, try and lock.
29
            if (state.compare_exchange_weak(current, nullptr, std::memory_order::acq_rel, std::memory_order::acquire))
471,054✔
30
            {
31
                // We've acquired the lock, don't suspend.
32
                m_awaiting_coroutine = nullptr;
307,907✔
33
                return false;
419,786✔
34
            }
35
        }
36
        else // if (current == nullptr || current is of type lock_operation_base*)
37
        {
38
            // The lock is still owned, attempt to add ourself as a waiter.
39
            m_next = static_cast<lock_operation_base*>(current);
3,522,261✔
40
            if (state.compare_exchange_weak(current, static_cast<void*>(this), std::memory_order::acq_rel, std::memory_order::acquire))
3,522,261✔
41
            {
42
                // We've successfully added ourself to the waiter queue.
43
                return true;
2,767,626✔
44
            }
45
        }
46
    } while (true);
47
}
48

49
} // namespace detail
50

51
scoped_lock::~scoped_lock()
27,279,271✔
52
{
53
    unlock();
27,279,271✔
54
}
27,283,469✔
55

56
auto scoped_lock::unlock() -> void
32,565,017✔
57
{
58
    if (m_mutex != nullptr)
32,565,017✔
59
    {
60
        std::atomic_thread_fence(std::memory_order::acq_rel);
61
        m_mutex->unlock();
27,296,929✔
62
        m_mutex = nullptr;
27,286,069✔
63
    }
64
}
32,554,157✔
65

66
auto mutex::try_lock() -> bool
48,999,814✔
67
{
68
    void* expected = const_cast<void*>(unlocked_value());
48,999,814✔
69
    return m_state.compare_exchange_strong(expected, nullptr, std::memory_order::acq_rel, std::memory_order::relaxed);
49,019,218✔
70
}
71

72
auto mutex::unlock() -> void
49,297,342✔
73
{
74
    void* current = m_state.load(std::memory_order::acquire);
49,297,342✔
75
    do
76
    {
77
        // Sanity check that the mutex isn't already unlocked.
78
        if (current == const_cast<void*>(unlocked_value()))
49,556,836✔
79
        {
NEW
80
            throw std::runtime_error{"coro::mutex is already unlocked"};
×
81
        }
82

83
        // There are no current waiters, attempt to set the mutex as unlocked.
84
        if (current == nullptr)
49,556,836✔
85
        {
86
            if (m_state.compare_exchange_weak(
46,785,090✔
87
                current,
88
                const_cast<void*>(unlocked_value()),
46,785,090✔
89
                std::memory_order::acq_rel,
90
                std::memory_order::acquire))
91
            {
92
                // We've successfully unlocked the mutex, return since there are no current waiters.
93
                std::atomic_thread_fence(std::memory_order::acq_rel);
94
                return;
46,525,587✔
95
            }
96
            else
97
            {
98
                // This means someone has added themselves as a waiter, we need to try again with our updated current state.
99
                // assert(m_state now holds a lock_operation_base*)
100
                continue;
259,494✔
101
            }
102
        }
103
        else
104
        {
105
            // There are waiters, lets wake the first one up. This will set the state to the next waiter, or nullptr (no waiters but locked).
106
            std::atomic<detail::lock_operation_base*>* casted = reinterpret_cast<std::atomic<detail::lock_operation_base*>*>(&m_state);
2,771,746✔
107
            auto* waiter = detail::awaiter_list_pop<detail::lock_operation_base>(*casted);
2,771,746✔
108
            // assert waiter != nullptr, nobody else should be unlocking this mutex.
109
            // Directly transfer control to the waiter, they are now responsible for unlocking the mutex.
110
            std::atomic_thread_fence(std::memory_order::acq_rel);
111
            waiter->m_awaiting_coroutine.resume();
2,771,746✔
112
            return;
2,753,016✔
113
        }
114
    } while (true);
259,494✔
115
}
116

117
} // namespace coro
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc