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