Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:28:40

0001 //
0002 // detail/io_uring_descriptor_service.hpp
0003 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 //
0010 
0011 #ifndef BOOST_ASIO_DETAIL_IO_URING_DESCRIPTOR_SERVICE_HPP
0012 #define BOOST_ASIO_DETAIL_IO_URING_DESCRIPTOR_SERVICE_HPP
0013 
0014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0015 # pragma once
0016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
0017 
0018 #include <boost/asio/detail/config.hpp>
0019 
0020 #if defined(BOOST_ASIO_HAS_IO_URING)
0021 
0022 #include <boost/asio/associated_cancellation_slot.hpp>
0023 #include <boost/asio/buffer.hpp>
0024 #include <boost/asio/cancellation_type.hpp>
0025 #include <boost/asio/execution_context.hpp>
0026 #include <boost/asio/detail/buffer_sequence_adapter.hpp>
0027 #include <boost/asio/detail/descriptor_ops.hpp>
0028 #include <boost/asio/detail/io_uring_descriptor_read_at_op.hpp>
0029 #include <boost/asio/detail/io_uring_descriptor_read_op.hpp>
0030 #include <boost/asio/detail/io_uring_descriptor_write_at_op.hpp>
0031 #include <boost/asio/detail/io_uring_descriptor_write_op.hpp>
0032 #include <boost/asio/detail/io_uring_null_buffers_op.hpp>
0033 #include <boost/asio/detail/io_uring_service.hpp>
0034 #include <boost/asio/detail/io_uring_wait_op.hpp>
0035 #include <boost/asio/detail/memory.hpp>
0036 #include <boost/asio/detail/noncopyable.hpp>
0037 #include <boost/asio/posix/descriptor_base.hpp>
0038 
0039 #include <boost/asio/detail/push_options.hpp>
0040 
0041 namespace boost {
0042 namespace asio {
0043 namespace detail {
0044 
0045 class io_uring_descriptor_service :
0046   public execution_context_service_base<io_uring_descriptor_service>
0047 {
0048 public:
0049   // The native type of a descriptor.
0050   typedef int native_handle_type;
0051 
0052   // The implementation type of the descriptor.
0053   class implementation_type
0054     : private boost::asio::detail::noncopyable
0055   {
0056   public:
0057     // Default constructor.
0058     implementation_type()
0059       : descriptor_(-1),
0060         state_(0)
0061     {
0062     }
0063 
0064   private:
0065     // Only this service will have access to the internal values.
0066     friend class io_uring_descriptor_service;
0067 
0068     // The native descriptor representation.
0069     int descriptor_;
0070 
0071     // The current state of the descriptor.
0072     descriptor_ops::state_type state_;
0073 
0074     // Per I/O object data used by the io_uring_service.
0075     io_uring_service::per_io_object_data io_object_data_;
0076   };
0077 
0078   // Constructor.
0079   BOOST_ASIO_DECL io_uring_descriptor_service(execution_context& context);
0080 
0081   // Destroy all user-defined handler objects owned by the service.
0082   BOOST_ASIO_DECL void shutdown();
0083 
0084   // Construct a new descriptor implementation.
0085   BOOST_ASIO_DECL void construct(implementation_type& impl);
0086 
0087   // Move-construct a new descriptor implementation.
0088   BOOST_ASIO_DECL void move_construct(implementation_type& impl,
0089       implementation_type& other_impl) noexcept;
0090 
0091   // Move-assign from another descriptor implementation.
0092   BOOST_ASIO_DECL void move_assign(implementation_type& impl,
0093       io_uring_descriptor_service& other_service,
0094       implementation_type& other_impl);
0095 
0096   // Destroy a descriptor implementation.
0097   BOOST_ASIO_DECL void destroy(implementation_type& impl);
0098 
0099   // Assign a native descriptor to a descriptor implementation.
0100   BOOST_ASIO_DECL boost::system::error_code assign(implementation_type& impl,
0101       const native_handle_type& native_descriptor,
0102       boost::system::error_code& ec);
0103 
0104   // Determine whether the descriptor is open.
0105   bool is_open(const implementation_type& impl) const
0106   {
0107     return impl.descriptor_ != -1;
0108   }
0109 
0110   // Destroy a descriptor implementation.
0111   BOOST_ASIO_DECL boost::system::error_code close(implementation_type& impl,
0112       boost::system::error_code& ec);
0113 
0114   // Get the native descriptor representation.
0115   native_handle_type native_handle(const implementation_type& impl) const
0116   {
0117     return impl.descriptor_;
0118   }
0119 
0120   // Release ownership of the native descriptor representation.
0121   BOOST_ASIO_DECL native_handle_type release(implementation_type& impl);
0122 
0123   // Release ownership of the native descriptor representation.
0124   native_handle_type release(implementation_type& impl,
0125       boost::system::error_code& ec)
0126   {
0127     ec = success_ec_;
0128     return release(impl);
0129   }
0130 
0131   // Cancel all operations associated with the descriptor.
0132   BOOST_ASIO_DECL boost::system::error_code cancel(implementation_type& impl,
0133       boost::system::error_code& ec);
0134 
0135   // Perform an IO control command on the descriptor.
0136   template <typename IO_Control_Command>
0137   boost::system::error_code io_control(implementation_type& impl,
0138       IO_Control_Command& command, boost::system::error_code& ec)
0139   {
0140     descriptor_ops::ioctl(impl.descriptor_, impl.state_,
0141         command.name(), static_cast<ioctl_arg_type*>(command.data()), ec);
0142     BOOST_ASIO_ERROR_LOCATION(ec);
0143     return ec;
0144   }
0145 
0146   // Gets the non-blocking mode of the descriptor.
0147   bool non_blocking(const implementation_type& impl) const
0148   {
0149     return (impl.state_ & descriptor_ops::user_set_non_blocking) != 0;
0150   }
0151 
0152   // Sets the non-blocking mode of the descriptor.
0153   boost::system::error_code non_blocking(implementation_type& impl,
0154       bool mode, boost::system::error_code& ec)
0155   {
0156     descriptor_ops::set_user_non_blocking(
0157         impl.descriptor_, impl.state_, mode, ec);
0158     BOOST_ASIO_ERROR_LOCATION(ec);
0159     return ec;
0160   }
0161 
0162   // Gets the non-blocking mode of the native descriptor implementation.
0163   bool native_non_blocking(const implementation_type& impl) const
0164   {
0165     return (impl.state_ & descriptor_ops::internal_non_blocking) != 0;
0166   }
0167 
0168   // Sets the non-blocking mode of the native descriptor implementation.
0169   boost::system::error_code native_non_blocking(implementation_type& impl,
0170       bool mode, boost::system::error_code& ec)
0171   {
0172     descriptor_ops::set_internal_non_blocking(
0173         impl.descriptor_, impl.state_, mode, ec);
0174     BOOST_ASIO_ERROR_LOCATION(ec);
0175     return ec;
0176   }
0177 
0178   // Wait for the descriptor to become ready to read, ready to write, or to have
0179   // pending error conditions.
0180   boost::system::error_code wait(implementation_type& impl,
0181       posix::descriptor_base::wait_type w, boost::system::error_code& ec)
0182   {
0183     switch (w)
0184     {
0185     case posix::descriptor_base::wait_read:
0186       descriptor_ops::poll_read(impl.descriptor_, impl.state_, ec);
0187       break;
0188     case posix::descriptor_base::wait_write:
0189       descriptor_ops::poll_write(impl.descriptor_, impl.state_, ec);
0190       break;
0191     case posix::descriptor_base::wait_error:
0192       descriptor_ops::poll_error(impl.descriptor_, impl.state_, ec);
0193       break;
0194     default:
0195       ec = boost::asio::error::invalid_argument;
0196       break;
0197     }
0198 
0199     BOOST_ASIO_ERROR_LOCATION(ec);
0200     return ec;
0201   }
0202 
0203   // Asynchronously wait for the descriptor to become ready to read, ready to
0204   // write, or to have pending error conditions.
0205   template <typename Handler, typename IoExecutor>
0206   void async_wait(implementation_type& impl,
0207       posix::descriptor_base::wait_type w,
0208       Handler& handler, const IoExecutor& io_ex)
0209   {
0210     bool is_continuation =
0211       boost_asio_handler_cont_helpers::is_continuation(handler);
0212 
0213     associated_cancellation_slot_t<Handler> slot
0214       = boost::asio::get_associated_cancellation_slot(handler);
0215 
0216     int op_type;
0217     int poll_flags;
0218     switch (w)
0219     {
0220     case posix::descriptor_base::wait_read:
0221       op_type = io_uring_service::read_op;
0222       poll_flags = POLLIN;
0223       break;
0224     case posix::descriptor_base::wait_write:
0225       op_type = io_uring_service::write_op;
0226       poll_flags = POLLOUT;
0227       break;
0228     case posix::descriptor_base::wait_error:
0229       op_type = io_uring_service::except_op;
0230       poll_flags = POLLPRI | POLLERR | POLLHUP;
0231       break;
0232     default:
0233       op_type = -1;
0234       poll_flags = -1;
0235       return;
0236     }
0237 
0238     // Allocate and construct an operation to wrap the handler.
0239     typedef io_uring_wait_op<Handler, IoExecutor> op;
0240     typename op::ptr p = { boost::asio::detail::addressof(handler),
0241       op::ptr::allocate(handler), 0 };
0242     p.p = new (p.v) op(success_ec_, impl.descriptor_,
0243         poll_flags, handler, io_ex);
0244 
0245     // Optionally register for per-operation cancellation.
0246     if (slot.is_connected() && op_type != -1)
0247     {
0248       p.p->cancellation_key_ =
0249         &slot.template emplace<io_uring_op_cancellation>(
0250             &io_uring_service_, &impl.io_object_data_, op_type);
0251     }
0252 
0253     BOOST_ASIO_HANDLER_CREATION((io_uring_service_.context(), *p.p,
0254           "descriptor", &impl, impl.descriptor_, "async_wait"));
0255 
0256     start_op(impl, op_type, p.p, is_continuation, op_type == -1);
0257     p.v = p.p = 0;
0258   }
0259 
0260   // Write some data to the descriptor.
0261   template <typename ConstBufferSequence>
0262   size_t write_some(implementation_type& impl,
0263       const ConstBufferSequence& buffers, boost::system::error_code& ec)
0264   {
0265     typedef buffer_sequence_adapter<boost::asio::const_buffer,
0266         ConstBufferSequence> bufs_type;
0267 
0268     size_t n;
0269     if (bufs_type::is_single_buffer)
0270     {
0271       n = descriptor_ops::sync_write1(impl.descriptor_,
0272           impl.state_, bufs_type::first(buffers).data(),
0273           bufs_type::first(buffers).size(), ec);
0274     }
0275     else
0276     {
0277       bufs_type bufs(buffers);
0278 
0279       n = descriptor_ops::sync_write(impl.descriptor_, impl.state_,
0280           bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
0281     }
0282 
0283     BOOST_ASIO_ERROR_LOCATION(ec);
0284     return n;
0285   }
0286 
0287   // Wait until data can be written without blocking.
0288   size_t write_some(implementation_type& impl,
0289       const null_buffers&, boost::system::error_code& ec)
0290   {
0291     // Wait for descriptor to become ready.
0292     descriptor_ops::poll_write(impl.descriptor_, impl.state_, ec);
0293 
0294     BOOST_ASIO_ERROR_LOCATION(ec);
0295     return 0;
0296   }
0297 
0298   // Start an asynchronous write. The data being sent must be valid for the
0299   // lifetime of the asynchronous operation.
0300   template <typename ConstBufferSequence, typename Handler, typename IoExecutor>
0301   void async_write_some(implementation_type& impl,
0302       const ConstBufferSequence& buffers, Handler& handler,
0303       const IoExecutor& io_ex)
0304   {
0305     bool is_continuation =
0306       boost_asio_handler_cont_helpers::is_continuation(handler);
0307 
0308     associated_cancellation_slot_t<Handler> slot
0309       = boost::asio::get_associated_cancellation_slot(handler);
0310 
0311     // Allocate and construct an operation to wrap the handler.
0312     typedef io_uring_descriptor_write_op<
0313       ConstBufferSequence, Handler, IoExecutor> op;
0314     typename op::ptr p = { boost::asio::detail::addressof(handler),
0315       op::ptr::allocate(handler), 0 };
0316     p.p = new (p.v) op(success_ec_, impl.descriptor_,
0317         impl.state_, buffers, handler, io_ex);
0318 
0319     // Optionally register for per-operation cancellation.
0320     if (slot.is_connected())
0321     {
0322       p.p->cancellation_key_ =
0323         &slot.template emplace<io_uring_op_cancellation>(&io_uring_service_,
0324             &impl.io_object_data_, io_uring_service::write_op);
0325     }
0326 
0327     BOOST_ASIO_HANDLER_CREATION((io_uring_service_.context(), *p.p,
0328           "descriptor", &impl, impl.descriptor_, "async_write_some"));
0329 
0330     start_op(impl, io_uring_service::write_op, p.p, is_continuation,
0331         buffer_sequence_adapter<boost::asio::const_buffer,
0332           ConstBufferSequence>::all_empty(buffers));
0333     p.v = p.p = 0;
0334   }
0335 
0336   // Start an asynchronous wait until data can be written without blocking.
0337   template <typename Handler, typename IoExecutor>
0338   void async_write_some(implementation_type& impl,
0339       const null_buffers&, Handler& handler, const IoExecutor& io_ex)
0340   {
0341     bool is_continuation =
0342       boost_asio_handler_cont_helpers::is_continuation(handler);
0343 
0344     associated_cancellation_slot_t<Handler> slot
0345       = boost::asio::get_associated_cancellation_slot(handler);
0346 
0347     // Allocate and construct an operation to wrap the handler.
0348     typedef io_uring_null_buffers_op<Handler, IoExecutor> op;
0349     typename op::ptr p = { boost::asio::detail::addressof(handler),
0350       op::ptr::allocate(handler), 0 };
0351     p.p = new (p.v) op(success_ec_, impl.descriptor_, POLLOUT, handler, io_ex);
0352 
0353     // Optionally register for per-operation cancellation.
0354     if (slot.is_connected())
0355     {
0356       p.p->cancellation_key_ =
0357         &slot.template emplace<io_uring_op_cancellation>(&io_uring_service_,
0358             &impl.io_object_data_, io_uring_service::write_op);
0359     }
0360 
0361     BOOST_ASIO_HANDLER_CREATION((io_uring_service_.context(),
0362           *p.p, "descriptor", &impl, impl.descriptor_,
0363           "async_write_some(null_buffers)"));
0364 
0365     start_op(impl, io_uring_service::write_op, p.p, is_continuation, false);
0366     p.v = p.p = 0;
0367   }
0368 
0369   // Write some data to the descriptor at the specified offset.
0370   template <typename ConstBufferSequence>
0371   size_t write_some_at(implementation_type& impl, uint64_t offset,
0372       const ConstBufferSequence& buffers, boost::system::error_code& ec)
0373   {
0374     typedef buffer_sequence_adapter<boost::asio::const_buffer,
0375         ConstBufferSequence> bufs_type;
0376 
0377     size_t n;
0378     if (bufs_type::is_single_buffer)
0379     {
0380       n = descriptor_ops::sync_write_at1(impl.descriptor_,
0381           impl.state_, offset, bufs_type::first(buffers).data(),
0382           bufs_type::first(buffers).size(), ec);
0383     }
0384     else
0385     {
0386       bufs_type bufs(buffers);
0387 
0388       n = descriptor_ops::sync_write_at(impl.descriptor_, impl.state_,
0389           offset, bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
0390     }
0391 
0392     BOOST_ASIO_ERROR_LOCATION(ec);
0393     return n;
0394   }
0395 
0396   // Wait until data can be written without blocking.
0397   size_t write_some_at(implementation_type& impl, uint64_t,
0398       const null_buffers& buffers, boost::system::error_code& ec)
0399   {
0400     return write_some(impl, buffers, ec);
0401   }
0402 
0403   // Start an asynchronous write at the specified offset. The data being sent
0404   // must be valid for the lifetime of the asynchronous operation.
0405   template <typename ConstBufferSequence, typename Handler, typename IoExecutor>
0406   void async_write_some_at(implementation_type& impl, uint64_t offset,
0407       const ConstBufferSequence& buffers, Handler& handler,
0408       const IoExecutor& io_ex)
0409   {
0410     bool is_continuation =
0411       boost_asio_handler_cont_helpers::is_continuation(handler);
0412 
0413     associated_cancellation_slot_t<Handler> slot
0414       = boost::asio::get_associated_cancellation_slot(handler);
0415 
0416     // Allocate and construct an operation to wrap the handler.
0417     typedef io_uring_descriptor_write_at_op<
0418       ConstBufferSequence, Handler, IoExecutor> op;
0419     typename op::ptr p = { boost::asio::detail::addressof(handler),
0420       op::ptr::allocate(handler), 0 };
0421     p.p = new (p.v) op(success_ec_, impl.descriptor_,
0422         impl.state_, offset, buffers, handler, io_ex);
0423 
0424     // Optionally register for per-operation cancellation.
0425     if (slot.is_connected())
0426     {
0427       p.p->cancellation_key_ =
0428         &slot.template emplace<io_uring_op_cancellation>(&io_uring_service_,
0429             &impl.io_object_data_, io_uring_service::write_op);
0430     }
0431 
0432     BOOST_ASIO_HANDLER_CREATION((io_uring_service_.context(), *p.p,
0433           "descriptor", &impl, impl.descriptor_, "async_write_some"));
0434 
0435     start_op(impl, io_uring_service::write_op, p.p, is_continuation,
0436         buffer_sequence_adapter<boost::asio::const_buffer,
0437           ConstBufferSequence>::all_empty(buffers));
0438     p.v = p.p = 0;
0439   }
0440 
0441   // Start an asynchronous wait until data can be written without blocking.
0442   template <typename Handler, typename IoExecutor>
0443   void async_write_some_at(implementation_type& impl,
0444       const null_buffers& buffers, Handler& handler, const IoExecutor& io_ex)
0445   {
0446     return async_write_some(impl, buffers, handler, io_ex);
0447   }
0448 
0449   // Read some data from the stream. Returns the number of bytes read.
0450   template <typename MutableBufferSequence>
0451   size_t read_some(implementation_type& impl,
0452       const MutableBufferSequence& buffers, boost::system::error_code& ec)
0453   {
0454     typedef buffer_sequence_adapter<boost::asio::mutable_buffer,
0455         MutableBufferSequence> bufs_type;
0456 
0457     size_t n;
0458     if (bufs_type::is_single_buffer)
0459     {
0460       n = descriptor_ops::sync_read1(impl.descriptor_,
0461           impl.state_, bufs_type::first(buffers).data(),
0462           bufs_type::first(buffers).size(), ec);
0463     }
0464     else
0465     {
0466       bufs_type bufs(buffers);
0467 
0468       n = descriptor_ops::sync_read(impl.descriptor_, impl.state_,
0469           bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
0470     }
0471 
0472     BOOST_ASIO_ERROR_LOCATION(ec);
0473     return n;
0474   }
0475 
0476   // Wait until data can be read without blocking.
0477   size_t read_some(implementation_type& impl,
0478       const null_buffers&, boost::system::error_code& ec)
0479   {
0480     // Wait for descriptor to become ready.
0481     descriptor_ops::poll_read(impl.descriptor_, impl.state_, ec);
0482 
0483     BOOST_ASIO_ERROR_LOCATION(ec);
0484     return 0;
0485   }
0486 
0487   // Start an asynchronous read. The buffer for the data being read must be
0488   // valid for the lifetime of the asynchronous operation.
0489   template <typename MutableBufferSequence,
0490       typename Handler, typename IoExecutor>
0491   void async_read_some(implementation_type& impl,
0492       const MutableBufferSequence& buffers,
0493       Handler& handler, const IoExecutor& io_ex)
0494   {
0495     bool is_continuation =
0496       boost_asio_handler_cont_helpers::is_continuation(handler);
0497 
0498     associated_cancellation_slot_t<Handler> slot
0499       = boost::asio::get_associated_cancellation_slot(handler);
0500 
0501     // Allocate and construct an operation to wrap the handler.
0502     typedef io_uring_descriptor_read_op<
0503       MutableBufferSequence, Handler, IoExecutor> op;
0504     typename op::ptr p = { boost::asio::detail::addressof(handler),
0505       op::ptr::allocate(handler), 0 };
0506     p.p = new (p.v) op(success_ec_, impl.descriptor_,
0507         impl.state_, buffers, handler, io_ex);
0508 
0509     // Optionally register for per-operation cancellation.
0510     if (slot.is_connected())
0511     {
0512       p.p->cancellation_key_ =
0513         &slot.template emplace<io_uring_op_cancellation>(&io_uring_service_,
0514             &impl.io_object_data_, io_uring_service::read_op);
0515     }
0516 
0517     BOOST_ASIO_HANDLER_CREATION((io_uring_service_.context(), *p.p,
0518           "descriptor", &impl, impl.descriptor_, "async_read_some"));
0519 
0520     start_op(impl, io_uring_service::read_op, p.p, is_continuation,
0521         buffer_sequence_adapter<boost::asio::mutable_buffer,
0522           MutableBufferSequence>::all_empty(buffers));
0523     p.v = p.p = 0;
0524   }
0525 
0526   // Wait until data can be read without blocking.
0527   template <typename Handler, typename IoExecutor>
0528   void async_read_some(implementation_type& impl,
0529       const null_buffers&, Handler& handler, const IoExecutor& io_ex)
0530   {
0531     bool is_continuation =
0532       boost_asio_handler_cont_helpers::is_continuation(handler);
0533 
0534     associated_cancellation_slot_t<Handler> slot
0535       = boost::asio::get_associated_cancellation_slot(handler);
0536 
0537     // Allocate and construct an operation to wrap the handler.
0538     typedef io_uring_null_buffers_op<Handler, IoExecutor> op;
0539     typename op::ptr p = { boost::asio::detail::addressof(handler),
0540       op::ptr::allocate(handler), 0 };
0541     p.p = new (p.v) op(success_ec_, impl.descriptor_, POLLIN, handler, io_ex);
0542 
0543     // Optionally register for per-operation cancellation.
0544     if (slot.is_connected())
0545     {
0546       p.p->cancellation_key_ =
0547         &slot.template emplace<io_uring_op_cancellation>(&io_uring_service_,
0548             &impl.io_object_data_, io_uring_service::read_op);
0549     }
0550 
0551     BOOST_ASIO_HANDLER_CREATION((io_uring_service_.context(),
0552           *p.p, "descriptor", &impl, impl.descriptor_,
0553           "async_read_some(null_buffers)"));
0554 
0555     start_op(impl, io_uring_service::read_op, p.p, is_continuation, false);
0556     p.v = p.p = 0;
0557   }
0558 
0559   // Read some data at the specified offset. Returns the number of bytes read.
0560   template <typename MutableBufferSequence>
0561   size_t read_some_at(implementation_type& impl, uint64_t offset,
0562       const MutableBufferSequence& buffers, boost::system::error_code& ec)
0563   {
0564     typedef buffer_sequence_adapter<boost::asio::mutable_buffer,
0565         MutableBufferSequence> bufs_type;
0566 
0567     if (bufs_type::is_single_buffer)
0568     {
0569       return descriptor_ops::sync_read_at1(impl.descriptor_,
0570           impl.state_, offset, bufs_type::first(buffers).data(),
0571           bufs_type::first(buffers).size(), ec);
0572     }
0573     else
0574     {
0575       bufs_type bufs(buffers);
0576 
0577       return descriptor_ops::sync_read_at(impl.descriptor_, impl.state_,
0578           offset, bufs.buffers(), bufs.count(), bufs.all_empty(), ec);
0579     }
0580   }
0581 
0582   // Wait until data can be read without blocking.
0583   size_t read_some_at(implementation_type& impl, uint64_t,
0584       const null_buffers& buffers, boost::system::error_code& ec)
0585   {
0586     return read_some(impl, buffers, ec);
0587   }
0588 
0589   // Start an asynchronous read. The buffer for the data being read must be
0590   // valid for the lifetime of the asynchronous operation.
0591   template <typename MutableBufferSequence,
0592       typename Handler, typename IoExecutor>
0593   void async_read_some_at(implementation_type& impl,
0594       uint64_t offset, const MutableBufferSequence& buffers,
0595       Handler& handler, const IoExecutor& io_ex)
0596   {
0597     bool is_continuation =
0598       boost_asio_handler_cont_helpers::is_continuation(handler);
0599 
0600     associated_cancellation_slot_t<Handler> slot
0601       = boost::asio::get_associated_cancellation_slot(handler);
0602 
0603     // Allocate and construct an operation to wrap the handler.
0604     typedef io_uring_descriptor_read_at_op<
0605       MutableBufferSequence, Handler, IoExecutor> op;
0606     typename op::ptr p = { boost::asio::detail::addressof(handler),
0607       op::ptr::allocate(handler), 0 };
0608     p.p = new (p.v) op(success_ec_, impl.descriptor_,
0609         impl.state_, offset, buffers, handler, io_ex);
0610 
0611     // Optionally register for per-operation cancellation.
0612     if (slot.is_connected())
0613     {
0614       p.p->cancellation_key_ =
0615         &slot.template emplace<io_uring_op_cancellation>(&io_uring_service_,
0616             &impl.io_object_data_, io_uring_service::read_op);
0617     }
0618 
0619     BOOST_ASIO_HANDLER_CREATION((io_uring_service_.context(), *p.p,
0620           "descriptor", &impl, impl.descriptor_, "async_read_some"));
0621 
0622     start_op(impl, io_uring_service::read_op, p.p, is_continuation,
0623         buffer_sequence_adapter<boost::asio::mutable_buffer,
0624           MutableBufferSequence>::all_empty(buffers));
0625     p.v = p.p = 0;
0626   }
0627 
0628   // Wait until data can be read without blocking.
0629   template <typename Handler, typename IoExecutor>
0630   void async_read_some_at(implementation_type& impl, uint64_t,
0631       const null_buffers& buffers, Handler& handler, const IoExecutor& io_ex)
0632   {
0633     return async_read_some(impl, buffers, handler, io_ex);
0634   }
0635 
0636 private:
0637   // Start the asynchronous operation.
0638   BOOST_ASIO_DECL void start_op(implementation_type& impl, int op_type,
0639       io_uring_operation* op, bool is_continuation, bool noop);
0640 
0641   // Helper class used to implement per-operation cancellation
0642   class io_uring_op_cancellation
0643   {
0644   public:
0645     io_uring_op_cancellation(io_uring_service* s,
0646         io_uring_service::per_io_object_data* p, int o)
0647       : io_uring_service_(s),
0648         io_object_data_(p),
0649         op_type_(o)
0650     {
0651     }
0652 
0653     void operator()(cancellation_type_t type)
0654     {
0655       if (!!(type &
0656             (cancellation_type::terminal
0657               | cancellation_type::partial
0658               | cancellation_type::total)))
0659       {
0660         io_uring_service_->cancel_ops_by_key(*io_object_data_, op_type_, this);
0661       }
0662     }
0663 
0664   private:
0665     io_uring_service* io_uring_service_;
0666     io_uring_service::per_io_object_data* io_object_data_;
0667     int op_type_;
0668   };
0669 
0670   // The io_uring_service that performs event demultiplexing for the service.
0671   io_uring_service& io_uring_service_;
0672 
0673   // Cached success value to avoid accessing category singleton.
0674   const boost::system::error_code success_ec_;
0675 };
0676 
0677 } // namespace detail
0678 } // namespace asio
0679 } // namespace boost
0680 
0681 #include <boost/asio/detail/pop_options.hpp>
0682 
0683 #if defined(BOOST_ASIO_HEADER_ONLY)
0684 # include <boost/asio/detail/impl/io_uring_descriptor_service.ipp>
0685 #endif // defined(BOOST_ASIO_HEADER_ONLY)
0686 
0687 #endif // defined(BOOST_ASIO_HAS_IO_URING)
0688 
0689 #endif // BOOST_ASIO_DETAIL_IO_URING_DESCRIPTOR_SERVICE_HPP