include/boost/corosio/local_datagram_socket.hpp

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