-
Notifications
You must be signed in to change notification settings - Fork 136
/
mingw.latch.h
122 lines (100 loc) · 3.27 KB
/
mingw.latch.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/// \file mingw.latch.h
/// \brief std::latch implementation for MinGW.
///
/// (c) 2023 by Mega Limited, Auckland, New Zealand
/// \author Edoardo Sanguineti
///
/// \copyright Simplified (2-clause) BSD License.
///
/// \note This file may become part of the mingw-w64 runtime package. If/when
/// this happens, the appropriate license will be added, i.e. this code will
/// become dual-licensed, and the current BSD 2-clause license will stay.
// Notes on the namespaces:
// - The implementation can be accessed directly in the namespace
// mingw_stdthread.
// - Objects will be brought into namespace std by a using directive. This
// will cause objects declared in std (such as MinGW's implementation) to
// hide this implementation's definitions.
// The end result is that if MinGW supplies an object, it is automatically
// used. If MinGW does not supply an object, this implementation's version will
// instead be used.
#ifndef MINGW_LATCH_H_
#define MINGW_LATCH_H_
#if !defined(__cplusplus) || (__cplusplus < 202002L)
#error The contents of <latch> require a C++20 compiler!
#endif
#include <atomic>
#include <cassert> // for descriptive errors
#include <cstddef> // for std::ptrdiff_t
#include <limits> // for std::numeric_limits
namespace mingw_stdthread
{
class latch
{
public:
[[nodiscard]] static constexpr std::ptrdiff_t max() noexcept
{
return std::numeric_limits<std::ptrdiff_t>::max();
}
constexpr explicit latch(std::ptrdiff_t expected) noexcept : mCounter{expected}
{
assert(expected >= 0);
}
~latch()=default;
latch(const latch&)=delete;
latch& operator=(const latch&)=delete;
void count_down(std::ptrdiff_t update = 1)
{
assert(update >= 0);
const auto current = mCounter.fetch_sub(update, std::memory_order_release);
assert(update <= current);
if (current <= update)
{
mCounter.notify_all();
}
}
[[nodiscard]] bool try_wait() const noexcept
{
return mCounter.load(std::memory_order_acquire) <= 0;
}
/**
* The call to atomic::wait() may unblock with a non-zero counter in some edge cases (see GH-91 for an in-depth explanation)
* To address the edge cases this loop will continue to wait until the counter has been verified to have reached 0 (or less)
*/
void wait() const
{
while (true)
{
const auto current = mCounter.load(std::memory_order_acquire);
if (current <= 0)
{
return;
}
mCounter.wait(current, std::memory_order_relaxed);
}
}
void arrive_and_wait(const std::ptrdiff_t update = 1) noexcept
{
assert(update >= 0);
const auto current = mCounter.fetch_sub(update, std::memory_order_acq_rel);
assert(current <= update);
if (current == update)
{
mCounter.notify_all();
}
else
{
assert(current > 0);
mCounter.wait(current - update, std::memory_order_relaxed);
wait();
}
}
private:
std::atomic<std::ptrdiff_t> mCounter;
};
} // Namespace mingw_stdthread
namespace std
{
using mingw_stdthread::latch;
} // Namespace std
#endif // MINGW_LATCH_H_