24#include <flow/log/log.hpp>
25#include <flow/async/single_thread_task_loop.hpp>
26#include <boost/move/make_unique.hpp>
27#include <boost/noncopyable.hpp>
111template<
typename Core_t>
113 public flow::log::Log_context,
114 private boost::noncopyable
140 flow::async::Single_thread_task_loop* worker,
Core* sync_io);
167 flow::async::Task_asio_err_sz&& on_done_func);
179 flow::async::Task_asio_err_sz&& on_done_func);
194 template<
typename Batch>
196 flow::async::Task_asio_err&& on_done_func);
211 template<
typename Batch>
213 flow::async::Task_asio_err&& on_done_func);
329 using User_request = std::variant<User_request_one, User_request_batch>;
349 flow::async::Task_asio_err_sz&& on_done_func);
388 template<
typename Batch,
bool HNDL_ELSE_BLOB>
464 mutable flow::util::Mutex_non_recursive
m_mutex;
484template<
typename Core_t>
487 flow::async::Single_thread_task_loop* worker,
489 flow::log::Log_context(logger_ptr,
Log_component::S_TRANSPORT),
496 using flow::util::Lock_guard;
512 m_sync_io.start_receive_blob_ops([
this](Asio_waitable_native_handle* hndl_of_interest,
513 bool ev_of_interest_snd_else_rcv,
519 FLOW_LOG_TRACE(
m_log_pfx <<
": Sync-IO receive-ops event-wait request: "
520 "descriptor [" << hndl_of_interest->native_handle() <<
"], "
521 "writable-else-readable [" << ev_of_interest_snd_else_rcv <<
"].");
524 assert(hndl_of_interest);
525 hndl_of_interest->async_wait(ev_of_interest_snd_else_rcv
526 ? Asio_waitable_native_handle::Base::wait_write
527 : Asio_waitable_native_handle::Base::wait_read,
528 [
this, on_active_ev_func = std::move(on_active_ev_func)]
533 if (err_code == boost::asio::error::operation_aborted)
547 (*on_active_ev_func)();
556template<
typename Core_t>
559 using flow::async::Single_thread_task_loop;
560 using flow::async::reset_thread_pinning;
561 using flow::util::ostream_op_string;
574 Single_thread_task_loop one_thread{get_logger(),
575 ostream_op_string(
"ARcDeinit-", m_log_pfx)};
576 one_thread.start([&]()
578 reset_thread_pinning(get_logger());
580 FLOW_LOG_TRACE(
"Running head slot async-receive completion handler.");
581 auto& req = *m_user_request;
582 if (
auto req1 = get_if<User_request_one>(&req))
590 FLOW_LOG_TRACE(
"User receive handler finished.");
592 while (!m_pending_user_requests_q.empty())
594 FLOW_LOG_TRACE(
"Running a queued async-receive completion handler.");
595 auto& req = *(m_pending_user_requests_q.front());
596 if (
auto req1 = get_if<User_request_one>(&req))
605 m_pending_user_requests_q.pop();
606 FLOW_LOG_TRACE(
"User receive handler finished. Popped from user request deficit queue.");
612 assert(m_pending_user_requests_q.empty());
616template<
typename Core_t>
619 flow::async::Task_asio_err_sz&& on_done_func)
621 assert(target_hndl &&
"Native_socket_stream::async_receive_native_handle() must take non-null Native_handle ptr.");
622 async_receive_impl(target_hndl, target_meta_blob, std::move(on_done_func));
630template<
typename Core_t>
632 flow::async::Task_asio_err_sz&& on_done_func)
634 async_receive_impl(
nullptr, target_blob, std::move(on_done_func));
637template<
typename Core_t>
640 flow::async::Task_asio_err_sz&& on_done_func)
642 using flow::util::Lock_guard;
643 using std::in_place_type;
677 FLOW_LOG_TRACE(m_log_pfx <<
": Incoming user async-receive request for "
678 "possible native handle and meta-blob (located @ [" << target_meta_blob.data() <<
"] of "
679 "max size [" << target_meta_blob.size() <<
"]). In worker now? = [" << m_worker.in_thread() <<
"].");
681 auto new_user_request = boost::movelib::make_unique<User_request>(in_place_type<User_request_one>);
682 auto& req1 = get<User_request_one>(*new_user_request);
684 req1.m_target_hndl_ptr = target_hndl_or_null;
685 req1.m_target_meta_blob = target_meta_blob;
686 req1.m_on_done_func = std::move(on_done_func);
691 Lock_guard<
decltype(m_mutex)> lock{m_mutex};
695 m_pending_user_requests_q.emplace(std::move(new_user_request));
696 FLOW_LOG_TRACE(
"At least 1 async-receive request is already in progress. "
697 "After registering the new async-receive request: Head slot is non-empty; and "
698 "subsequently-pending deficit queue has size [" << m_pending_user_requests_q.size() <<
"]. "
699 "Will sync-IO-receive to handle this request once it reaches the front of that queue.");
704 FLOW_LOG_TRACE(
"No async-receive request is currently in progress. Starting sync-IO-receive chain to service the "
705 "new request and any further-queued requests that might appear in the meantime.");
706 m_user_request = std::move(new_user_request);
714 sync_receive(req1, &sync_err_code, &sync_sz);
725 process_msg_or_error(sync_err_code, sync_sz);
727 FLOW_LOG_TRACE(
"Message was immediately available; synchronously returned to user; handler posted onto "
728 "async worker thread. Done until next request.");
733template<
typename Core_t>
744 if constexpr(Core::S_TRANSMIT_NATIVE_HANDLES)
754 on_sync_io_rcv_done(err_code, sz);
759 assert(
false &&
"This code should never be reached.");
769 { on_sync_io_rcv_done(err_code, sz); });
772 assert(ok &&
"We are by definition in PEER state, and ctor starts receive-ops, and we never start a "
773 "sync_io async-receive before ensuring previous one has executed; so that should never "
774 "return false. Bug somewhere?");
777template<
typename Core_t>
778template<
typename Batch>
780 flow::async::Task_asio_err&& on_done_func)
782 async_receive_batch_impl<Batch, true>(batch, assume_would_block, std::move(on_done_func));
785template<
typename Core_t>
786template<
typename Batch>
788 flow::async::Task_asio_err&& on_done_func)
790 async_receive_batch_impl<Batch, false>(batch, assume_would_block, std::move(on_done_func));
793template<
typename Core_t>
794template<
typename Batch,
bool HNDL_ELSE_BLOB>
796 bool assume_would_block,
797 flow::async::Task_asio_err&& on_done_func)
799 using flow::async::Task_asio_err;
800 using flow::util::Lock_guard;
801 using std::in_place_type;
815 FLOW_LOG_TRACE(m_log_pfx <<
": Incoming user async-receive-batch request on "
816 "batch [" << *batch <<
"] with assume-would-block? = [" << assume_would_block <<
"]; "
817 "HNDL_ELSE_BLOB = [" << HNDL_ELSE_BLOB <<
"]. "
818 "In worker now? = [" << m_worker.in_thread() <<
"].");
820 auto new_user_request = boost::movelib::make_unique<User_request>(in_place_type<User_request_batch>);
821 auto& req_batch = get<User_request_batch>(*new_user_request);
823 req_batch.m_on_done_func = std::move(on_done_func);
825 req_batch.m_sync_rcv_batch_func = [
this, assume_would_block, batch]
832 if constexpr(HNDL_ELSE_BLOB)
837 m_sync_io.template async_receive_native_handle_batch<typename Batch::Msg_resource>
838 (batch, assume_would_block, sync_err_code,
842 on_sync_io_rcv_done(err_code, 0);
851 m_sync_io.template async_receive_blob_batch<typename Batch::Msg_resource>
852 (batch, assume_would_block, sync_err_code,
853 [
this](
const Error_code& err_code) { on_sync_io_rcv_done(err_code, 0); });
856 assert(ok &&
"We are by definition in PEER state, and ctor starts receive-ops, and we never start a "
857 "sync_io async-receive before ensuring previous one has executed; so that should never "
858 "return false. Bug somewhere?");
862 Lock_guard<
decltype(m_mutex)> lock{m_mutex};
866 m_pending_user_requests_q.emplace(std::move(new_user_request));
867 FLOW_LOG_TRACE(
"At least 1 async-receive request is already in progress. "
868 "After registering the new async-receive request: Head slot is non-empty; and "
869 "subsequently-pending deficit queue has size [" << m_pending_user_requests_q.size() <<
"]. "
870 "Will sync-IO-receive to handle this request once it reaches the front of that queue.");
876 FLOW_LOG_TRACE(
"No async-receive request is currently in progress. Starting sync-IO-receive chain to service the "
877 "new request and any further-queued requests that might appear in the meantime.");
878 m_user_request = std::move(new_user_request);
882 req_batch.m_sync_rcv_batch_func(&sync_err_code);
891 process_msg_or_error(sync_err_code, 0);
893 FLOW_LOG_TRACE(
"In-batch was immediately available; synchronously returned to user; handler posted onto "
894 "async worker thread. Done until next request.");
899template<
typename Core_t>
907 assert(err_code != boost::asio::error::operation_aborted);
910 FLOW_LOG_TRACE(m_log_pfx <<
": Earlier async-wait => event active => rcv-mutex lock => "
911 "on-active-event-func => sync_io module => on-rcv-done handler => here. "
912 "Or else: rcv-mutex lock => sync_io module => no async-wait needed => here.");
946 assert(m_user_request);
947 User_req_ptr ex_user_request{std::move(m_user_request)};
948 assert(!m_user_request);
950 decltype(m_pending_user_requests_q) ex_pending_user_requests_q_or_none;
953 FLOW_LOG_TRACE(
"Error emitted by sync-IO => all [" << m_pending_user_requests_q.size() <<
"] pending "
954 "handlers (may well be none) will be invoked with error in addition to the head handler.");
956 ex_pending_user_requests_q_or_none = std::move(m_pending_user_requests_q);
957 assert(m_pending_user_requests_q.empty());
959 else if (!m_pending_user_requests_q.empty())
961 FLOW_LOG_TRACE(
"Success emitted by sync-IO => lead handler will be invoked; next pending request will "
962 "be serviced by sync-IO; pending request count has become (after popping from queue into lead "
963 "slot): [" << (m_pending_user_requests_q.size() - 1) <<
"].\n");
965 m_user_request = std::move(m_pending_user_requests_q.front());
967 m_pending_user_requests_q.pop();
971 FLOW_LOG_TRACE(
"Success emitted by sync-IO => lead handler will be invoked; no pending requests queued up.\n");
975 m_worker.post([
this, err_code, sz_if_applicable,
978 ex_user_request = boost::shared_ptr<User_request>{std::move(ex_user_request)},
979 ex_pending_user_requests_q_or_none
980 = boost::make_shared<decltype(ex_pending_user_requests_q_or_none)>
981 (std::move(ex_pending_user_requests_q_or_none))]()
986 assert(ex_user_request);
987 FLOW_LOG_TRACE(m_log_pfx <<
": Invoking head handler.");
989 auto& req = *ex_user_request;
990 if (
auto req1 = get_if<User_request_one>(&req))
992 req1->m_on_done_func(err_code, sz_if_applicable);
996 assert((sz_if_applicable == 0) &&
"sz_if_applicable is not applicable to batches; should have been passed as 0.");
997 get<User_request_batch>(req).m_on_done_func(err_code);
1000 FLOW_LOG_TRACE(
"Handler completed.");
1001 if (!ex_pending_user_requests_q_or_none->empty())
1004 assert(sz_if_applicable == 0);
1006 FLOW_LOG_TRACE(m_log_pfx <<
": Invoking [" << ex_pending_user_requests_q_or_none->size() <<
"] "
1007 "pending handlers in one shot (due to error).");
1008 while (!ex_pending_user_requests_q_or_none->empty())
1010 auto& req = *(ex_pending_user_requests_q_or_none->front());
1011 if (
auto req1 = get_if<User_request_one>(&req))
1013 req1->m_on_done_func(err_code, 0);
1017 get<User_request_batch>(req).m_on_done_func(err_code);
1020 ex_pending_user_requests_q_or_none->pop();
1021 FLOW_LOG_TRACE(
"In-queue handler finished.");
1027 assert(!ex_user_request);
1028 assert(ex_pending_user_requests_q_or_none.empty());
1031template<
typename Core_t>
1040 assert(err_code != boost::asio::error::operation_aborted);
1043 FLOW_LOG_TRACE(m_log_pfx <<
": Earlier async-wait => event active => rcv-mutex lock => "
1044 "on-active-event-func => sync_io module => here (on-rcv-done handler).");
1055 process_msg_or_error(err_code, sz_if_applicable);
1058 auto& sync_sz = sz_if_applicable;
1064 auto& req = *m_user_request;
1065 if (
auto req1 = get_if<User_request_one>(&req))
1067 sync_receive(*req1, &sync_err_code, &sync_sz);
1071 get<User_request_batch>(req).m_sync_rcv_batch_func(&sync_err_code);
1082 process_msg_or_error(sync_err_code, sync_sz);
1093template<
typename Core_t>
1096 using flow::util::Lock_guard;
1100 Lock_guard<
decltype(m_mutex)> lock{m_mutex};
1101 return m_sync_io.idle_timer_run(timeout);
1104template<
typename Core_t>
1107 using flow::util::Lock_guard;
1109 Lock_guard<
decltype(m_mutex)> lock{m_mutex};
1110 return m_sync_io.blob_receive_stats();
1113template<
typename Core_t>
1116 using flow::util::Lock_guard;
1118 Lock_guard<
decltype(m_mutex)> lock{m_mutex};
1119 m_sync_io.blob_receive_stats_reset();
1122template<
typename Core_t>
1125 using flow::util::Lock_guard;
1127 Lock_guard<
decltype(m_mutex)> lock{m_mutex};
1128 return m_sync_io.native_handle_receive_stats();
1131template<
typename Core_t>
1134 using flow::util::Lock_guard;
1136 Lock_guard<
decltype(m_mutex)> lock{m_mutex};
1137 m_sync_io.native_handle_receive_stats_reset();
Type that adapts a given PEER-state sync_io::Native_handle_receiver or sync_io::Blob_receiver core in...
flow::async::Single_thread_task_loop & m_worker
Single-thread worker pool for all internal async work. Referred to as thread W in comments.
flow::util::Mutex_non_recursive m_mutex
Protects m_user_request, m_pending_user_requests_q, and receive-ops data of m_sync_io.
User_req_ptr m_user_request
The head slot containing the currently-being-serviced "deficit" async-receive request,...
bool idle_timer_run(util::Fine_duration timeout)
See transport::Native_handle_receiver counterpart.
Async_adapter_receiver(flow::log::Logger *logger_ptr, util::String_view log_pfx, flow::async::Single_thread_task_loop *worker, Core *sync_io)
Constructs the adapter around sync_io::X object *sync_io.
void async_receive_batch_impl(Batch *batch, bool assume_would_block, flow::async::Task_asio_err &&on_done_func)
Body of async_receive_native_handle_batch() and async_receive_blob_batch(); with t-param HNDL_ELSE_BL...
void async_receive_impl(Native_handle *target_hndl_or_null, const util::Blob_mutable &target_meta_blob, flow::async::Task_asio_err_sz &&on_done_func)
Body of async_receive_native_handle() and async_receive_blob(); with target_hndl null if and only if ...
stat::Blob_rcv_stats blob_receive_stats() const
See Native_socket_stream counterpart.
boost::movelib::unique_ptr< User_request > User_req_ptr
Short-hand for smart-pointer handle to User_request.
const std::string m_log_pfx
See log_pfx arg of ctor.
void on_sync_io_rcv_done(const Error_code &err_code, size_t sz_if_applicable)
Invoked via active-event API, handles the async completion of m_sync_io.async_receive_*() operation.
~Async_adapter_receiver()
To be invoked after ->stop()ping *worker (from ctor), as well as flushing any still-queued tasks in i...
void blob_receive_stats_reset()
See Native_socket_stream counterpart.
void async_receive_blob_batch(Batch *batch, bool assume_would_block, flow::async::Task_asio_err &&on_done_func)
See transport::Blob_receiver counterpart.
void process_msg_or_error(const Error_code &err_code, size_t sz_if_applicable)
Invoked from thread U/W (async_receive_impl() or async_receive_batch_impl()) or W (active-event API),...
void sync_receive(const User_request_one &req, Error_code *sync_err_code, size_t *sz)
With the pre-condition that m_user_request is of type User_request_one (that is originating from a si...
std::variant< User_request_one, User_request_batch > User_request
An async-receive request made by user, to be stored in m_user_request and/or m_pending_user_requests_...
void async_receive_native_handle_batch(Batch *batch, bool assume_would_block, flow::async::Task_asio_err &&on_done_func)
See transport::Native_handle_receiver counterpart.
void native_handle_receive_stats_reset()
See Native_socket_stream counterpart.
void async_receive_native_handle(Native_handle *target_hndl, const util::Blob_mutable &target_meta_blob, flow::async::Task_asio_err_sz &&on_done_func)
See transport::Native_handle_receiver counterpart.
void async_receive_blob(const util::Blob_mutable &target_blob, flow::async::Task_asio_err_sz &&on_done_func)
See transport::Blob_receiver counterpart.
Core_t Core
The sync_io::X type being adapted into async-I/O-pattern X.
Core & m_sync_io
The core Core engine, implementing the sync_io pattern (see util::sync_io doc header).
stat::Blob_rcv_stats native_handle_receive_stats() const
See Native_socket_stream counterpart.
std::queue< User_req_ptr > m_pending_user_requests_q
Queue storing deficit async-receive requests queued up due to m_user_request being not null while mor...
Useful if using the sync_io pattern within a user event loop built on boost.asio (optionally with flo...
@ S_SYNC_IO_WOULD_BLOCK
A sync_io operation could not immediately complete; it will complete contingent on active async-wait ...
@ S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER
Async completion handler is being called prematurely, because underlying object is shutting down,...
sync_io-pattern counterparts to async-I/O-pattern object types in parent namespace ipc::transport.
boost::shared_ptr< Task > Task_ptr
Short-hand for ref-counted pointer to a Function<> that takes no arguments and returns nothing; in pa...
boost::asio::mutable_buffer Blob_mutable
Short-hand for a mutable blob somewhere in memory, stored as exactly a void* and a size_t.
flow::Fine_duration Fine_duration
Short-hand for Flow's Fine_duration.
flow::util::String_view String_view
Short-hand for Flow's String_view.
Log_component
The flow::log::Component payload enumeration containing various log components used by Flow-IPC inter...
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
flow::Function< Signature > Function
Short-hand for polymorphic functor holder which is very common. This is essentially std::function.
Cumulative stats from the incoming-direction (receive) pipe of a Blob_receiver or Native_handle_recei...
Analogous to User_request_one but for a batch async-receive request as opposed to single-message requ...
Function< void(Error_code *)> Rcv_batch_func
Short-hand for polymorphic type of function which is the batch-receive equivalent of Async_adapter_re...
Rcv_batch_func m_sync_rcv_batch_func
See Rcv_batch_func doc header. Again though: This is the equivalent of sync_receive() for batch-recei...
flow::async::Task_asio_err m_on_done_func
See async_receive_native_handle_batch() or async_receive_blob_batch() on_done_func.
Data store representing a deficit user single-message async-receive request: either one being current...
Native_handle * m_target_hndl_ptr
See async_receive_native_handle() target_hndl. Null for async_receive_blob().
flow::async::Task_asio_err_sz m_on_done_func
See async_receive_native_handle() or async_receive_blob() on_done_func.
util::Blob_mutable m_target_meta_blob
See async_receive_native_handle() target_meta_blob. Or see async_receive_blob() target_blob.
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.