include/boost/corosio/local_datagram_socket.hpp

100.0% Lines (88/0/88) 100.0% List of functions (30/0/30)
local_datagram_socket.hpp
f(x) Functions (30)
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) :301 88x 100.0% 100.0% boost::corosio::local_datagram_socket::send_to_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :306 86x 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) :327 88x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_from_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :332 86x 100.0% 80.0% boost::corosio::local_datagram_socket::wait_awaitable::wait_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::wait_type) :371 14x 100.0% 100.0% boost::corosio::local_datagram_socket::wait_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :374 14x 100.0% 80.0% boost::corosio::local_datagram_socket::send_awaitable::send_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, int) :393 93x 100.0% 100.0% boost::corosio::local_datagram_socket::send_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :398 91x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_awaitable::recv_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, int) :418 97x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :423 95x 100.0% 80.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::local_datagram_socket&&) :465 2x 100.0% 100.0% boost::corosio::local_datagram_socket::operator=(boost::corosio::local_datagram_socket&&) :477 2x 100.0% 100.0% boost::corosio::local_datagram_socket::is_open() const :521 1155x 100.0% 100.0% boost::corosio::local_datagram_socket::wait(boost::corosio::wait_type) :584 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) :607 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) :620 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) :647 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&) :660 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) :681 93x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&) :691 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) :712 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&) :722 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&) :787 2x 62.5% 75.0% void boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::receive_buffer_size>(boost::corosio::socket_option::receive_buffer_size const&) :787 10x 62.5% 75.0% void boost::corosio::local_datagram_socket::set_option<boost::corosio::socket_option::send_buffer_size>(boost::corosio::socket_option::send_buffer_size const&) :787 12x 87.5% 75.0% boost::corosio::socket_option::no_delay boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::no_delay>() const :812 2x 63.6% 67.0% boost::corosio::socket_option::receive_buffer_size boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::receive_buffer_size>() const :812 2x 72.7% 78.0% boost::corosio::socket_option::send_buffer_size boost::corosio::local_datagram_socket::get_option<boost::corosio::socket_option::send_buffer_size>() const :812 4x 90.9% 78.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::io_object::handle) :878 30x 100.0% 100.0% boost::corosio::local_datagram_socket::get() const :887 1584x 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 @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 /** Release ownership of the socket descriptor.
229
230 The implementation deregisters from the reactor and cancels
231 pending operations. The caller takes ownership of the
232 returned descriptor.
233
234 @return The native handle, or an invalid sentinel if
235 not open.
236 */
237 virtual native_handle_type release_socket() noexcept = 0;
238
239 /** Request cancellation of pending asynchronous operations.
240
241 All outstanding operations complete with operation_canceled
242 error. Check ec == cond::canceled for portable comparison.
243 */
244 virtual void cancel() noexcept = 0;
245
246 /** Set a socket option.
247
248 @param level The protocol level (e.g. SOL_SOCKET).
249 @param optname The option name.
250 @param data Pointer to the option value.
251 @param size Size of the option value in bytes.
252 @return Error code on failure, empty on success.
253 */
254 virtual std::error_code set_option(
255 int level,
256 int optname,
257 void const* data,
258 std::size_t size) noexcept = 0;
259
260 /** Get 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 receive the option value.
265 @param size On entry, the size of the buffer. On exit,
266 the size of the option value.
267 @return Error code on failure, empty on success.
268 */
269 virtual std::error_code
270 get_option(int level, int optname, void* data, std::size_t* size)
271 const noexcept = 0;
272
273 /// Return the cached local endpoint.
274 virtual corosio::local_endpoint local_endpoint() const noexcept = 0;
275
276 /// Return the cached remote endpoint (connected mode).
277 virtual corosio::local_endpoint remote_endpoint() const noexcept = 0;
278
279 /** Bind the socket to a local endpoint.
280
281 @param ep The local endpoint to bind to.
282 @return Error code on failure, empty on success.
283 */
284 virtual std::error_code
285 bind(corosio::local_endpoint ep) noexcept = 0;
286 };
287
288 /** Represent the awaitable returned by @ref send_to.
289
290 Captures the destination endpoint and buffer, then dispatches
291 to the backend implementation on suspension.
292 */
293 struct send_to_awaitable
294 : detail::bytes_op_base<send_to_awaitable>
295 {
296 local_datagram_socket& s_;
297 buffer_param buf_;
298 corosio::local_endpoint dest_;
299 int flags_;
300
301 88x send_to_awaitable(
302 local_datagram_socket& s, buffer_param buf,
303 corosio::local_endpoint dest, int flags = 0) noexcept
304 88x : s_(s), buf_(buf), dest_(dest), flags_(flags) {}
305
306 86x std::coroutine_handle<> dispatch(
307 std::coroutine_handle<> h, capy::executor_ref ex) const
308 {
309 172x return s_.get().send_to(
310 172x h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
311 }
312 };
313
314 /** Represent the awaitable returned by @ref recv_from.
315
316 Captures the source endpoint reference and buffer, then
317 dispatches to the backend implementation on suspension.
318 */
319 struct recv_from_awaitable
320 : detail::bytes_op_base<recv_from_awaitable>
321 {
322 local_datagram_socket& s_;
323 buffer_param buf_;
324 corosio::local_endpoint& source_;
325 int flags_;
326
327 88x recv_from_awaitable(
328 local_datagram_socket& s, buffer_param buf,
329 corosio::local_endpoint& source, int flags = 0) noexcept
330 88x : s_(s), buf_(buf), source_(source), flags_(flags) {}
331
332 86x std::coroutine_handle<> dispatch(
333 std::coroutine_handle<> h, capy::executor_ref ex) const
334 {
335 172x return s_.get().recv_from(
336 172x h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
337 }
338 };
339
340 /** Represent the awaitable returned by @ref connect.
341
342 Captures the target endpoint, then dispatches to the
343 backend implementation on suspension.
344 */
345 struct connect_awaitable
346 : detail::void_op_base<connect_awaitable>
347 {
348 local_datagram_socket& s_;
349 corosio::local_endpoint endpoint_;
350
351 connect_awaitable(
352 local_datagram_socket& s,
353 corosio::local_endpoint ep) noexcept
354 : s_(s), endpoint_(ep) {}
355
356 std::coroutine_handle<> dispatch(
357 std::coroutine_handle<> h, capy::executor_ref ex) const
358 {
359 return s_.get().connect(
360 h, ex, endpoint_, token_, &ec_);
361 }
362 };
363
364 /// Represent the awaitable returned by @ref wait.
365 struct wait_awaitable
366 : detail::void_op_base<wait_awaitable>
367 {
368 local_datagram_socket& s_;
369 wait_type w_;
370
371 14x wait_awaitable(local_datagram_socket& s, wait_type w) noexcept
372 14x : s_(s), w_(w) {}
373
374 14x std::coroutine_handle<> dispatch(
375 std::coroutine_handle<> h, capy::executor_ref ex) const
376 {
377 14x return s_.get().wait(h, ex, w_, token_, &ec_);
378 }
379 };
380
381 /** Represent the awaitable returned by @ref send.
382
383 Captures the buffer, then dispatches to the backend
384 implementation on suspension. Requires a prior connect().
385 */
386 struct send_awaitable
387 : detail::bytes_op_base<send_awaitable>
388 {
389 local_datagram_socket& s_;
390 buffer_param buf_;
391 int flags_;
392
393 93x send_awaitable(
394 local_datagram_socket& s, buffer_param buf,
395 int flags = 0) noexcept
396 93x : s_(s), buf_(buf), flags_(flags) {}
397
398 91x std::coroutine_handle<> dispatch(
399 std::coroutine_handle<> h, capy::executor_ref ex) const
400 {
401 182x return s_.get().send(
402 182x h, ex, buf_, flags_, token_, &ec_, &bytes_);
403 }
404 };
405
406 /** Represent the awaitable returned by @ref recv.
407
408 Captures the buffer, then dispatches to the backend
409 implementation on suspension. Requires a prior connect().
410 */
411 struct recv_awaitable
412 : detail::bytes_op_base<recv_awaitable>
413 {
414 local_datagram_socket& s_;
415 buffer_param buf_;
416 int flags_;
417
418 97x recv_awaitable(
419 local_datagram_socket& s, buffer_param buf,
420 int flags = 0) noexcept
421 97x : s_(s), buf_(buf), flags_(flags) {}
422
423 95x std::coroutine_handle<> dispatch(
424 std::coroutine_handle<> h, capy::executor_ref ex) const
425 {
426 190x return s_.get().recv(
427 190x h, ex, buf_, flags_, token_, &ec_, &bytes_);
428 }
429 };
430
431 public:
432 /** Destructor.
433
434 Closes the socket if open, cancelling any pending operations.
435 */
436 ~local_datagram_socket() override;
437
438 /** Construct a socket from an execution context.
439
440 @param ctx The execution context that will own this socket.
441 */
442 explicit local_datagram_socket(capy::execution_context& ctx);
443
444 /** Construct a socket from an executor.
445
446 The socket is associated with the executor's context.
447
448 @param ex The executor whose context will own the socket.
449 */
450 template<class Ex>
451 requires(
452 !std::same_as<std::remove_cvref_t<Ex>, local_datagram_socket>) &&
453 capy::Executor<Ex>
454 explicit local_datagram_socket(Ex const& ex)
455 : local_datagram_socket(ex.context())
456 {
457 }
458
459 /** Move constructor.
460
461 Transfers ownership of the socket resources.
462
463 @param other The socket to move from.
464 */
465 2x local_datagram_socket(local_datagram_socket&& other) noexcept
466 2x : io_object(std::move(other))
467 {
468 2x }
469
470 /** Move assignment operator.
471
472 Closes any existing socket and transfers ownership.
473
474 @param other The socket to move from.
475 @return Reference to this socket.
476 */
477 2x local_datagram_socket& operator=(local_datagram_socket&& other) noexcept
478 {
479 2x if (this != &other)
480 {
481 2x close();
482 2x io_object::operator=(std::move(other));
483 }
484 2x return *this;
485 }
486
487 local_datagram_socket(local_datagram_socket const&) = delete;
488 local_datagram_socket& operator=(local_datagram_socket const&) = delete;
489
490 /** Open the socket.
491
492 Creates a Unix datagram socket and associates it with
493 the platform reactor.
494
495 Failures such as descriptor exhaustion are normal runtime
496 conditions and are reported through the returned error code.
497 Opening an already-open socket is a no-op that reports
498 success.
499
500 @param proto The protocol. Defaults to local_datagram{}.
501
502 @return The error code, empty on success.
503 */
504 [[nodiscard]] std::error_code open(local_datagram proto = {}) noexcept;
505
506 /** Close the socket.
507
508 Cancels any pending asynchronous operations and releases
509 the underlying file descriptor. Has no effect if the
510 socket is not open.
511
512 @post is_open() == false
513 */
514 void close() noexcept;
515
516 /** Check if the socket is open.
517
518 @return `true` if the socket holds a valid file descriptor,
519 `false` otherwise.
520 */
521 1155x bool is_open() const noexcept
522 {
523 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
524 return h_ && get().native_handle() != ~native_handle_type(0);
525 #else
526 1155x return h_ && get().native_handle() >= 0;
527 #endif
528 }
529
530 /** Bind the socket to a local endpoint.
531
532 Associates the socket with a local address (filesystem path).
533 Required before calling recv_from in connectionless mode.
534
535 @param ep The local endpoint to bind to.
536
537 @return Error code on failure, empty on success.
538
539 A closed socket reports `errc::bad_file_descriptor`.
540 */
541 [[nodiscard]] std::error_code bind(corosio::local_endpoint ep) noexcept;
542
543 /** Initiate an asynchronous connect to set the default peer.
544
545 If the socket is not already open, it is opened automatically.
546 After successful completion, send()/recv() may be used
547 without specifying an endpoint.
548
549 @param ep The remote endpoint to connect to.
550
551 @par Cancellation
552 Supports cancellation via the awaitable's stop_token or by
553 calling cancel(). On cancellation, yields
554 `capy::cond::canceled`.
555
556 @return An awaitable that completes with io_result<>.
557
558 If the socket needs to be opened and the open fails, the
559 awaitable completes immediately with that error.
560 */
561 [[nodiscard]] auto connect(corosio::local_endpoint ep)
562 {
563 connect_awaitable aw(*this, ep);
564 if (!is_open())
565 aw.ec_ = open();
566 return aw;
567 }
568
569 /** Wait for the socket to become ready in a given direction.
570
571 Suspends until the socket is ready for the requested
572 direction, or an error condition is reported. No bytes
573 are transferred.
574
575 @param w The wait direction (read, write, or error).
576
577 @return An awaitable that completes with `io_result<>`.
578
579 A closed socket completes with `errc::bad_file_descriptor`.
580
581 @par Preconditions
582 This socket must outlive the returned awaitable.
583 */
584 14x [[nodiscard]] auto wait(wait_type w)
585 {
586 14x 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 A closed socket reports `errc::bad_file_descriptor`.
605 */
606 template<capy::ConstBufferSequence Buffers>
607 88x [[nodiscard]] auto send_to(
608 Buffers const& buf,
609 corosio::local_endpoint dest,
610 corosio::message_flags flags)
611 {
612 88x send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
613 88x if (!is_open())
614 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
615 88x return aw;
616 }
617
618 /// @overload
619 template<capy::ConstBufferSequence Buffers>
620 88x [[nodiscard]] auto send_to(Buffers const& buf, corosio::local_endpoint dest)
621 {
622 88x 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 A closed socket reports `errc::bad_file_descriptor`.
645 */
646 template<capy::MutableBufferSequence Buffers>
647 88x [[nodiscard]] auto recv_from(
648 Buffers const& buf,
649 corosio::local_endpoint& source,
650 corosio::message_flags flags)
651 {
652 88x recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
653 88x if (!is_open())
654 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
655 88x return aw;
656 }
657
658 /// @overload
659 template<capy::MutableBufferSequence Buffers>
660 86x [[nodiscard]] auto recv_from(Buffers const& buf, corosio::local_endpoint& source)
661 {
662 86x 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 A closed socket reports `errc::bad_file_descriptor`.
679 */
680 template<capy::ConstBufferSequence Buffers>
681 93x [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
682 {
683 93x send_awaitable aw(*this, buf, static_cast<int>(flags));
684 93x if (!is_open())
685 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
686 93x return aw;
687 }
688
689 /// @overload
690 template<capy::ConstBufferSequence Buffers>
691 93x [[nodiscard]] auto send(Buffers const& buf)
692 {
693 93x 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 A closed socket reports `errc::bad_file_descriptor`.
710 */
711 template<capy::MutableBufferSequence Buffers>
712 97x [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
713 {
714 97x recv_awaitable aw(*this, buf, static_cast<int>(flags));
715 97x if (!is_open())
716 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
717 97x return aw;
718 }
719
720 /// @overload
721 template<capy::MutableBufferSequence Buffers>
722 95x [[nodiscard]] auto recv(Buffers const& buf)
723 {
724 95x 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() noexcept;
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.
748
749 @throws std::system_error `errc::bad_file_descriptor` if the
750 socket is not open.
751 */
752 native_handle_type release();
753
754 /** Query the number of bytes available for reading.
755
756 @return The number of bytes that can be read without blocking.
757
758 @throws std::system_error `errc::bad_file_descriptor` if the
759 socket is not open; otherwise thrown on ioctl failure.
760 */
761 std::size_t available() const;
762
763 /** Shut down part or all of the socket.
764
765 Failures such as an unconnected socket are normal runtime
766 conditions and are reported through the returned error
767 code. A closed socket reports `errc::bad_file_descriptor`.
768
769 @param what Which direction to shut down.
770
771 @return The error code, empty on success.
772 */
773 [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
774
775 /** Set a socket option.
776
777 @tparam Option A socket option type that provides static
778 `level()` and `name()` members, and `data()` / `size()`
779 accessors for the option value.
780
781 @param opt The option to set.
782
783 @throws std::system_error `errc::bad_file_descriptor` if the
784 socket is not open; otherwise thrown on failure.
785 */
786 template<class Option>
787 24x void set_option(Option const& opt)
788 {
789 24x if (!is_open())
790 2x detail::throw_system_error(
791 4x make_error_code(std::errc::bad_file_descriptor),
792 "local_datagram_socket::set_option");
793 22x std::error_code ec = get().set_option(
794 Option::level(), Option::name(), opt.data(), opt.size());
795 22x if (ec)
796 2x detail::throw_system_error(
797 ec, "local_datagram_socket::set_option");
798 20x }
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::system_error `errc::bad_file_descriptor` if the
809 socket is not open; otherwise thrown on failure.
810 */
811 template<class Option>
812 8x Option get_option() const
813 {
814 8x if (!is_open())
815 2x detail::throw_system_error(
816 4x make_error_code(std::errc::bad_file_descriptor),
817 "local_datagram_socket::get_option");
818 6x Option opt{};
819 6x std::size_t sz = opt.size();
820 std::error_code ec =
821 6x get().get_option(Option::level(), Option::name(), opt.data(), &sz);
822 6x if (ec)
823 2x detail::throw_system_error(
824 ec, "local_datagram_socket::get_option");
825 4x opt.resize(sz);
826 4x return opt;
827 }
828
829 /** Assign an existing native socket to this object.
830
831 Adopts a Unix domain datagram socket created outside the
832 library — from `socketpair()`, received over `SCM_RIGHTS`,
833 or made natively — and registers it with the backend. The
834 socket must be a datagram socket in the `AF_UNIX` family.
835 Adoption never alters the descriptor's flags or options; the
836 fd must already be non-blocking.
837
838 If this object is already open, pending operations complete
839 with `errc::operation_canceled` and the held socket is
840 closed before the new one is adopted.
841
842 @par Exception Safety
843 Strong guarantee on validation failure: the object is
844 unchanged. If backend registration fails, the object either
845 retains its previous socket or is left closed, depending on
846 the backend. In all failure cases the caller retains
847 ownership of `fd`.
848
849 @param fd The native socket to adopt. On success the object
850 owns it and will close it.
851
852 @return The error code, empty on success. Validation and
853 registration failures are normal runtime conditions when
854 adopting foreign descriptors.
855 */
856 [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
857
858 /** Get the local endpoint of the socket.
859
860 @return The local endpoint, or a default endpoint if not bound.
861 */
862 corosio::local_endpoint local_endpoint() const noexcept;
863
864 /** Get the remote endpoint of the socket.
865
866 Returns the address of the connected peer.
867
868 @return The remote endpoint, or a default endpoint if
869 not connected.
870 */
871 corosio::local_endpoint remote_endpoint() const noexcept;
872
873 protected:
874 /// Default-construct (for derived types).
875 local_datagram_socket() noexcept = default;
876
877 /// Construct from a pre-built handle.
878 30x explicit local_datagram_socket(handle h) noexcept
879 30x : io_object(std::move(h))
880 {
881 30x }
882
883 private:
884 [[nodiscard]] std::error_code
885 open_for_family(int family, int type, int protocol) noexcept;
886
887 1584x inline implementation& get() const noexcept
888 {
889 1584x return *static_cast<implementation*>(h_.get());
890 }
891 };
892
893 } // namespace boost::corosio
894
895 #endif // BOOST_COROSIO_POSIX
896
897 #endif // BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
898