Skip to content

Commit 251f2b2

Browse files
committed
Add Subscription handle class
Needed for Subscription IPC calls
1 parent 1eb858d commit 251f2b2

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#ifndef GGL_IPC_SUBSCRIPTION_HPP
2+
#define GGL_IPC_SUBSCRIPTION_HPP
3+
4+
#include <ggl/error.hpp>
5+
#include <ggl/ipc/client_c_api.hpp>
6+
#include <compare>
7+
#include <functional>
8+
#include <utility>
9+
10+
extern "C" {
11+
void ggipc_close_subscription(GgIpcSubscriptionHandle handle) noexcept;
12+
}
13+
14+
namespace ggl::ipc {
15+
16+
/// Holds a bound function associated with a subscription. Instances of
17+
/// SubscriptionCallback must outlive their associated subscription(s) (i.e.
18+
/// close() on the Subscription must be called before destroying its
19+
/// SubscriptionCallback). The subscription callback function for a bound
20+
/// subscription is called by exactly one other thread.
21+
class SubscriptionCallback {
22+
private:
23+
/// TODO:
24+
std::function<void(void)> callback;
25+
26+
public:
27+
/// TODO:
28+
};
29+
30+
/// Subscription handle with std::unique_ptr semantics. The underlying
31+
/// handle has two copies; one is returned by a Client subscription method, and
32+
/// another is passed to SubscriptionCallbacks, which are called by a
33+
/// library-controlled thread. Either copy of the handle can terminate the
34+
/// underlying subscription.
35+
class [[nodiscard]] Subscription {
36+
private:
37+
GgIpcSubscriptionHandle handle {};
38+
39+
public:
40+
/// A default-constructed Subscription is guaranteed to refer to no
41+
/// subscription at all.
42+
constexpr Subscription() noexcept = default;
43+
44+
explicit constexpr Subscription(
45+
GgIpcSubscriptionHandle subscription_handle
46+
) noexcept
47+
: handle { subscription_handle } {
48+
}
49+
50+
/// Moved Subscriptions are guaranteed afterwards to refer to no
51+
/// subscription.
52+
constexpr Subscription(Subscription &&subscription) noexcept
53+
: handle { subscription.release() } {
54+
}
55+
56+
Subscription &operator=(Subscription &&subscription) noexcept {
57+
// Guard against self-moves
58+
if (subscription != *this) {
59+
reset(subscription.release());
60+
}
61+
return *this;
62+
}
63+
64+
Subscription &operator=(const Subscription &) = delete;
65+
Subscription(const Subscription &) = delete;
66+
67+
~Subscription() noexcept {
68+
close();
69+
}
70+
71+
/// Returns true if Subscription may refer to an active subscription.
72+
/// Only semantically meaningful to detect default/moved values.
73+
/// Does not detect whether another thread calling close() on its copy of
74+
/// this handle (i.e. calling close() on a SubscriptionCallback's
75+
/// Subscription& parameter).
76+
constexpr bool holds_subscription() const noexcept {
77+
return handle.val != 0;
78+
}
79+
80+
/// Copies the current raw handle. Use only for hashing.
81+
constexpr GgIpcSubscriptionHandle get() const noexcept {
82+
return handle;
83+
}
84+
85+
/// Relinquish ownership of handle without closing it.
86+
[[nodiscard]]
87+
constexpr GgIpcSubscriptionHandle release() noexcept {
88+
return std::exchange(handle, GgIpcSubscriptionHandle {});
89+
}
90+
91+
/// closes current subscription and replaces it with a new handle.
92+
void reset(GgIpcSubscriptionHandle new_handle) noexcept {
93+
close();
94+
handle = new_handle;
95+
}
96+
97+
/// Swap handles
98+
constexpr void swap(Subscription &other) noexcept {
99+
std::swap(handle, other.handle);
100+
}
101+
102+
/// Non-member swap
103+
friend constexpr void swap(Subscription &lhs, Subscription &rhs) noexcept {
104+
lhs.swap(rhs);
105+
}
106+
107+
/// Same as holds_subscription()
108+
constexpr operator bool() const noexcept {
109+
return holds_subscription();
110+
}
111+
112+
/// Sends a terminate stream request to the IPC server. After this function
113+
/// returns, the Subscription's associated SubscriptionCallback (if any) is
114+
/// no longer called for messages to this Subscription after this method
115+
/// returns. Threadsafe and reentrant. Valid even if Subscription is moved /
116+
/// default-constructed. Valid to call from within a SubscriptionCallback
117+
/// handler, but such a call is not visible to other threads until they call
118+
/// close as well.
119+
void close() noexcept {
120+
// This check avoids locking a mutex when Subscription is
121+
// default-initialized
122+
if (holds_subscription()) {
123+
ggipc_close_subscription(release());
124+
}
125+
}
126+
127+
/// Comparisons for associative containers.
128+
constexpr std::strong_ordering operator<=>(
129+
const Subscription &other
130+
) const noexcept {
131+
return handle.val <=> other.handle.val;
132+
}
133+
};
134+
135+
}
136+
137+
/// Subscription hashing
138+
template <> class std::hash<ggl::ipc::Subscription> {
139+
public:
140+
constexpr std::size_t operator()(
141+
const ggl::ipc::Subscription &subscription
142+
) const noexcept {
143+
return std::hash<std::uint32_t> {}(subscription.get().val);
144+
}
145+
};
146+
147+
#endif

0 commit comments

Comments
 (0)