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