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