include/boost/corosio/local_datagram_socket.hpp

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