include/boost/corosio/local_datagram_socket.hpp

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