include/boost/corosio/local_datagram_socket.hpp

100.0% Lines (112/0/112) 100.0% List of functions (33/0/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) :299 88x 100.0% 100.0% boost::corosio::local_datagram_socket::send_to_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :312 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) :331 88x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_from_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :344 86x 100.0% 80.0% boost::corosio::local_datagram_socket::connect_awaitable::connect_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::local_endpoint) :361 2x 100.0% 100.0% boost::corosio::local_datagram_socket::connect_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :369 2x 100.0% 80.0% boost::corosio::local_datagram_socket::wait_awaitable::wait_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::wait_type) :381 14x 100.0% 100.0% boost::corosio::local_datagram_socket::wait_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :388 14x 100.0% 80.0% boost::corosio::local_datagram_socket::send_awaitable::send_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, int) :405 93x 100.0% 100.0% boost::corosio::local_datagram_socket::send_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :414 91x 100.0% 80.0% boost::corosio::local_datagram_socket::recv_awaitable::recv_awaitable(boost::corosio::local_datagram_socket&, boost::corosio::buffer_param, int) :431 97x 100.0% 100.0% boost::corosio::local_datagram_socket::recv_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :440 95x 100.0% 80.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::local_datagram_socket&&) :480 2x 100.0% 100.0% boost::corosio::local_datagram_socket::operator=(boost::corosio::local_datagram_socket&&) :492 2x 100.0% 100.0% boost::corosio::local_datagram_socket::is_open() const :536 1169x 100.0% 100.0% boost::corosio::local_datagram_socket::connect(boost::corosio::local_endpoint) :576 2x 100.0% 100.0% boost::corosio::local_datagram_socket::wait(boost::corosio::wait_type) :599 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) :622 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) :635 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) :662 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&) :676 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) :697 93x 100.0% 100.0% auto boost::corosio::local_datagram_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&) :707 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) :728 97x 100.0% 100.0% auto boost::corosio::local_datagram_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&) :738 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&) :803 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&) :803 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&) :803 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 :827 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 :827 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 :827 4x 90.9% 78.0% boost::corosio::local_datagram_socket::local_datagram_socket(boost::corosio::io_object::handle) :892 30x 100.0% 100.0% boost::corosio::local_datagram_socket::get() const :900 1600x 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 bind(corosio::local_endpoint ep) noexcept = 0;
285 };
286
287 /** Represent the awaitable returned by @ref send_to.
288
289 Captures the destination endpoint and buffer, then dispatches
290 to the backend implementation on suspension.
291 */
292 struct send_to_awaitable : detail::bytes_op_base<send_to_awaitable>
293 {
294 local_datagram_socket& s_;
295 buffer_param buf_;
296 corosio::local_endpoint dest_;
297 int flags_;
298
299 88x send_to_awaitable(
300 local_datagram_socket& s,
301 buffer_param buf,
302 corosio::local_endpoint dest,
303 int flags = 0) noexcept
304 176x : s_(s)
305 88x , buf_(buf)
306 88x , dest_(dest)
307 88x , flags_(flags)
308 {
309 88x }
310
311 std::coroutine_handle<>
312 86x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
313 {
314 172x return s_.get().send_to(
315 172x h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
316 }
317 };
318
319 /** Represent the awaitable returned by @ref recv_from.
320
321 Captures the source endpoint reference and buffer, then
322 dispatches to the backend implementation on suspension.
323 */
324 struct recv_from_awaitable : detail::bytes_op_base<recv_from_awaitable>
325 {
326 local_datagram_socket& s_;
327 buffer_param buf_;
328 corosio::local_endpoint& source_;
329 int flags_;
330
331 88x recv_from_awaitable(
332 local_datagram_socket& s,
333 buffer_param buf,
334 corosio::local_endpoint& source,
335 int flags = 0) noexcept
336 176x : s_(s)
337 88x , buf_(buf)
338 88x , source_(source)
339 88x , flags_(flags)
340 {
341 88x }
342
343 std::coroutine_handle<>
344 86x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
345 {
346 172x return s_.get().recv_from(
347 172x h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
348 }
349 };
350
351 /** Represent the awaitable returned by @ref connect.
352
353 Captures the target endpoint, then dispatches to the
354 backend implementation on suspension.
355 */
356 struct connect_awaitable : detail::void_op_base<connect_awaitable>
357 {
358 local_datagram_socket& s_;
359 corosio::local_endpoint endpoint_;
360
361 2x connect_awaitable(
362 local_datagram_socket& s, corosio::local_endpoint ep) noexcept
363 4x : s_(s)
364 2x , endpoint_(ep)
365 {
366 2x }
367
368 std::coroutine_handle<>
369 2x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
370 {
371 2x return s_.get().connect(h, ex, endpoint_, token_, &ec_);
372 }
373 };
374
375 /// Represent the awaitable returned by @ref wait.
376 struct wait_awaitable : detail::void_op_base<wait_awaitable>
377 {
378 local_datagram_socket& s_;
379 wait_type w_;
380
381 14x wait_awaitable(local_datagram_socket& s, wait_type w) noexcept
382 28x : s_(s)
383 14x , w_(w)
384 {
385 14x }
386
387 std::coroutine_handle<>
388 14x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
389 {
390 14x return s_.get().wait(h, ex, w_, token_, &ec_);
391 }
392 };
393
394 /** Represent the awaitable returned by @ref send.
395
396 Captures the buffer, then dispatches to the backend
397 implementation on suspension. Requires a prior connect().
398 */
399 struct send_awaitable : detail::bytes_op_base<send_awaitable>
400 {
401 local_datagram_socket& s_;
402 buffer_param buf_;
403 int flags_;
404
405 93x send_awaitable(
406 local_datagram_socket& s, buffer_param buf, int flags = 0) noexcept
407 186x : s_(s)
408 93x , buf_(buf)
409 93x , flags_(flags)
410 {
411 93x }
412
413 std::coroutine_handle<>
414 91x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
415 {
416 91x return s_.get().send(h, ex, buf_, flags_, token_, &ec_, &bytes_);
417 }
418 };
419
420 /** Represent the awaitable returned by @ref recv.
421
422 Captures the buffer, then dispatches to the backend
423 implementation on suspension. Requires a prior connect().
424 */
425 struct recv_awaitable : detail::bytes_op_base<recv_awaitable>
426 {
427 local_datagram_socket& s_;
428 buffer_param buf_;
429 int flags_;
430
431 97x recv_awaitable(
432 local_datagram_socket& s, buffer_param buf, int flags = 0) noexcept
433 194x : s_(s)
434 97x , buf_(buf)
435 97x , flags_(flags)
436 {
437 97x }
438
439 std::coroutine_handle<>
440 95x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
441 {
442 95x return s_.get().recv(h, ex, buf_, flags_, token_, &ec_, &bytes_);
443 }
444 };
445
446 public:
447 /** Destructor.
448
449 Closes the socket if open, cancelling any pending operations.
450 */
451 ~local_datagram_socket() override;
452
453 /** Construct a socket from an execution context.
454
455 @param ctx The execution context that will own this socket.
456 */
457 explicit local_datagram_socket(capy::execution_context& ctx);
458
459 /** Construct a socket from an executor.
460
461 The socket is associated with the executor's context.
462
463 @param ex The executor whose context will own the socket.
464 */
465 template<class Ex>
466 requires(!std::
467 same_as<std::remove_cvref_t<Ex>, local_datagram_socket>) &&
468 capy::Executor<Ex>
469 explicit local_datagram_socket(Ex const& ex)
470 : local_datagram_socket(ex.context())
471 {
472 }
473
474 /** Move constructor.
475
476 Transfers ownership of the socket resources.
477
478 @param other The socket to move from.
479 */
480 2x local_datagram_socket(local_datagram_socket&& other) noexcept
481 2x : io_object(std::move(other))
482 {
483 2x }
484
485 /** Move assignment operator.
486
487 Closes any existing socket and transfers ownership.
488
489 @param other The socket to move from.
490 @return Reference to this socket.
491 */
492 2x local_datagram_socket& operator=(local_datagram_socket&& other) noexcept
493 {
494 2x if (this != &other)
495 {
496 2x close();
497 2x io_object::operator=(std::move(other));
498 }
499 2x return *this;
500 }
501
502 local_datagram_socket(local_datagram_socket const&) = delete;
503 local_datagram_socket& operator=(local_datagram_socket const&) = delete;
504
505 /** Open the socket.
506
507 Creates a Unix datagram socket and associates it with
508 the platform reactor.
509
510 Failures such as descriptor exhaustion are normal runtime
511 conditions and are reported through the returned error code.
512 Opening an already-open socket is a no-op that reports
513 success.
514
515 @param proto The protocol. Defaults to local_datagram{}.
516
517 @return The error code, empty on success.
518 */
519 [[nodiscard]] std::error_code open(local_datagram proto = {}) noexcept;
520
521 /** Close the socket.
522
523 Cancels any pending asynchronous operations and releases
524 the underlying file descriptor. Has no effect if the
525 socket is not open.
526
527 @post is_open() == false
528 */
529 void close() noexcept;
530
531 /** Check if the socket is open.
532
533 @return `true` if the socket holds a valid file descriptor,
534 `false` otherwise.
535 */
536 1169x bool is_open() const noexcept
537 {
538 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
539 return h_ && get().native_handle() != ~native_handle_type(0);
540 #else
541 1169x return h_ && get().native_handle() >= 0;
542 #endif
543 }
544
545 /** Bind the socket to a local endpoint.
546
547 Associates the socket with a local address (filesystem path).
548 Required before calling recv_from in connectionless mode.
549
550 @param ep The local endpoint to bind to.
551
552 @return Error code on failure, empty on success.
553
554 A closed socket reports `errc::bad_file_descriptor`.
555 */
556 [[nodiscard]] std::error_code bind(corosio::local_endpoint ep) noexcept;
557
558 /** Initiate an asynchronous connect to set the default peer.
559
560 If the socket is not already open, it is opened automatically.
561 After successful completion, send()/recv() may be used
562 without specifying an endpoint.
563
564 @param ep The remote endpoint to connect to.
565
566 @par Cancellation
567 Supports cancellation via the awaitable's stop_token or by
568 calling cancel(). On cancellation, yields
569 `capy::cond::canceled`.
570
571 @return An awaitable that completes with io_result<>.
572
573 If the socket needs to be opened and the open fails, the
574 awaitable completes immediately with that error.
575 */
576 2x [[nodiscard]] auto connect(corosio::local_endpoint ep)
577 {
578 2x connect_awaitable aw(*this, ep);
579 2x if (!is_open())
580 2x aw.ec_ = open();
581 2x return aw;
582 }
583
584 /** Wait for the socket to become ready in a given direction.
585
586 Suspends until the socket is ready for the requested
587 direction, or an error condition is reported. No bytes
588 are transferred.
589
590 @param w The wait direction (read, write, or error).
591
592 @return An awaitable that completes with `io_result<>`.
593
594 A closed socket completes with `errc::bad_file_descriptor`.
595
596 @par Preconditions
597 This socket must outlive the returned awaitable.
598 */
599 14x [[nodiscard]] auto wait(wait_type w)
600 {
601 14x return wait_awaitable(*this, w);
602 }
603
604 /** Send a datagram to the specified destination.
605
606 Completes when the entire datagram has been accepted
607 by the kernel. The bytes_transferred value equals the
608 datagram size on success.
609
610 @param buf The buffer containing data to send.
611 @param dest The destination endpoint.
612
613 @par Cancellation
614 Supports cancellation via stop_token or cancel().
615
616 @return An awaitable that completes with
617 io_result<std::size_t>.
618
619 A closed socket reports `errc::bad_file_descriptor`.
620 */
621 template<capy::ConstBufferSequence Buffers>
622 88x [[nodiscard]] auto send_to(
623 Buffers const& buf,
624 corosio::local_endpoint dest,
625 corosio::message_flags flags)
626 {
627 88x send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
628 88x if (!is_open())
629 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
630 88x return aw;
631 }
632
633 /// @overload
634 template<capy::ConstBufferSequence Buffers>
635 88x [[nodiscard]] auto send_to(Buffers const& buf, corosio::local_endpoint dest)
636 {
637 88x return send_to(buf, dest, corosio::message_flags::none);
638 }
639
640 /** Receive a datagram and capture the sender's endpoint.
641
642 Completes when one datagram has been received. The
643 bytes_transferred value is the number of bytes copied
644 into the buffer. If the buffer is smaller than the
645 datagram, excess bytes are discarded (datagram
646 semantics).
647
648 @param buf The buffer to receive data into.
649 @param source Reference to an endpoint that will be set to
650 the sender's address on successful completion.
651 @param flags Message flags (e.g. message_flags::peek).
652
653 @par Cancellation
654 Supports cancellation via stop_token or cancel().
655
656 @return An awaitable that completes with
657 io_result<std::size_t>.
658
659 A closed socket reports `errc::bad_file_descriptor`.
660 */
661 template<capy::MutableBufferSequence Buffers>
662 88x [[nodiscard]] auto recv_from(
663 Buffers const& buf,
664 corosio::local_endpoint& source,
665 corosio::message_flags flags)
666 {
667 88x recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
668 88x if (!is_open())
669 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
670 88x return aw;
671 }
672
673 /// @overload
674 template<capy::MutableBufferSequence Buffers>
675 [[nodiscard]] auto
676 86x recv_from(Buffers const& buf, corosio::local_endpoint& source)
677 {
678 86x return recv_from(buf, source, corosio::message_flags::none);
679 }
680
681 /** Send a datagram to the connected peer.
682
683 @pre connect() has been called successfully.
684
685 @param buf The buffer containing data to send.
686 @param flags Message flags.
687
688 @par Cancellation
689 Supports cancellation via stop_token or cancel().
690
691 @return An awaitable that completes with
692 io_result<std::size_t>.
693
694 A closed socket reports `errc::bad_file_descriptor`.
695 */
696 template<capy::ConstBufferSequence Buffers>
697 93x [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
698 {
699 93x send_awaitable aw(*this, buf, static_cast<int>(flags));
700 93x if (!is_open())
701 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
702 93x return aw;
703 }
704
705 /// @overload
706 template<capy::ConstBufferSequence Buffers>
707 93x [[nodiscard]] auto send(Buffers const& buf)
708 {
709 93x return send(buf, corosio::message_flags::none);
710 }
711
712 /** Receive a datagram from the connected peer.
713
714 @pre connect() has been called successfully.
715
716 @param buf The buffer to receive data into.
717 @param flags Message flags (e.g. message_flags::peek).
718
719 @par Cancellation
720 Supports cancellation via stop_token or cancel().
721
722 @return An awaitable that completes with
723 io_result<std::size_t>.
724
725 A closed socket reports `errc::bad_file_descriptor`.
726 */
727 template<capy::MutableBufferSequence Buffers>
728 97x [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
729 {
730 97x recv_awaitable aw(*this, buf, static_cast<int>(flags));
731 97x if (!is_open())
732 2x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
733 97x return aw;
734 }
735
736 /// @overload
737 template<capy::MutableBufferSequence Buffers>
738 95x [[nodiscard]] auto recv(Buffers const& buf)
739 {
740 95x return recv(buf, corosio::message_flags::none);
741 }
742
743 /** Cancel any pending asynchronous operations.
744
745 All outstanding operations complete with
746 errc::operation_canceled. Check ec == cond::canceled
747 for portable comparison.
748 */
749 void cancel() noexcept;
750
751 /** Get the native socket handle.
752
753 @return The native socket handle, or -1 if not open.
754 */
755 native_handle_type native_handle() const noexcept;
756
757 /** Release ownership of the native socket handle.
758
759 Deregisters the socket from the reactor and cancels pending
760 operations without closing the fd. The caller takes ownership
761 of the returned descriptor.
762
763 @return The native handle.
764
765 @throws std::system_error `errc::bad_file_descriptor` if the
766 socket is not open.
767 */
768 native_handle_type release();
769
770 /** Query the number of bytes available for reading.
771
772 @return The number of bytes that can be read without blocking.
773
774 @throws std::system_error `errc::bad_file_descriptor` if the
775 socket is not open; otherwise thrown on ioctl failure.
776 */
777 std::size_t available() const;
778
779 /** Shut down part or all of the socket.
780
781 Failures such as an unconnected socket are normal runtime
782 conditions and are reported through the returned error
783 code. A closed socket reports `errc::bad_file_descriptor`.
784
785 @param what Which direction to shut down.
786
787 @return The error code, empty on success.
788 */
789 [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
790
791 /** Set a socket option.
792
793 @tparam Option A socket option type that provides static
794 `level()` and `name()` members, and `data()` / `size()`
795 accessors for the option value.
796
797 @param opt The option to set.
798
799 @throws std::system_error `errc::bad_file_descriptor` if the
800 socket is not open; otherwise thrown on failure.
801 */
802 template<class Option>
803 24x void set_option(Option const& opt)
804 {
805 24x if (!is_open())
806 2x detail::throw_system_error(
807 4x make_error_code(std::errc::bad_file_descriptor),
808 "local_datagram_socket::set_option");
809 22x std::error_code ec = get().set_option(
810 Option::level(), Option::name(), opt.data(), opt.size());
811 22x if (ec)
812 2x detail::throw_system_error(ec, "local_datagram_socket::set_option");
813 20x }
814
815 /** Get a socket option.
816
817 @tparam Option A socket option type that provides static
818 `level()` and `name()` members, `data()` / `size()`
819 accessors, and a `resize()` member.
820
821 @return The current option value.
822
823 @throws std::system_error `errc::bad_file_descriptor` if the
824 socket is not open; otherwise thrown on failure.
825 */
826 template<class Option>
827 8x Option get_option() const
828 {
829 8x if (!is_open())
830 2x detail::throw_system_error(
831 4x make_error_code(std::errc::bad_file_descriptor),
832 "local_datagram_socket::get_option");
833 6x Option opt{};
834 6x std::size_t sz = opt.size();
835 std::error_code ec =
836 6x get().get_option(Option::level(), Option::name(), opt.data(), &sz);
837 6x if (ec)
838 2x detail::throw_system_error(ec, "local_datagram_socket::get_option");
839 4x opt.resize(sz);
840 4x return opt;
841 }
842
843 /** Assign an existing native socket to this object.
844
845 Adopts a Unix domain datagram socket created outside the
846 library — from `socketpair()`, received over `SCM_RIGHTS`,
847 or made natively — and registers it with the backend. The
848 socket must be a datagram socket in the `AF_UNIX` family.
849 Adoption never alters the descriptor's flags or options; the
850 fd must already be non-blocking.
851
852 If this object is already open, pending operations complete
853 with `errc::operation_canceled` and the held socket is
854 closed before the new one is adopted.
855
856 @par Exception Safety
857 Strong guarantee on validation failure: the object is
858 unchanged. If backend registration fails, the object either
859 retains its previous socket or is left closed, depending on
860 the backend. In all failure cases the caller retains
861 ownership of `fd`.
862
863 @param fd The native socket to adopt. On success the object
864 owns it and will close it.
865
866 @return The error code, empty on success. Validation and
867 registration failures are normal runtime conditions when
868 adopting foreign descriptors.
869 */
870 [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
871
872 /** Get the local endpoint of the socket.
873
874 @return The local endpoint, or a default endpoint if not bound.
875 */
876 corosio::local_endpoint local_endpoint() const noexcept;
877
878 /** Get the remote endpoint of the socket.
879
880 Returns the address of the connected peer.
881
882 @return The remote endpoint, or a default endpoint if
883 not connected.
884 */
885 corosio::local_endpoint remote_endpoint() const noexcept;
886
887 protected:
888 /// Default-construct (for derived types).
889 local_datagram_socket() noexcept = default;
890
891 /// Construct from a pre-built handle.
892 30x explicit local_datagram_socket(handle h) noexcept : io_object(std::move(h))
893 {
894 30x }
895
896 private:
897 [[nodiscard]] std::error_code
898 open_for_family(int family, int type, int protocol) noexcept;
899
900 1600x inline implementation& get() const noexcept
901 {
902 1600x return *static_cast<implementation*>(h_.get());
903 }
904 };
905
906 } // namespace boost::corosio
907
908 #endif // BOOST_COROSIO_POSIX
909
910 #endif // BOOST_COROSIO_LOCAL_DATAGRAM_SOCKET_HPP
911