TLA Line data 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 HIT 88 : send_to_awaitable(
301 : local_datagram_socket& s,
302 : buffer_param buf,
303 : corosio::local_endpoint dest,
304 : int flags = 0) noexcept
305 176 : : s_(s)
306 88 : , buf_(buf)
307 88 : , dest_(dest)
308 88 : , flags_(flags)
309 : {
310 88 : }
311 :
312 : std::coroutine_handle<>
313 84 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
314 : {
315 168 : return s_.get().send_to(
316 168 : 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 88 : recv_from_awaitable(
333 : local_datagram_socket& s,
334 : buffer_param buf,
335 : corosio::local_endpoint& source,
336 : int flags = 0) noexcept
337 176 : : s_(s)
338 88 : , buf_(buf)
339 88 : , source_(source)
340 88 : , flags_(flags)
341 : {
342 88 : }
343 :
344 : std::coroutine_handle<>
345 84 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
346 : {
347 168 : return s_.get().recv_from(
348 168 : 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 2 : connect_awaitable(
363 : local_datagram_socket& s, corosio::local_endpoint ep) noexcept
364 4 : : s_(s)
365 2 : , endpoint_(ep)
366 : {
367 2 : }
368 :
369 : std::coroutine_handle<>
370 2 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
371 : {
372 2 : 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 14 : wait_awaitable(local_datagram_socket& s, wait_type w) noexcept
383 28 : : s_(s)
384 14 : , w_(w)
385 : {
386 14 : }
387 :
388 : std::coroutine_handle<>
389 12 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
390 : {
391 12 : 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 93 : send_awaitable(
407 : local_datagram_socket& s, buffer_param buf, int flags = 0) noexcept
408 186 : : s_(s)
409 93 : , buf_(buf)
410 93 : , flags_(flags)
411 : {
412 93 : }
413 :
414 : std::coroutine_handle<>
415 89 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
416 : {
417 89 : 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 97 : recv_awaitable(
433 : local_datagram_socket& s, buffer_param buf, int flags = 0) noexcept
434 194 : : s_(s)
435 97 : , buf_(buf)
436 97 : , flags_(flags)
437 : {
438 97 : }
439 :
440 : std::coroutine_handle<>
441 93 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
442 : {
443 93 : 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 2 : local_datagram_socket(local_datagram_socket&& other) noexcept
482 2 : : io_object(std::move(other))
483 : {
484 2 : }
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 2 : local_datagram_socket& operator=(local_datagram_socket&& other) noexcept
494 : {
495 2 : if (this != &other)
496 : {
497 2 : close();
498 2 : io_object::operator=(std::move(other));
499 : }
500 2 : 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 1169 : 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 1169 : 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 2 : [[nodiscard]] auto connect(corosio::local_endpoint ep)
578 : {
579 2 : connect_awaitable aw(*this, ep);
580 2 : if (!is_open())
581 2 : aw.ec_ = open();
582 2 : 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 14 : [[nodiscard]] auto wait(wait_type w)
601 : {
602 14 : 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 88 : [[nodiscard]] auto send_to(
624 : Buffers const& buf,
625 : corosio::local_endpoint dest,
626 : corosio::message_flags flags)
627 : {
628 88 : send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
629 88 : if (!is_open())
630 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
631 88 : return aw;
632 : }
633 :
634 : /// @overload
635 : template<capy::ConstBufferSequence Buffers>
636 88 : [[nodiscard]] auto send_to(Buffers const& buf, corosio::local_endpoint dest)
637 : {
638 88 : 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 88 : [[nodiscard]] auto recv_from(
664 : Buffers const& buf,
665 : corosio::local_endpoint& source,
666 : corosio::message_flags flags)
667 : {
668 88 : recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
669 88 : if (!is_open())
670 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
671 88 : return aw;
672 : }
673 :
674 : /// @overload
675 : template<capy::MutableBufferSequence Buffers>
676 : [[nodiscard]] auto
677 86 : recv_from(Buffers const& buf, corosio::local_endpoint& source)
678 : {
679 86 : 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 93 : [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
699 : {
700 93 : send_awaitable aw(*this, buf, static_cast<int>(flags));
701 93 : if (!is_open())
702 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
703 93 : return aw;
704 : }
705 :
706 : /// @overload
707 : template<capy::ConstBufferSequence Buffers>
708 93 : [[nodiscard]] auto send(Buffers const& buf)
709 : {
710 93 : 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 97 : [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
730 : {
731 97 : recv_awaitable aw(*this, buf, static_cast<int>(flags));
732 97 : if (!is_open())
733 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
734 97 : return aw;
735 : }
736 :
737 : /// @overload
738 : template<capy::MutableBufferSequence Buffers>
739 95 : [[nodiscard]] auto recv(Buffers const& buf)
740 : {
741 95 : 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 24 : void set_option(Option const& opt)
806 : {
807 24 : if (!is_open())
808 2 : detail::throw_system_error(
809 4 : make_error_code(std::errc::bad_file_descriptor),
810 : "local_datagram_socket::set_option");
811 22 : std::error_code ec = get().set_option(
812 : Option::level(), Option::name(), opt.data(), opt.size());
813 22 : if (ec)
814 2 : detail::throw_system_error(ec, "local_datagram_socket::set_option");
815 20 : }
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 8 : Option get_option() const
830 : {
831 8 : if (!is_open())
832 2 : detail::throw_system_error(
833 4 : make_error_code(std::errc::bad_file_descriptor),
834 : "local_datagram_socket::get_option");
835 6 : Option opt{};
836 6 : std::size_t sz = opt.size();
837 : std::error_code ec =
838 6 : get().get_option(Option::level(), Option::name(), opt.data(), &sz);
839 6 : if (ec)
840 2 : detail::throw_system_error(ec, "local_datagram_socket::get_option");
841 4 : opt.resize(sz);
842 4 : 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 30 : explicit local_datagram_socket(handle h) noexcept : io_object(std::move(h))
895 : {
896 30 : }
897 :
898 : private:
899 : [[nodiscard]] std::error_code
900 : open_for_family(int family, int type, int protocol) noexcept;
901 :
902 1590 : inline implementation& get() const noexcept
903 : {
904 1590 : 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
|