1

I am reading Boost.Asio source code, but there is the code I am confused. Has anyone help to explain for better understand, or some materials I can refer for understanding? Thanks.

The actual code:

// Wait until data can be received without blocking.
  template <typename Handler>
  void async_receive_from(implementation_type& impl,
      const null_buffers&, endpoint_type& sender_endpoint,
      socket_base::message_flags flags, Handler& handler)
  {
    bool is_continuation =
      boost_asio_handler_cont_helpers::is_continuation(handler);

    // Allocate and construct an operation to wrap the handler.
    typedef reactive_null_buffers_op<Handler> op;
    typename op::ptr p = { boost::asio::detail::addressof(handler),
      boost_asio_handler_alloc_helpers::allocate(
        sizeof(op), handler), 0 };
    p.p = new (p.v) op(handler);

    BOOST_ASIO_HANDLER_CREATION((p.p, "socket",
          &impl, "async_receive_from(null_buffers)"));

    // Reset endpoint since it can be given no sensible value at this time.
    sender_endpoint = endpoint_type();

    start_op(impl,
        (flags & socket_base::message_out_of_band)
          ? reactor::except_op : reactor::read_op,
        p.p, is_continuation, false, false);
    p.v = p.p = 0;
  }

especially for:

typedef reactive_null_buffers_op<Handler> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
  boost_asio_handler_alloc_helpers::allocate(
    sizeof(op), handler), 0 };
p.p = new (p.v) op(handler);

Thanks.

Arunmu
  • 6,837
  • 1
  • 24
  • 46
Nicol TAO
  • 11
  • 3

1 Answers1

1

Hmm..lets see..you are trying to understand the function async_receive_from of reactive_socket_service class. Specifically the one which takes a null_buffer.

The signature of the function:

template <typename Handler>
  void async_receive_from(implementation_type& impl,
                          const null_buffers&, 
                          endpoint_type& sender_endpoint,
                          socket_base::message_flags flags, 
                          Handler& handler)

Now, what is the importance of the null_buffers ?

In brief, it basically allows one to do the buffer management in the invoked callback rather than providing the buffer while calling async_receive_from like in its other overload.
See THIS answer for more detailed explanation for its use case.

What is reactive_null_buffers_op<Handler> op ?

Since you are at this point, I am assuming that you are aware of operation class in asio. In simple words, it basically invokes a user provided callback when the particular operation is executed completely. Look for scheduler_operation::complete in scheduler_operation.hpp.

reactive_null_buffers_op is derived from scheduler_operation(via reactive_op) and it also stores the copy of the Handler passed to async_receive_from. The actual details are bit more complicated and wieldy. For now it might be sufficient to know that the do_complete method of this class is what does the upcall to the handler.

What is typename op::ptr ?

Look for ASIO_DEFINE_HANDLER_PTR in handler_alloc_helpers.hpp. In simple terms, its a struct which holds the handler passed to the async function. ASIO needs to allocate the handler separately on heap (or via a custom allocator) to make sure its exists for the duration of completion of the socket receive operation. This is what below 2 lines does:

typename op::ptr p = { boost::asio::detail::addressof(handler),
      boost_asio_handler_alloc_helpers::allocate(
        sizeof(op), handler), 0 };
    p.p = new (p.v) op(handler);

We are creating an instance of reactive_null_buffers_op<Handler> on the allocated space via placement new (2nd line).

Now what ?

One ASIO has stored the user passed handler object, it needs to start the main read operation on the socket. For that, it calls the start_op function. The details of start_op is beyond the current scope of the asked question. So, in brief start_op ends up registering the socket to the poller, say epoll. And our handler, now in op ends up getting called when the read operation is ready to be performed (Lots of details skipped for that).

Community
  • 1
  • 1
Arunmu
  • 6,837
  • 1
  • 24
  • 46
  • I think a correction is needed here: "in brief start_op ends up registering the socket to the poller", now, by the time you call async_* the IO object on which it has been called (basically the descriptor ) has to be already registered , if not, it will be registered before Starting any OP, so the start_op basically registers the Handler; not the socket (or push the Handler ) to the op_queue associated with the IO object's descriptor_data. – RaGa__M May 17 '19 at 10:28