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