Skip to content

Commit 131d9ff

Browse files
authored
Store raw pointer in udp::peer and implement copy/move-assignment (#403)
* Store raw pointer in udp::peer and implement copy/move-assignment Previously the peer struct held a reference member that could not be reseated and so the defaulted copy/move-assignments were implicitly deleted. We want these objects to be moveable and copyable so instead store a raw pointer. * Remove potential for false-positive fail in task_container test Since we detach our (fast to run) task, it might complete on the thread_pool thread before we get time-sliced in to check its size on the main thread, leading to a false positive failure in that the container is already empty. To fix this, use an event to ensure the task does not complete until we've checked the container's size.
1 parent ae8f34e commit 131d9ff

File tree

3 files changed

+60
-19
lines changed

3 files changed

+60
-19
lines changed

include/coro/net/udp/peer.hpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
#include <chrono>
1212
#include <span>
13-
#include <variant>
1413

1514
namespace coro
1615
{
@@ -43,11 +42,11 @@ class peer
4342
*/
4443
explicit peer(std::unique_ptr<coro::io_scheduler>& scheduler, const info& bind_info);
4544

46-
peer(const peer&) = default;
47-
peer(peer&&) = default;
48-
auto operator=(const peer&) noexcept -> peer& = default;
49-
auto operator=(peer&&) noexcept -> peer& = default;
50-
~peer() = default;
45+
peer(const peer&) noexcept = default;
46+
peer(peer&&) noexcept;
47+
auto operator=(const peer&) noexcept -> peer&;
48+
auto operator=(peer&&) noexcept -> peer&;
49+
~peer() = default;
5150

5251
/**
5352
* @return A reference to the underlying socket.
@@ -147,7 +146,7 @@ class peer
147146

148147
private:
149148
/// The scheduler that will drive this udp client.
150-
std::unique_ptr<coro::io_scheduler>& m_io_scheduler;
149+
coro::io_scheduler* m_io_scheduler;
151150
/// The udp socket.
152151
net::socket m_socket{-1};
153152
/// Did the user request this udp socket is bound locally to receive packets?

src/net/udp/peer.cpp

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,60 @@
11
#include "coro/net/udp/peer.hpp"
2+
#include <memory>
3+
#include <utility>
24

35
namespace coro::net::udp
46
{
57
peer::peer(std::unique_ptr<coro::io_scheduler>& scheduler, net::domain_t domain)
6-
: m_io_scheduler(scheduler),
8+
: m_io_scheduler(scheduler.get()),
79
m_socket(net::make_socket(net::socket::options{domain, net::socket::type_t::udp, net::socket::blocking_t::no}))
810
{
11+
if (m_io_scheduler == nullptr)
12+
{
13+
throw std::runtime_error("udp::peer cannot have nullptr scheduler");
14+
}
915
}
1016

1117
peer::peer(std::unique_ptr<coro::io_scheduler>& scheduler, const info& bind_info)
12-
: m_io_scheduler(scheduler),
13-
m_socket(net::make_accept_socket(
14-
net::socket::options{bind_info.address.domain(), net::socket::type_t::udp, net::socket::blocking_t::no},
15-
bind_info.address,
16-
bind_info.port)),
18+
: m_io_scheduler(scheduler.get()),
19+
m_socket(
20+
net::make_accept_socket(
21+
net::socket::options{bind_info.address.domain(), net::socket::type_t::udp, net::socket::blocking_t::no},
22+
bind_info.address,
23+
bind_info.port)),
1724
m_bound(true)
1825
{
26+
if (m_io_scheduler == nullptr)
27+
{
28+
throw std::runtime_error("udp::peer cannot have nullptr scheduler");
29+
}
1930
}
2031

32+
peer::peer(peer&& other) noexcept
33+
: m_io_scheduler(std::exchange(other.m_io_scheduler, nullptr)),
34+
m_socket(std::move(other.m_socket)),
35+
m_bound(other.m_bound)
36+
{
37+
}
38+
39+
auto peer::operator=(peer&& other) noexcept -> peer&
40+
{
41+
if (std::addressof(other) != this)
42+
{
43+
m_io_scheduler = std::exchange(other.m_io_scheduler, nullptr);
44+
m_socket = std::move(other.m_socket);
45+
m_bound = other.m_bound;
46+
}
47+
return *this;
48+
}
49+
50+
auto peer::operator=(const peer& other) noexcept -> peer&
51+
{
52+
if (std::addressof(other) != this)
53+
{
54+
m_io_scheduler = other.m_io_scheduler;
55+
m_socket = other.m_socket;
56+
m_bound = other.m_bound;
57+
}
58+
return *this;
59+
}
2160
} // namespace coro::net::udp

test/test_task_container.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,22 @@ using namespace std::chrono_literals;
1414

1515
TEST_CASE("task_container schedule single task", "[task_container]")
1616
{
17-
auto s = coro::thread_pool::make_unique(
18-
coro::thread_pool::options{.thread_count = 1});
19-
20-
int value = 0;
21-
auto make_task = [] (int &value) -> coro::task<void>
17+
auto s = coro::thread_pool::make_unique(coro::thread_pool::options{.thread_count = 1});
18+
coro::event can_start{false};
19+
int value = 0;
20+
auto make_task = [](int& value, coro::event& can_start) -> coro::task<void>
2221
{
22+
co_await can_start;
2323
value = 37;
2424
co_return;
2525
};
2626

2727
coro::task_container<coro::thread_pool> tc{s};
28-
tc.start(make_task(value));
28+
tc.start(make_task(value, can_start));
2929
REQUIRE(tc.size() == 1);
30+
// Only allow the task to complete once we have checked the container's size,
31+
// otherwise the require could "spuriously" fail due to the task already being done.
32+
can_start.set(s);
3033
coro::sync_wait(tc.yield_until_empty());
3134
REQUIRE(value == 37);
3235
REQUIRE(tc.empty());

0 commit comments

Comments
 (0)