include/boost/corosio/local_datagram_socket.hpp

92.2% Lines (47/51) 100.0% List of functions (19/19)
local_datagram_socket.hpp
f(x) Functions (19)
Function Calls Lines Blocks
boost::corosio::local_datagram_socket::send_to_awaitable::send_to_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, boost::corosio::local_endpoint, int) :315 6x 100.0% 100.0% boost::corosio::local_datagram_socket::send_to_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :320 6x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_from_awaitable::recv_from_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, boost::corosio::local_endpoint&, int) :341 8x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_from_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :346 8x 100.0% 80.0% boost::corosio::local_datagram_socket::send_awaitable::send_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, int) :407 8x 100.0% 100.0% boost::corosio::local_datagram_socket::send_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :412 8x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_awaitable::recv_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, int) :432 10x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :437 10x 100.0% 80.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::local_datagram_socket&&) :479 14x 100.0% 100.0% boost::corosio::local_datagram_socket::is_open() const :530 142x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send_to<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::local_endpoint, boost::corosio::message_flags) :614 6x 75.0% 86.0% auto boost::corosio::local_datagram_socket::send_to<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::local_endpoint) :627 6x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv_from<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::local_endpoint&, boost::corosio::message_flags) :654 8x 75.0% 86.0% auto boost::corosio::local_datagram_socket::recv_from<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::local_endpoint&) :667 6x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::message_flags) :688 8x 75.0% 86.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&) :698 8x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::message_flags) :719 10x 75.0% 86.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&) :729 8x 100.0% 100.0% boost::corosio::local_datagram_socket::get() const :873 158x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
11 #define BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
12
13 #include <boost/corosio/detail/config.hpp>
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_POSIX
17
18 #include <boost/corosio/detail/except.hpp>
19 #include <boost/corosio/detail/native_handle.hpp>
20 #include <boost/corosio/detail/op_base.hpp>
21 #include <boost/corosio/io/io_object.hpp>
22 #include <boost/capy/io_result.hpp>
23 #include <boost/corosio/detail/buffer_param.hpp>
24 #include <boost/corosio/local_endpoint.hpp>
25 #include <boost/corosio/local_datagram.hpp>
26 #include <boost/corosio/message_flags.hpp>
27 #include <boost/corosio/shutdown_type.hpp>
28 #include <boost/corosio/wait_type.hpp>
29 #include <boost/capy/ex/executor_ref.hpp>
30 #include <boost/capy/ex/execution_context.hpp>
31 #include <boost/capy/ex/io_env.hpp>
32 #include <boost/capy/concept/executor.hpp>
33
34 #include <system_error>
35
36 #include <concepts>
37 #include <coroutine>
38 #include <cstddef>
39 #include <stop_token>
40 #include <type_traits>
41
42 namespace boost::corosio {
43
44 /** An asynchronous Unix datagram socket for coroutine I/O.
45
46 This class provides asynchronous Unix domain datagram socket
47 operations that return awaitable types. Each operation
48 participates in the affine awaitable protocol, ensuring
49 coroutines resume on the correct executor.
50
51 Supports two modes of operation:
52
53 @li **Connectionless:** each send_to() specifies a destination
54 endpoint, and each recv_from() captures the source. The
55 socket must be opened (and optionally bound) before I/O.
56
57 @li **Connected:** call connect() to set a default peer,
58 then use send()/recv() without endpoint arguments. The
59 kernel filters incoming datagrams to those from the
60 connected peer.
61
62 @note Not available on Windows. Windows does not support
63 AF_UNIX datagram sockets (SOCK_DGRAM). Attempting to
64 open this socket on Windows will fail.
65
66 @par Cancellation
67 All asynchronous operations support cancellation through
68 `std::stop_token` via the affine protocol, or explicitly
69 through cancel(). Cancelled operations complete with
70 `capy::cond::canceled`. Datagram sends and receives are
71 atomic — there is no partial progress on cancellation.
72
73 @par Thread Safety
74 Distinct objects: Safe.@n
75 Shared objects: Unsafe. A socket must not have concurrent
76 operations of the same type (e.g., two simultaneous
77 recv_from). One send and one recv may be in flight
78 simultaneously. Note that recv and recv_from share the
79 same internal read slot, so they must not overlap; likewise
80 send and send_to share the write slot.
81
82 @par Example
83 @code
84 // Connectionless
85 local_datagram_socket sender(ioc);
86 sender.open();
87 sender.bind(local_endpoint("/tmp/sender.sock"));
88 auto [ec, n] = co_await sender.send_to(
89 capy::const_buffer("hello", 5),
90 local_endpoint("/tmp/receiver.sock"));
91
92 // Connected
93 local_datagram_socket sock(ioc);
94 co_await sock.connect(local_endpoint("/tmp/peer.sock"));
95 auto [ec2, n2] = co_await sock.send(
96 capy::const_buffer("hi", 2));
97 @endcode
98 */
99 class BOOST_COROSIO_DECL local_datagram_socket : public io_object
100 {
101 public:
102 /// The shutdown direction type used by shutdown().
103 using shutdown_type = corosio::shutdown_type;
104 using enum corosio::shutdown_type;
105
106 /** Define backend hooks for local datagram socket operations.
107
108 Platform backends (epoll, kqueue, select) derive from this
109 to implement datagram I/O, connection, and option management.
110 */
111 struct implementation : io_object::implementation
112 {
113 /** Initiate an asynchronous send_to operation.
114
115 @param h Coroutine handle to resume on completion.
116 @param ex Executor for dispatching the completion.
117 @param buf The buffer data to send.
118 @param dest The destination endpoint.
119 @param token Stop token for cancellation.
120 @param ec Output error code.
121 @param bytes_out Output bytes transferred.
122
123 @return Coroutine handle to resume immediately.
124 */
125 virtual std::coroutine_handle<> send_to(
126 std::coroutine_handle<> h,
127 capy::executor_ref ex,
128 buffer_param buf,
129 corosio::local_endpoint dest,
130 int flags,
131 std::stop_token token,
132 std::error_code* ec,
133 std::size_t* bytes_out) = 0;
134
135 /** Initiate an asynchronous recv_from operation.
136
137 @param h Coroutine handle to resume on completion.
138 @param ex Executor for dispatching the completion.
139 @param buf The buffer to receive into.
140 @param source Output endpoint for the sender's address.
141 @param token Stop token for cancellation.
142 @param ec Output error code.
143 @param bytes_out Output bytes transferred.
144
145 @return Coroutine handle to resume immediately.
146 */
147 virtual std::coroutine_handle<> recv_from(
148 std::coroutine_handle<> h,
149 capy::executor_ref ex,
150 buffer_param buf,
151 corosio::local_endpoint* source,
152 int flags,
153 std::stop_token token,
154 std::error_code* ec,
155 std::size_t* bytes_out) = 0;
156
157 /** Initiate an asynchronous connect to set the default peer.
158
159 @param h Coroutine handle to resume on completion.
160 @param ex Executor for dispatching the completion.
161 @param ep The remote endpoint to connect to.
162 @param token Stop token for cancellation.
163 @param ec Output error code.
164
165 @return Coroutine handle to resume immediately.
166 */
167 virtual std::coroutine_handle<> connect(
168 std::coroutine_handle<> h,
169 capy::executor_ref ex,
170 corosio::local_endpoint ep,
171 std::stop_token token,
172 std::error_code* ec) = 0;
173
174 /** Initiate an asynchronous connected send operation.
175
176 @param h Coroutine handle to resume on completion.
177 @param ex Executor for dispatching the completion.
178 @param buf The buffer data to send.
179 @param token Stop token for cancellation.
180 @param ec Output error code.
181 @param bytes_out Output bytes transferred.
182
183 @return Coroutine handle to resume immediately.
184 */
185 virtual std::coroutine_handle<> send(
186 std::coroutine_handle<> h,
187 capy::executor_ref ex,
188 buffer_param buf,
189 int flags,
190 std::stop_token token,
191 std::error_code* ec,
192 std::size_t* bytes_out) = 0;
193
194 /** Initiate an asynchronous connected recv operation.
195
196 @param h Coroutine handle to resume on completion.
197 @param ex Executor for dispatching the completion.
198 @param buf The buffer to receive into.
199 @param flags Message flags (e.g. MSG_PEEK).
200 @param token Stop token for cancellation.
201 @param ec Output error code.
202 @param bytes_out Output bytes transferred.
203
204 @return Coroutine handle to resume immediately.
205 */
206 virtual std::coroutine_handle<> recv(
207 std::coroutine_handle<> h,
208 capy::executor_ref ex,
209 buffer_param buf,
210 int flags,
211 std::stop_token token,
212 std::error_code* ec,
213 std::size_t* bytes_out) = 0;
214
215 /** Initiate an asynchronous wait for socket readiness.
216
217 Completes when the socket becomes ready for the
218 specified direction, or an error condition is
219 reported. No bytes are transferred.
220
221 @param h Coroutine handle to resume on completion.
222 @param ex Executor for dispatching the completion.
223 @param w The direction to wait on.
224 @param token Stop token for cancellation.
225 @param ec Output error code.
226
227 @return Coroutine handle to resume immediately.
228 */
229 virtual std::coroutine_handle<> wait(
230 std::coroutine_handle<> h,
231 capy::executor_ref ex,
232 wait_type w,
233 std::stop_token token,
234 std::error_code* ec) = 0;
235
236 /// Shut down part or all of the socket.
237 virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
238
239 /// Return the platform socket descriptor.
240 virtual native_handle_type native_handle() const noexcept = 0;
241
242 /** Release ownership of the socket descriptor.
243
244 The implementation deregisters from the reactor and cancels
245 pending operations. The caller takes ownership of the
246 returned descriptor.
247
248 @return The native handle, or an invalid sentinel if
249 not open.
250 */
251 virtual native_handle_type release_socket() noexcept = 0;
252
253 /** Request cancellation of pending asynchronous operations.
254
255 All outstanding operations complete with operation_canceled
256 error. Check ec == cond::canceled for portable comparison.
257 */
258 virtual void cancel() noexcept = 0;
259
260 /** Set a socket option.
261
262 @param level The protocol level (e.g. SOL_SOCKET).
263 @param optname The option name.
264 @param data Pointer to the option value.
265 @param size Size of the option value in bytes.
266 @return Error code on failure, empty on success.
267 */
268 virtual std::error_code set_option(
269 int level,
270 int optname,
271 void const* data,
272 std::size_t size) noexcept = 0;
273
274 /** Get a socket option.
275
276 @param level The protocol level (e.g. SOL_SOCKET).
277 @param optname The option name.
278 @param data Pointer to receive the option value.
279 @param size On entry, the size of the buffer. On exit,
280 the size of the option value.
281 @return Error code on failure, empty on success.
282 */
283 virtual std::error_code
284 get_option(int level, int optname, void* data, std::size_t* size)
285 const noexcept = 0;
286
287 /// Return the cached local endpoint.
288 virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
289
290 /// Return the cached remote endpoint (connected mode).
291 virtual corosio::local_endpoint remote_endpoint() const noexcept = 0;
292
293 /** Bind the socket to a local endpoint.
294
295 @param ep The local endpoint to bind to.
296 @return Error code on failure, empty on success.
297 */
298 virtual std::error_code
299 bind(corosio::local_endpoint ep) noexcept = 0;
300 };
301
302 /** Represent the awaitable returned by @ref send_to.
303
304 Captures the destination endpoint and buffer, then dispatches
305 to the backend implementation on suspension.
306 */
307 struct send_to_awaitable
308 : detail::bytes_op_base<send_to_awaitable>
309 {
310 local_datagram_socket& s_;
311 buffer_param buf_;
312 corosio::local_endpoint dest_;
313 int flags_;
314
315 6x send_to_awaitable(
316 local_datagram_socket& s, buffer_param buf,
317 corosio::local_endpoint dest, int flags = 0) noexcept
318 6x : s_(s), buf_(buf), dest_(dest), flags_(flags) {}
319
320 6x std::coroutine_handle<> dispatch(
321 std::coroutine_handle<> h, capy::executor_ref ex) const
322 {
323 12x return s_.get().send_to(
324 12x h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
325 }
326 };
327
328 /** Represent the awaitable returned by @ref recv_from.
329
330 Captures the source endpoint reference and buffer, then
331 dispatches to the backend implementation on suspension.
332 */
333 struct recv_from_awaitable
334 : detail::bytes_op_base<recv_from_awaitable>
335 {
336 local_datagram_socket& s_;
337 buffer_param buf_;
338 corosio::local_endpoint& source_;
339 int flags_;
340
341 8x recv_from_awaitable(
342 local_datagram_socket& s, buffer_param buf,
343 corosio::local_endpoint& source, int flags = 0) noexcept
344 8x : s_(s), buf_(buf), source_(source), flags_(flags) {}
345
346 8x std::coroutine_handle<> dispatch(
347 std::coroutine_handle<> h, capy::executor_ref ex) const
348 {
349 16x return s_.get().recv_from(
350 16x h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
351 }
352 };
353
354 /** Represent the awaitable returned by @ref connect.
355
356 Captures the target endpoint, then dispatches to the
357 backend implementation on suspension.
358 */
359 struct connect_awaitable
360 : detail::void_op_base<connect_awaitable>
361 {
362 local_datagram_socket& s_;
363 corosio::local_endpoint endpoint_;
364
365 connect_awaitable(
366 local_datagram_socket& s,
367 corosio::local_endpoint ep) noexcept
368 : s_(s), endpoint_(ep) {}
369
370 std::coroutine_handle<> dispatch(
371 std::coroutine_handle<> h, capy::executor_ref ex) const
372 {
373 return s_.get().connect(
374 h, ex, endpoint_, token_, &ec_);
375 }
376 };
377
378 /// Represent the awaitable returned by @ref wait.
379 struct wait_awaitable
380 : detail::void_op_base<wait_awaitable>
381 {
382 local_datagram_socket& s_;
383 wait_type w_;
384
385 wait_awaitable(local_datagram_socket& s, wait_type w) noexcept
386 : s_(s), w_(w) {}
387
388 std::coroutine_handle<> dispatch(
389 std::coroutine_handle<> h, capy::executor_ref ex) const
390 {
391 return s_.get().wait(h, ex, w_, token_, &ec_);
392 }
393 };
394
395 /** Represent the awaitable returned by @ref send.
396
397 Captures the buffer, then dispatches to the backend
398 implementation on suspension. Requires a prior connect().
399 */
400 struct send_awaitable
401 : detail::bytes_op_base<send_awaitable>
402 {
403 local_datagram_socket& s_;
404 buffer_param buf_;
405 int flags_;
406
407 8x send_awaitable(
408 local_datagram_socket& s, buffer_param buf,
409 int flags = 0) noexcept
410 8x : s_(s), buf_(buf), flags_(flags) {}
411
412 8x std::coroutine_handle<> dispatch(
413 std::coroutine_handle<> h, capy::executor_ref ex) const
414 {
415 16x return s_.get().send(
416 16x h, ex, buf_, flags_, token_, &ec_, &bytes_);
417 }
418 };
419
420 /** Represent the awaitable returned by @ref recv.
421
422 Captures the buffer, then dispatches to the backend
423 implementation on suspension. Requires a prior connect().
424 */
425 struct recv_awaitable
426 : detail::bytes_op_base<recv_awaitable>
427 {
428 local_datagram_socket& s_;
429 buffer_param buf_;
430 int flags_;
431
432 10x recv_awaitable(
433 local_datagram_socket& s, buffer_param buf,
434 int flags = 0) noexcept
435 10x : s_(s), buf_(buf), flags_(flags) {}
436
437 10x std::coroutine_handle<> dispatch(
438 std::coroutine_handle<> h, capy::executor_ref ex) const
439 {
440 20x return s_.get().recv(
441 20x h, ex, buf_, flags_, token_, &ec_, &bytes_);
442 }
443 };
444
445 public:
446 /** Destructor.
447
448 Closes the socket if open, cancelling any pending operations.
449 */
450 ~local_datagram_socket() override;
451
452 /** Construct a socket from an execution context.
453
454 @param ctx The execution context that will own this socket.
455 */
456 explicit local_datagram_socket(capy::execution_context& ctx);
457
458 /** Construct a socket from an executor.
459
460 The socket is associated with the executor's context.
461
462 @param ex The executor whose context will own the socket.
463 */
464 template<class Ex>
465 requires(
466 !std::same_as<std::remove_cvref_t<Ex>, local_datagram_socket>) &&
467 capy::Executor<Ex>
468 explicit local_datagram_socket(Ex const& ex)
469 : local_datagram_socket(ex.context())
470 {
471 }
472
473 /** Move constructor.
474
475 Transfers ownership of the socket resources.
476
477 @param other The socket to move from.
478 */
479 14x local_datagram_socket(local_datagram_socket&& other) noexcept
480 14x : io_object(std::move(other))
481 {
482 14x }
483
484 /** Move assignment operator.
485
486 Closes any existing socket and transfers ownership.
487
488 @param other The socket to move from.
489 @return Reference to this socket.
490 */
491 local_datagram_socket& operator=(local_datagram_socket&& other) noexcept
492 {
493 if (this != &other)
494 {
495 close();
496 io_object::operator=(std::move(other));
497 }
498 return *this;
499 }
500
501 local_datagram_socket(local_datagram_socket const&) = delete;
502 local_datagram_socket& operator=(local_datagram_socket const&) = delete;
503
504 /** Open the socket.
505
506 Creates a Unix datagram socket and associates it with
507 the platform reactor.
508
509 @param proto The protocol. Defaults to local_datagram{}.
510
511 @throws std::system_error on failure.
512 */
513 void open(local_datagram proto = {});
514
515 /** Close the socket.
516
517 Cancels any pending asynchronous operations and releases
518 the underlying file descriptor. Has no effect if the
519 socket is not open.
520
521 @post is_open() == false
522 */
523 void close();
524
525 /** Check if the socket is open.
526
527 @return `true` if the socket holds a valid file descriptor,
528 `false` otherwise.
529 */
530 142x bool is_open() const noexcept
531 {
532 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
533 return h_ && get().native_handle() != ~native_handle_type(0);
534 #else
535 142x return h_ && get().native_handle() >= 0;
536 #endif
537 }
538
539 /** Bind the socket to a local endpoint.
540
541 Associates the socket with a local address (filesystem path).
542 Required before calling recv_from in connectionless mode.
543
544 @param ep The local endpoint to bind to.
545
546 @return Error code on failure, empty on success.
547
548 @throws std::logic_error if the socket is not open.
549 */
550 std::error_code bind(corosio::local_endpoint ep);
551
552 /** Initiate an asynchronous connect to set the default peer.
553
554 If the socket is not already open, it is opened automatically.
555 After successful completion, send()/recv() may be used
556 without specifying an endpoint.
557
558 @param ep The remote endpoint to connect to.
559
560 @par Cancellation
561 Supports cancellation via the awaitable's stop_token or by
562 calling cancel(). On cancellation, yields
563 `capy::cond::canceled`.
564
565 @return An awaitable that completes with io_result<>.
566
567 @throws std::system_error if the socket needs to be opened
568 and the open fails.
569 */
570 auto connect(corosio::local_endpoint ep)
571 {
572 if (!is_open())
573 open();
574 return connect_awaitable(*this, ep);
575 }
576
577 /** Wait for the socket to become ready in a given direction.
578
579 Suspends until the socket is ready for the requested
580 direction, or an error condition is reported. No bytes
581 are transferred.
582
583 @param w The wait direction (read, write, or error).
584
585 @return An awaitable that completes with `io_result<>`.
586
587 @par Preconditions
588 The socket must be open. This socket must outlive the
589 returned awaitable.
590 */
591 [[nodiscard]] auto wait(wait_type w)
592 {
593 return wait_awaitable(*this, w);
594 }
595
596 /** Send a datagram to the specified destination.
597
598 Completes when the entire datagram has been accepted
599 by the kernel. The bytes_transferred value equals the
600 datagram size on success.
601
602 @param buf The buffer containing data to send.
603 @param dest The destination endpoint.
604
605 @par Cancellation
606 Supports cancellation via stop_token or cancel().
607
608 @return An awaitable that completes with
609 io_result<std::size_t>.
610
611 @throws std::logic_error if the socket is not open.
612 */
613 template<capy::ConstBufferSequence Buffers>
614 6x auto send_to(
615 Buffers const& buf,
616 corosio::local_endpoint dest,
617 corosio::message_flags flags)
618 {
619 6x if (!is_open())
620 detail::throw_logic_error("send_to: socket not open");
621 return send_to_awaitable(
622 6x *this, buf, dest, static_cast<int>(flags));
623 }
624
625 /// @overload
626 template<capy::ConstBufferSequence Buffers>
627 6x auto send_to(Buffers const& buf, corosio::local_endpoint dest)
628 {
629 6x return send_to(buf, dest, corosio::message_flags::none);
630 }
631
632 /** Receive a datagram and capture the sender's endpoint.
633
634 Completes when one datagram has been received. The
635 bytes_transferred value is the number of bytes copied
636 into the buffer. If the buffer is smaller than the
637 datagram, excess bytes are discarded (datagram
638 semantics).
639
640 @param buf The buffer to receive data into.
641 @param source Reference to an endpoint that will be set to
642 the sender's address on successful completion.
643 @param flags Message flags (e.g. message_flags::peek).
644
645 @par Cancellation
646 Supports cancellation via stop_token or cancel().
647
648 @return An awaitable that completes with
649 io_result<std::size_t>.
650
651 @throws std::logic_error if the socket is not open.
652 */
653 template<capy::MutableBufferSequence Buffers>
654 8x auto recv_from(
655 Buffers const& buf,
656 corosio::local_endpoint& source,
657 corosio::message_flags flags)
658 {
659 8x if (!is_open())
660 detail::throw_logic_error("recv_from: socket not open");
661 return recv_from_awaitable(
662 8x *this, buf, source, static_cast<int>(flags));
663 }
664
665 /// @overload
666 template<capy::MutableBufferSequence Buffers>
667 6x auto recv_from(Buffers const& buf, corosio::local_endpoint& source)
668 {
669 6x return recv_from(buf, source, corosio::message_flags::none);
670 }
671
672 /** Send a datagram to the connected peer.
673
674 @pre connect() has been called successfully.
675
676 @param buf The buffer containing data to send.
677 @param flags Message flags.
678
679 @par Cancellation
680 Supports cancellation via stop_token or cancel().
681
682 @return An awaitable that completes with
683 io_result<std::size_t>.
684
685 @throws std::logic_error if the socket is not open.
686 */
687 template<capy::ConstBufferSequence Buffers>
688 8x auto send(Buffers const& buf, corosio::message_flags flags)
689 {
690 8x if (!is_open())
691 detail::throw_logic_error("send: socket not open");
692 return send_awaitable(
693 8x *this, buf, static_cast<int>(flags));
694 }
695
696 /// @overload
697 template<capy::ConstBufferSequence Buffers>
698 8x auto send(Buffers const& buf)
699 {
700 8x return send(buf, corosio::message_flags::none);
701 }
702
703 /** Receive a datagram from the connected peer.
704
705 @pre connect() has been called successfully.
706
707 @param buf The buffer to receive data into.
708 @param flags Message flags (e.g. message_flags::peek).
709
710 @par Cancellation
711 Supports cancellation via stop_token or cancel().
712
713 @return An awaitable that completes with
714 io_result<std::size_t>.
715
716 @throws std::logic_error if the socket is not open.
717 */
718 template<capy::MutableBufferSequence Buffers>
719 10x auto recv(Buffers const& buf, corosio::message_flags flags)
720 {
721 10x if (!is_open())
722 detail::throw_logic_error("recv: socket not open");
723 return recv_awaitable(
724 10x *this, buf, static_cast<int>(flags));
725 }
726
727 /// @overload
728 template<capy::MutableBufferSequence Buffers>
729 8x auto recv(Buffers const& buf)
730 {
731 8x return recv(buf, corosio::message_flags::none);
732 }
733
734 /** Cancel any pending asynchronous operations.
735
736 All outstanding operations complete with
737 errc::operation_canceled. Check ec == cond::canceled
738 for portable comparison.
739 */
740 void cancel();
741
742 /** Get the native socket handle.
743
744 @return The native socket handle, or -1 if not open.
745 */
746 native_handle_type native_handle() const noexcept;
747
748 /** Release ownership of the native socket handle.
749
750 Deregisters the socket from the reactor and cancels pending
751 operations without closing the fd. The caller takes ownership
752 of the returned descriptor.
753
754 @return The native handle, or -1 if not open.
755
756 @throws std::logic_error if the socket is not open.
757 */
758 native_handle_type release();
759
760 /** Query the number of bytes available for reading.
761
762 @return The number of bytes that can be read without blocking.
763
764 @throws std::logic_error if the socket is not open.
765 @throws std::system_error on ioctl failure.
766 */
767 std::size_t available() const;
768
769 /** Shut down part or all of the socket.
770
771 @param what Which direction to shut down.
772
773 @throws std::system_error on failure.
774 */
775 void shutdown(shutdown_type what);
776
777 /** Shut down part or all of the socket (non-throwing).
778
779 @param what Which direction to shut down.
780 @param ec Set to the error code on failure.
781 */
782 void shutdown(shutdown_type what, std::error_code& ec) noexcept;
783
784 /** Set a socket option.
785
786 @tparam Option A socket option type that provides static
787 `level()` and `name()` members, and `data()` / `size()`
788 accessors for the option value.
789
790 @param opt The option to set.
791
792 @throws std::logic_error if the socket is not open.
793 @throws std::system_error on failure.
794 */
795 template<class Option>
796 void set_option(Option const& opt)
797 {
798 if (!is_open())
799 detail::throw_logic_error("set_option: socket not open");
800 std::error_code ec = get().set_option(
801 Option::level(), Option::name(), opt.data(), opt.size());
802 if (ec)
803 detail::throw_system_error(
804 ec, "local_datagram_socket::set_option");
805 }
806
807 /** Get a socket option.
808
809 @tparam Option A socket option type that provides static
810 `level()` and `name()` members, `data()` / `size()`
811 accessors, and a `resize()` member.
812
813 @return The current option value.
814
815 @throws std::logic_error if the socket is not open.
816 @throws std::system_error on failure.
817 */
818 template<class Option>
819 Option get_option() const
820 {
821 if (!is_open())
822 detail::throw_logic_error("get_option: socket not open");
823 Option opt{};
824 std::size_t sz = opt.size();
825 std::error_code ec =
826 get().get_option(Option::level(), Option::name(), opt.data(), &sz);
827 if (ec)
828 detail::throw_system_error(
829 ec, "local_datagram_socket::get_option");
830 opt.resize(sz);
831 return opt;
832 }
833
834 /** Assign an existing file descriptor to this socket.
835
836 The socket must not already be open. The fd is adopted
837 and registered with the platform reactor.
838
839 @param fd The file descriptor to adopt.
840
841 @throws std::system_error on failure.
842 */
843 void assign(native_handle_type fd);
844
845 /** Get the local endpoint of the socket.
846
847 @return The local endpoint, or a default endpoint if not bound.
848 */
849 corosio::local_endpoint local_endpoint() const noexcept;
850
851 /** Get the remote endpoint of the socket.
852
853 Returns the address of the connected peer.
854
855 @return The remote endpoint, or a default endpoint if
856 not connected.
857 */
858 corosio::local_endpoint remote_endpoint() const noexcept;
859
860 protected:
861 /// Default-construct (for derived types).
862 local_datagram_socket() noexcept = default;
863
864 /// Construct from a pre-built handle.
865 explicit local_datagram_socket(handle h) noexcept
866 : io_object(std::move(h))
867 {
868 }
869
870 private:
871 void open_for_family(int family, int type, int protocol);
872
873 158x inline implementation& get() const noexcept
874 {
875 158x return *static_cast<implementation*>(h_.get());
876 }
877 };
878
879 } // namespace boost::corosio
880
881 #endif // BOOST_COROSIO_POSIX
882
883 #endif // BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
884