21#include <boost/make_shared.hpp>
22#include <boost/array.hpp>
34 return start_ops<Op::S_RCV>(std::move(ev_wait_func));
47 flow::async::Task_asio_err_sz&& on_done_func)
49 assert(!on_done_func.empty());
57 assert(target_hndl &&
"Native_socket_stream::async_receive_native_handle() must take non-null Native_handle ptr.");
60 std::move(on_done_func));
65 flow::async::Task_asio_err_sz&& on_done_func)
67 assert(!on_done_func.empty());
68 return async_receive_impl(
nullptr, target_blob, sync_err_code, sync_sz, std::move(on_done_func));
74 flow::async::Task_asio_err_sz&& on_done_func)
76 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
bool,
78 target_hndl_or_null, target_meta_blob, _1, sync_sz, std::move(on_done_func));
80 auto& sync_err_code = *err_code;
82 if ((!op_started<Op::S_RCV>(
"async_receive_impl()"))
93 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Async-receive requested, but the preceding such request "
94 "is still in progress; the message has not arrived yet. "
95 "Likely a user error, but who are we to judge? Ignoring.");
100 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: User async-receive request for "
101 "possible native handle and meta-blob (located @ [" << target_meta_blob.data() <<
"] of "
102 "max size [" << target_meta_blob.size() <<
"]).");
106 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request for "
107 "possible native handle and meta-blob (located @ [" << target_meta_blob.data() <<
"] of "
108 "max size [" << target_meta_blob.size() <<
"]): Error already encountered earlier. Emitting "
116 async_receive_core(target_hndl_or_null,
false, target_meta_blob, &sync_err_code, sync_sz, std::move(on_done_func));
122 rcv_log_stats(
"async_receive_impl(): while sync-processing rcv-pipe hosed");
130 bool assume_would_block,
133 flow::async::Task_asio_err_sz&& on_done_func_or_none)
135 assert(sync_err_code);
137 sync_err_code->clear();
167 "See large-ish comment above this place in the code.");
172 if (!on_done_func_or_none.empty())
192 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: User async-receive request for "
193 "possible native handle and meta-blob (located @ [" << target_meta_blob.data() <<
"] of "
194 "max size [" << target_meta_blob.size() <<
"]): Encountered corner-case situation wherein "
195 "a batch-receive-emulation op earlier encountered a would-block in the middle of "
196 "reading a message having already received 1+ complete in-messages; which meant it had to "
197 "finish the batch-receive op and leave the state machine to resume at that point in the next "
198 "async-receive op (that is we). Executing the steps to place the state machine in that "
199 "state again and resume.");
213 resume_func(sync_err_code, sync_sz);
228 assert(
m_rcv_user_request->m_own_hndl.get().null() &&
"Move-from Owned_nh=>Nh should have nullified the former.");
234 FLOW_LOG_TRACE(
"Async-request completed synchronously (result "
235 "[" << *sync_err_code <<
"] [" << sync_err_code->message() <<
"]); emitting synchronously and "
236 "disregarding handler.");
247 using boost::chrono::round;
248 using boost::chrono::milliseconds;
252 assert(timeout.count() > 0);
254 if ((!op_started<Op::S_RCV>(
"idle_timer_run()")) || (!
state_peer(
"idle_timer_run()")))
263 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User wants to start idle timer, but they have already "
264 "started it before. Therefore ignoring.");
274 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: User wants to start idle timer, but an error has already "
275 "been found and emitted earlier. It's moot; ignoring.");
280 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: User wants to start idle-timer with timeout "
281 "[" << round<milliseconds>(
m_rcv_idle_timeout) <<
"]. Scheduling (will be rescheduled as needed).");
290 boost::make_shared<Task>
310 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Idle timer fired: There's been 0 traffic past idle timeout. "
311 "However an error has already been found and emitted earlier. Therefore ignoring.");
319 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Idle timer fired: There's been 0 traffic past idle timeout. "
320 "Will not proceed with any further low-level receiving. If a user async-receive request is "
322 "will emit to completion handler.");
326 rcv_log_stats(
"rcv_on_ev_idle_timer_fired(): idle timeout fired, rcv-pipe hosed");
335 assert((!on_done_func.empty())
336 &&
"Empty m_on_done_func => that m_rcv_user_request should have been nullified before returning "
337 "from the async-receive API, hence before the timer fired and we were called. Bug.");
340 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Handler completed.");
347 assert((!on_done_func.empty())
348 &&
"Empty m_on_done_func => that m_rcv_user_batch_request should have been nullified before returning "
349 "from the async-receive API, hence before the timer fired and we were called. Bug.");
351 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Handler completed.");
378 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: Finished reading a message, which means "
379 "we just received traffic, which means the idle timer should be rescheduled. However "
380 "when trying to reschedule it, we found we were too late: it was very recently queued to "
381 "be invoked in the near future. An idle timeout error shall be emitted very soon.");
385 assert((n_canceled == 1) &&
"We only issue 1 timer async_wait() at a time.");
401 using flow::util::Lock_guard;
404 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
405 "static_assert() rather than run-time.");
408 if (assume_would_block)
415 const bool proto_negotiating
418 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Async-receive: "
419 "Trying nb-read of in-dgram synchronously; proto-negotiating? = [" << proto_negotiating <<
"].");
424 const auto n_rcvd_or_zero
434 if (n_rcvd_or_zero != 0)
436 FLOW_LOG_TRACE(
"Got in-dgram.");
438 rcv_on_dgram(std::move(target_hndl), n_rcvd_or_zero, sync_err_code, sync_sz);
445 FLOW_LOG_TRACE(
"Got nothing but would-block. This is part of a sub-op (as of this writing when emulating "
446 "batch-receive using single-receives) in which would-block means we end the op and do *not* "
447 "await readability; so we emit would-block and stop the overall receive op.");
455 FLOW_LOG_TRACE(
"Got nothing but would-block. Awaiting readability.");
471 boost::make_shared<Task>([
this]()
483 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request: "
484 "was about to await readability but discovered opposite-direction socket-hosing error; "
485 "emitting error via completion handler (or via sync-args).");
492 FLOW_LOG_TRACE(
"Nb-read, or async-read following nb-read encountering would-block, detected error (details "
493 "above); will emit via completion handler (or via sync-args). Most errors hose the pipe "
494 "in both directions; however as of this writing MESSAGE_SIZE_EXCEEDS_USER_STORAGE "
495 "hoses only in in-direction while the out-direction pipe is usable.");
508 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
509 "static_assert() rather than run-time. Anyway... bug; we should never be called because "
510 "!USE_OS_DGRAM_SUPPORT.");
515 bool proto_negotiating
517 const bool hndl_is_null = hndl_or_null.get().null();
519 if (proto_negotiating && (!hndl_is_null))
521 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Expecting protocol-negotiation (first) in-dgram "
522 "to contain *only* a meta-blob: but received Native_handle is non-null which is "
523 "unexpected; emitting error via completion handler (or via sync-args).");
528 assert(ok &&
"Protocol_negotiator breaking contract? Bug?");
530 &&
"Protocol_negotiator should have emitted error given intentionally bad version.");
535 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request for "
536 "*only* a meta-blob: but received Native_handle is non-null which is "
537 "unexpected; emitting error via completion handler (or via sync-args).");
547 && (!proto_negotiating))
563 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Received in-dgram contains invalid header (opposing "
565 "but the entire in-dgram has size only [" << n_rcvd <<
"]; "
566 "emitting error via completion handler (or via sync-args).");
575 if (proto_negotiating)
579 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Expecting protocol-negotiation (first) in-dgram "
580 "to contain *only* the header: but received more bytes "
582 "unexpected; emitting error via completion handler (or via sync-args).");
595 assert(ok &&
"Protocol_negotiator breaking contract? Bug?");
596 proto_negotiating =
false;
602 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of negotiation payload; passed. "
603 "Ignoring other than registering non-idle activity. Proceeding with the next dgram read.");
620 sync_err_code->clear();
623 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received in-dgram with user message sized "
624 "[" << *sync_sz <<
"]. Will register non-idle activity; "
625 "and invoke handler (or report via sync-args).");
637 if (no_data && (!hndl_is_null))
639 sync_err_code->clear();
642 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received in-dgram; header=0 + non-null handle + "
643 "no data => handle received with no meta-blob. Will register non-idle activity; "
644 "and invoke handler (success).");
657 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: Received in-dgram: Graceful-close-of-incoming-pipe "
658 "message. Will not proceed with any further low-level receiving. "
659 "Will invoke handler (graceful-close error).");
683 if (no_data && hndl_is_null)
685 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received in-dgram; header "
686 "contains special value indicating a ping. Ignoring other than registering non-idle "
687 "activity. Proceeding with the next dgram read.");
697 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Received in-dgram contains invalid header (opposing "
698 "side misbehaved/bug?): it contains PING_SENTINEL, but a ping shall contain no "
699 "further data and no native handle, yet we received either more bytes "
701 "a handle (got one? = [" << (!hndl_is_null) <<
"]) or both; "
702 "emitting error via completion handler (or via sync-args).");
707 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Received in-dgram contains invalid header (opposing "
710 "are allowed; we also possibly received more bytes "
712 "emitting error via completion handler (or via sync-args).");
737 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
738 "static_assert() rather than run-time.");
742 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User's wait-for-readable finished (readable or error, "
743 "we do not know which yet); would resume processing depending on what we were doing before; "
744 "however an error was detected in the meantime (as of this writing: idle timeout). "
745 "Stopping read chain.");
747 &&
"If rcv-error emitted during low-level async-wait, we should have fed it to any pending async-receive.");
758 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: User-performed wait-for-readable finished (readable or error, "
759 "we do not know which yet). Retrying to resume read chain.");
774 rcv_log_stats(
"rcv_on_ev_peer_socket_pkt_stream_readable_or_error(): while processing ev-ready rcv-pipe hosed");
777 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Async-op result ready after successful async-wait. "
778 "Executing handler now.");
789 assert(
m_rcv_user_request->m_own_hndl.get().null() &&
"Move-from Owned_nh=>Nh should have nullified the former.");
795 assert((!on_done_func.empty())
796 &&
"Empty m_on_done_func => that m_rcv_user_request should have been nullified without issuing async-wait, "
797 "so we should not have been called. Bug.");
799 on_done_func(sync_err_code, sync_sz);
800 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Handler completed.");
809 using flow::util::Lock_guard;
812 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
813 "static_assert() rather than run-time.");
815 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Async-receive: Start of payload 1: "
816 "Trying nb-read of payload 1 (handle if any, meta-blob-length) synchronously.");
819 if (assume_would_block)
829 const auto n_rcvd_or_zero
836 if (n_rcvd_or_zero != 0)
838 FLOW_LOG_TRACE(
"Got some or all of payload 1.");
846 FLOW_LOG_TRACE(
"Got nothing but would-block. This is part of a sub-op (as of this writing when emulating "
847 "batch-receive using single-receives) in which would-block means we end the op and do *not* "
848 "await readability; so we emit would-block and stop the overall receive op.");
856 FLOW_LOG_TRACE(
"Got nothing but would-block. Awaiting readability.");
872 boost::make_shared<Task>([
this]()
885 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request: "
886 "was about to await readability but discovered opposite-direction socket-hosing error; "
887 "emitting error via completion handler (or via sync-args).");
894 FLOW_LOG_TRACE(
"Nb-read, or async-read following nb-read encountering would-block, detected error (details "
895 "above); will emit via completion handler (or via sync-args).");
907 using flow::util::Lock_guard;
910 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
911 "static_assert() rather than run-time.");
916 const bool proto_negotiating
918 const bool hndl_is_null = hndl_or_null.get().null();
920 if (proto_negotiating && (!hndl_is_null))
922 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Expecting protocol-negotiation (first) in-message "
923 "to contain *only* a meta-blob: but received Native_handle is non-null which is "
924 "unexpected; emitting error via completion handler (or via sync-args).");
929 assert(ok &&
"Protocol_negotiator breaking contract? Bug?");
931 &&
"Protocol_negotiator should have emitted error given intentionally bad version.");
936 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request for "
937 "*only* a meta-blob: but received Native_handle is non-null which is "
938 "unexpected; emitting error via completion handler (or via sync-args).");
948 && (!proto_negotiating))
965 FLOW_LOG_TRACE(
"Got would-block after only partially reading the meta-length/msg-type from the byte stream. "
966 "This is part of a sub-op (as of this writing when emulating "
967 "batch-receive using single-receives) in which would-block means we end the op and do *not* "
968 "await readability; so we emit would-block and stop the overall receive op. However saving "
969 "the partial-message info, and any subsequent receive-op will resume from here.");
978 assert((!proto_negotiating) &&
"This corner case should only happen after already banking 1+ user in-message, "
979 "so protocol negotiation should have already succeeded.");
981 &&
"This should have been nullified at the start of the async-receive.");
995 if (hndl_or_null.get().null())
1001 owned_hndl_or_null = boost::make_shared<Own_native_handle>(std::move(hndl_or_null)),
1007 target_meta_length_incomplete, n_rcvd, {});
1009 assert(hndl_or_null.get().null() &&
"The move-from should have nullified the local hndl_or_null.");
1026 boost::make_shared<Task>([
this, n_rcvd]()
1039 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request: "
1040 "was about to await readability but discovered opposite-direction socket-hosing error; "
1041 "emitting error via completion handler (or via sync-args).");
1059 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
1060 "static_assert() rather than run-time.");
1064 bool proto_negotiating
1067 if (proto_negotiating)
1082 assert(ok &&
"Protocol_negotiator breaking contract? Bug?");
1083 proto_negotiating =
false;
1094 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of negotiation payload; passed. "
1095 "Ignoring other than registering non-idle activity. Proceeding with the next message read.");
1109 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of payload 1; length prefix "
1110 "contains special value indicating a ping. Ignoring other than registering non-idle "
1111 "activity. Proceeding with the next message read.");
1122 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Received all of payload 1; length prefix "
1123 "contains special value indicating a ping; "
1124 "however it also contains a native handle which is a protocol violation. "
1125 "Treating similarly to a graceful-close but with a bad error code and "
1126 "this warning. Will not proceed with any further low-level receiving; will invoke "
1127 "handler (failure).");
1138 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of payload 1; length prefix "
1140 "Reading payload 2.");
1144 sync_err_code, sync_sz);
1149 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Received all of payload 1; length prefix "
1151 "however it exceeds user target blob size [" << user_target_size <<
"] and would "
1152 "overflow. Treating similarly to a graceful-close but with a bad error code and "
1153 "this warning. Will not proceed with any further low-level receiving; will invoke "
1154 "handler (failure).");
1166 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of payload 1; length 0 + non-null handle => "
1167 "handle received with no meta-blob. Will register non-idle activity; "
1168 "and invoke handler (success).");
1181 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: User message received: Graceful-close-of-incoming-pipe "
1182 "message. Will not proceed with any further low-level receiving. "
1183 "Will invoke handler (graceful-close error).");
1207 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
1208 "static_assert() rather than run-time.");
1212 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User's wait-for-readable finished (readable or error, "
1213 "we do not know which yet); would resume processing depending on what we were doing before; "
1214 "however an error was detected in the meantime (as of this writing: idle timeout). "
1215 "Stopping read chain.");
1217 &&
"If rcv-error emitted during low-level async-wait, we should have fed it to any pending async-receive.");
1228 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: User-performed wait-for-readable finished (readable or error, "
1229 "we do not know which yet). Resuming processing depending on what we were doing before.");
1234 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: In state MSG_START: Reading from byte 0/handle if any.");
1239 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: In state HEAD_PAYLOAD: "
1240 "Reading meta-length/ping/graceful-close specifier: [" << n_left <<
"] bytes left.");
1245 &sync_err_code, &sync_sz);
1249 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: In state META_BLOB_PAYLOAD: "
1250 "Reading meta-blob: [" << n_left <<
"] bytes left.");
1255 &sync_err_code, &sync_sz);
1269 rcv_log_stats(
"rcv_on_ev_peer_socket_byte_stream_readable_or_error(): while processing ev-ready rcv-pipe hosed");
1272 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Async-op result ready after successful async-wait. "
1273 "Executing handler now.");
1284 assert(
m_rcv_user_request->m_own_hndl.get().null() &&
"Move-from Owned_nh=>Nh should have nullified the former.");
1290 assert((!on_done_func.empty())
1291 &&
"Empty m_on_done_func => that m_rcv_user_request should have been nullified without issuing async-wait, "
1292 "so we should not have been called. Bug.");
1294 on_done_func(sync_err_code, sync_sz);
1295 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Handler completed.");
1304 using flow::util::Lock_guard;
1305 using flow::util::Blob;
1308 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
1309 "static_assert() rather than run-time.");
1317 if (n_rcvd_or_zero == target_blob.size())
1326 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of payload 2 (meta-blob of length "
1328 "and invoke handler (or report via sync-args).");
1334 assert(!*sync_err_code);
1348 assert(
false &&
"rcv_read_blob() shall be used only for S_*_PAYLOAD phases.");
1350 assert(
false &&
"Should not get here.");
1361 FLOW_LOG_TRACE(
"Got would-block after only partially reading the requested blob. "
1362 "This is part of a sub-op (as of this writing when emulating "
1363 "batch-receive using single-receives) in which would-block means we end the op and do *not* "
1364 "await readability; so we emit would-block and stop the overall receive op. However saving "
1365 "the partial-message info, and any subsequent receive-op will resume from here.");
1377 &&
"This corner case should only happen after already banking 1+ user in-message, "
1378 "so protocol negotiation should have already succeeded.");
1380 &&
"This should have been nullified at the start of the async-receive.");
1392 - target_blob.size() + n_rcvd_or_zero]
1396 target_meta_length_incomplete,
1397 target_meta_length_incomplete_n_rcvd, {});
1400 &&
"The move-from should have nullified m_own_hndl.");
1409 Blob target_blob_incomplete{get_logger()};
1412 - target_blob.size() + n_rcvd_or_zero;
1413 if (target_blob_incomplete_sz != 0)
1422 target_blob_incomplete_sz});
1430 target_blob_incomplete = std::move(target_blob_incomplete)]
1434 target_meta_length, 0, target_blob_incomplete);
1437 &&
"The move-from should have nullified m_own_hndl.");
1447 FLOW_LOG_TRACE(
"Do not have all of requested payload; got would-block. Awaiting readability.");
1457 boost::make_shared<Task>([
this, msg_state,
1458 n_left = target_blob.size() - n_rcvd_or_zero]()
1460 rcv_on_ev_peer_socket_byte_stream_readable_or_error(msg_state, n_left);
1482 size_t target_meta_length_incomplete_n_rcvd_or_zero_if_complete,
1483 const flow::util::Blob& target_blob_incomplete)
1487 assert(
m_rcv_user_request &&
"We should be called from top-level-ish async-receive....");
1494 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: While replaying: User async-receive request for "
1495 "*only* a meta-blob: but earlier-received Native_handle is non-null which is "
1496 "unexpected; emitting error.");
1512 if (target_meta_length_incomplete_n_rcvd_or_zero_if_complete != 0)
1519 + target_meta_length_incomplete_n_rcvd_or_zero_if_complete,
1521 - target_meta_length_incomplete_n_rcvd_or_zero_if_complete},
1530 &&
"Got all of m_rcv_target_meta_length, and it is zero, yet instructed to finish reading meta-blob... "
1531 "but there should have been none; the message should have been completed; and we should not have "
1532 "been invoked at all. Bug on someone's part?");
1534 if (!target_blob_incomplete.empty())
1536 target_blob_incomplete.sub_copy(target_blob_incomplete.begin(),
1537 Blob_mutable{m_rcv_user_request->m_target_meta_blob.data(),
1538 target_blob_incomplete.size()});
1546 + target_blob_incomplete.size(),
1548 - target_blob_incomplete.size()},
1555 using flow::util::Lock_guard;
1572 FLOW_LOG_TRACE(
"Got nothing but would-block (pre-assumed). Awaiting readability.");
1593 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request (pre-assumed would-block): "
1594 "was about to await readability but discovered opposite-direction socket-hosing error; "
1595 "emitting error via completion handler (or via sync-args).");
1608 using flow::util::Lock_guard;
1616 &&
"@todo Really this function should be made to not compile in this case, and this then should be "
1617 "static_assert() rather than run-time.");
1619 assert(target_payload_hndl &&
"Our contract is we must have a target Native_handle; rationale is in our doc header.");
1628 size_t n_rcvd_or_zero = 0;
1649 if (target_payload_blob2_or_none.size() == 0)
1651 n_rcvd_or_zero = nb_read_some_with_native_handle<Native_socket_stream_cfg::Protocol>
1652 (get_logger(),
m_peer_socket.get(), &hndl, target_payload_blob1, err_code);
1656 array<Blob_mutable, 2> target_buf_seq = { target_payload_blob1, target_payload_blob2_or_none };
1657 n_rcvd_or_zero = nb_read_some_with_native_handle<Native_socket_stream_cfg::Protocol>
1658 (get_logger(),
m_peer_socket.get(), &hndl, target_buf_seq, err_code);
1662 target_payload_hndl->reset(std::move(hndl));
1668 assert(((!*err_code) && (n_rcvd_or_zero != 0))
1669 || (*err_code && (n_rcvd_or_zero == 0)));
1670 if (*err_code == boost::asio::error::would_block)
1682 assert((!
m_peer_socket_hosed) &&
"m_peer_socket_hosed must start as null and only become non-null once. Bug?");
1684 assert((!
m_peer_socket) &&
"Shocking unique_ptr misbehavior!");
1712 || (n_rcvd_or_zero == 0));
1716 FLOW_LOG_TRACE(
"Received nothing due to error [" << *err_code <<
"] [" << err_code->message() <<
"].");
1720 FLOW_LOG_TRACE(
"Receive: no error. Was able to receive [" << n_rcvd_or_zero <<
"] of "
1721 "[" << (target_payload_blob1.size() + target_payload_blob2_or_none.size()) <<
"] bytes; "
1722 "interest in native handle: got [" << target_payload_hndl->get() <<
"].");
1725 return n_rcvd_or_zero;
1728template<
typename Ignored>
1734 using flow::util::Lock_guard;
1741 &&
"Really this function should be made to not compile in this case, and this then should be "
1742 "static_assert() rather than run-time. There is a @todo lower-down in this function to that effect.");
1751 size_t n_rcvd_or_zero = 0;
1759 if (target_payload_hndl_or_null)
1766 n_rcvd_or_zero = nb_read_some_with_native_handle<Native_socket_stream_cfg::Protocol>
1767 (get_logger(),
m_peer_socket.get(), &hndl, target_payload_blob, err_code);
1770 target_payload_hndl_or_null->reset(std::move(hndl));
1780 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Setting boost.asio peer socket non-blocking mode.");
1792 FLOW_LOG_TRACE(
"Reading low-level blob directly via boost.asio (blob details logged above hopefully).");
1816 ([&](
auto& sock) ->
auto {
return sock->receive(target_payload_blob, 0, *err_code); })
1821 assert(
false &&
"We are not a template so would have been compiled even though USE_OS_DGRAM_SUPPORT; "
1822 "but we should not have been *called* ever.");
1839 assert(((!*err_code) && (n_rcvd_or_zero != 0))
1840 || (*err_code && (n_rcvd_or_zero == 0)));
1841 if (*err_code == boost::asio::error::would_block)
1853 assert((!
m_peer_socket_hosed) &&
"m_peer_socket_hosed must start as null and only become non-null once. Bug?");
1855 assert((!
m_peer_socket) &&
"Shocking unique_ptr misbehavior!");
1868 || (n_rcvd_or_zero == 0));
1872 FLOW_LOG_TRACE(
"Received nothing due to error [" << *err_code <<
"] [" << err_code->message() <<
"].");
1876 FLOW_LOG_TRACE(
"Receive: no error. Was able to receive [" << n_rcvd_or_zero <<
"] of "
1877 "[" << target_payload_blob.size() <<
"] bytes.");
1878 if (target_payload_hndl_or_null)
1880 if (n_rcvd_or_zero != 0)
1882 FLOW_LOG_TRACE(
"Interest in native handle; got [" << target_payload_hndl_or_null->get() <<
"].");
1887 FLOW_LOG_TRACE(
"No interest in native handle.");
1891 return n_rcvd_or_zero;
1896 using flow::util::stat::print;
1900 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: In context [" << context <<
"]: Stats: "
uint16_t low_lvl_payload_blob_length_t
The type used to encode the internal meta-blob length/type-indicator for sync_io::Native_socket_strea...
static constexpr low_lvl_payload_blob_length_t S_PING_SENTINEL
For sync_io::Native_socket_stream and derivative(s): Value for the length/type-indicator field in eac...
static constexpr size_t S_MAX_META_BLOB_LENGTH
The maximum length of a blob that can be sent by the protocol internally used by sync_io::Native_sock...
static constexpr bool S_USE_OS_DGRAM_SUPPORT
Whether the OS provides, and we shall use for sync_io::Native_socket_stream and derivative(s),...
int16_t proto_ver_t
Type sufficient to store a protocol version; positive values identify newer versions of a protocol; w...
bool compute_negotiated_proto_ver(proto_ver_t opposing_max_proto_ver, Error_code *err_code=nullptr)
Based on the presumably-just-received-from-opposing-side value of their local_max_proto_ver,...
proto_ver_t negotiated_proto_ver() const
Returns S_VER_UNKNOWN before compute_negotiated_proto_ver(); then either the positive version of the ...
static constexpr proto_ver_t S_VER_UNKNOWN
A proto_ver_t value, namely a negative one, which is a reserved value indicating "unknown version"; i...
void blob_receive_stats_reset()
See Native_socket_stream counterpart.
util::sync_io::Timer_event_emitter m_timer_worker
As typical in timer-needing sync_io-pattern-implementing objects, maintains a thread exclusively for ...
util::sync_io::Event_wait_func m_rcv_ev_wait_func
Function (set forever in start_receive_*_ops()) through which we invoke the outside event loop's asyn...
decltype(m_peer_socket) m_peer_socket_hosed
Null to start, this takes on the value from m_peer_socket when and only when m_peer_socket is nullifi...
util::Fine_duration m_rcv_idle_timeout
timeout from idle_timer_run() args; or zero() if not yet called.
std::optional< Rcv_user_request > m_rcv_user_request
Null if no single-message async-receive request is currently pending; else describes the arguments to...
util::sync_io::Asio_waitable_native_handle m_rcv_ev_wait_hndl_idle_timer_fired_peer
Descriptor waitable by outside event loop async-waits – storing the same Native_handle as (and thus b...
void rcv_read_msg_from_byte_stream(bool assume_would_block, Error_code *sync_err_code, size_t *sync_sz)
Begins read chain (completing it as synchronously as possible, async-completing the rest) for the nex...
void rcv_on_ev_peer_socket_byte_stream_readable_or_error(Rcv_msg_state msg_state, size_t n_left)
Completion handler, from outside event loop via sync_io pattern, for the async-wait initiated by vari...
Blob_rcv_stats blob_receive_stats() const
See Native_socket_stream counterpart.
size_t rcv_nb_read_low_lvl_payload_from_pkt_stream(util::Own_native_handle *target_payload_hndl, const util::Blob_mutable &target_payload_blob1, const util::Blob_mutable &target_payload_blob2_or_none, Error_code *err_code)
Similar to rcv_nb_read_low_lvl_payload_from_byte_stream() but for Protocol_pkt_stream instead of Prot...
Function< void(Error_code *, size_t *)> m_rcv_resume_incomplete_msg_processing_func
Relevant only if Protocol_byte_stream was chosen (at compile-time) for m_peer_socket,...
flow::util::Timer m_rcv_idle_timer
Timer that fires rcv_on_ev_idle_timer_fired() (which hoses the in-pipe with idle timeout error) and i...
bool async_receive_impl(Native_handle *target_hndl_or_null, const util::Blob_mutable &target_meta_blob, Error_code *sync_err_code, size_t *sync_sz, flow::async::Task_asio_err_sz &&on_done_func)
Body of both async_receive_native_handle() and async_receive_blob().
void rcv_on_ev_idle_timer_fired()
Handler for the async-wait, via util::sync_io::Timer_event_emitter, of the idle timer firing; if stil...
bool start_receive_native_handle_ops(util::sync_io::Event_wait_func &&ev_wait_func)
See Native_socket_stream counterpart.
util::sync_io::Asio_waitable_native_handle m_ev_wait_hndl_peer_socket
Descriptor waitable by outside event loop async-waits – storing the same Native_handle as (and thus b...
bool async_receive_blob(const util::Blob_mutable &target_blob, Error_code *sync_err_code, size_t *sync_sz, flow::async::Task_asio_err_sz &&on_done_func)
See Native_socket_stream counterpart.
size_t rcv_nb_read_low_lvl_payload_from_byte_stream(util::Own_native_handle *target_payload_hndl_or_null, const util::Blob_mutable &target_payload_blob, Error_code *err_code)
Utility that synchronously, non-blockingly attempts to read over m_peer_socket into the target blob a...
void rcv_on_ev_peer_socket_pkt_stream_readable_or_error()
Completion handler, from outside event loop via sync_io pattern, for the async-wait initiated by the ...
void rcv_read_blob(Rcv_msg_state msg_state, const util::Blob_mutable &target_blob, Error_code *sync_err_code, size_t *sync_sz)
A somewhat-general utility that continues read chain with the aim to complete the present in-message,...
size_t receive_meta_blob_max_size() const
See Native_socket_stream counterpart.
boost::movelib::unique_ptr< asio_local_stream_socket::Peer_socket< Native_socket_stream_cfg::Protocol > > m_peer_socket
The peer Unix domain socket; or null pointer if we've detected the connection has become hosed and he...
Rcv_msg_state
Used to organize the incoming-direction state machine tactically, this indicates what part of payload...
@ S_HEAD_PAYLOAD
Reading payload 1, but at least byte 2: already have at least byte 1 and either the Native_handle or ...
@ S_MSG_START
Reading start of payload 1: already have nothing – no Native_handle; no 1st byte.
@ S_META_BLOB_PAYLOAD
Reading payload 2; M bytes remain, where M is in [1, N], and N is the meta-length encoded in precedin...
bool state_peer(util::String_view context) const
Checks whether m_state has reached State::S_PEER; if so returns true; if not logs WARNING and returns...
void rcv_on_handle_finalized(util::Own_native_handle &&hndl_or_null, size_t n_rcvd, Error_code *sync_err_code, size_t *sync_sz)
Helper of rcv_read_msg_from_byte_stream() – it could have been inlined instead of a method but for re...
size_t receive_blob_max_size() const
See Native_socket_stream counterpart.
flow::util::Mutex_non_recursive m_peer_socket_mutex
Protects m_peer_socket and its bros m_ev_wait_hndl_peer_socket and m_peer_socket_hosed.
void rcv_on_dgram(util::Own_native_handle &&hndl_or_null, size_t n_rcvd, Error_code *sync_err_code, size_t *sync_sz)
Reacts to an in-datagram (hence possible user in-message) having been received.
std::optional< Rcv_user_batch_request > m_rcv_user_batch_request
Null if no native async-batch-receive request is currently pending; else describes the arguments to t...
Protocol_negotiator m_protocol_negotiator
Handles the protocol negotiation at the start of the pipe.
void native_handle_receive_stats_reset()
See Native_socket_stream counterpart.
void rcv_read_msg_from_stream_having_assumed_would_block(Error_code *sync_err_code, size_t *sync_sz)
Helper for rcv_read_msg_from_{pkt|byte}_stream() that handles the assume_would_block == true case,...
Native_socket_stream_cfg::low_lvl_payload_blob_length_t m_rcv_target_meta_length
Direct-write target, encoding the message type of the message currently being received; and possibly ...
void rcv_on_head_payload(Error_code *sync_err_code, size_t *sync_sz)
Reacts to payload 1 having been completely received.
Blob_rcv_stats native_handle_receive_stats() const
See Native_socket_stream counterpart.
void async_receive_core(Native_handle *target_hndl_or_null, bool assume_would_block, const util::Blob_mutable &target_meta_blob, Error_code *sync_err_code, size_t *sync_sz, flow::async::Task_asio_err_sz &&on_done_func_or_none)
Core of either async_receive_impl() (if user invoked single-message receiving directly) or the functi...
util::sync_io::Timer_event_emitter::Timer_fired_read_end * m_rcv_idle_timer_fired_peer
Read-end of IPC-mechanism used by m_timer_worker to ferry timer-fired events from m_rcv_idle_timer to...
State m_state
The current state of *this.
Error_code m_rcv_pending_err_code
The first and only connection-hosing error condition detected when attempting to low-level-read on m_...
bool idle_timer_run(util::Fine_duration timeout)
See Native_socket_stream counterpart.
@ S_PEER
Is or was a connected peer.
void rcv_not_idle()
No-ops if idle_timer_run() is not engaged; otherwise reacts to non-idleness of the in-pipe by resched...
bool start_receive_blob_ops(util::sync_io::Event_wait_func &&ev_wait_func)
See Native_socket_stream counterpart.
void rcv_read_msg_from_pkt_stream(bool assume_would_block, Error_code *sync_err_code, size_t *sync_sz)
Analogous to rcv_read_msg_from_byte_stream() but used if m_peer_socket is of socket-type Protocol_pkt...
void rcv_log_stats(util::String_view context) const
INFO-logs current rcv-half stats: as log_stats() but callable from receive-op context (see "Which hal...
void rcv_resume_incomplete_msg_processing(Error_code *err_code, size_t *sz, util::Own_native_handle &&hndl_or_null, Native_socket_stream_cfg::low_lvl_payload_blob_length_t target_meta_length_possibly_incomplete, size_t target_meta_length_incomplete_n_rcvd_or_zero_if_complete, const flow::util::Blob &target_blob_incomplete)
Core of what is assigned, when necessary, to m_rcv_resume_incomplete_msg_processing_func; at start of...
stat::Blob_rcv_stats m_rcv_stats
Incoming-direction (receive-pipe) stats.
bool async_receive_native_handle(Native_handle *target_hndl, const util::Blob_mutable &target_meta_blob, Error_code *sync_err_code, size_t *sync_sz, flow::async::Task_asio_err_sz &&on_done_func)
See Native_socket_stream counterpart.
static constexpr bool S_BLOB_UNDERFLOW_ALLOWED
Implements concept API; namely it is true.
void consume_timer_firing_signal(Timer_fired_read_end *read_end)
Must be called after a timer_async_wait()-triggered timer fired, before invoking that method for the ...
void timer_async_wait(flow::util::Timer *timer, Timer_fired_read_end *read_end)
To be used on a timer T returned by create_timer(), this is the replacement for the usual T....
size_t nb_read_some_with_native_handle(flow::log::Logger *logger_ptr, Peer_socket< Protocol > *peer_socket_ptr, Native_handle *target_payload_hndl_ptr, const Mutable_buffer_sequence &target_payload_blob, Error_code *err_code, int message_flags)
boost.asio extension similar to peer_socket->non_blocking(true); auto n = peer_socket->receive(target...
@ S_LOW_LVL_TRANSPORT_HOSED_CANNOT_RECEIVE
Unable to receive incoming traffic: an earlier-reported, or at least logged, system error had hosed t...
@ S_LOW_LVL_INTERNAL_PROTOCOL_INVALID_HEADER
Unable to receive incoming traffic: Opposing side sent invalid internal per-message header.
@ S_SYNC_IO_WOULD_BLOCK
A sync_io operation could not immediately complete; it will complete contingent on active async-wait ...
@ S_RECEIVES_FINISHED_CANNOT_RECEIVE
Will not receive message: opposing user sent graceful-close via API.
@ S_RECEIVER_IDLE_TIMEOUT
No messages (optional auto-pings or otherwise) have been received; optionally configured timeout exce...
@ S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE
User protocol-code mismatch: local user-provided storage cannot fit entire message received from oppo...
@ S_BLOB_RECEIVER_GOT_NON_BLOB
User protocol-code mismatch: local user expected blob only and no native handle; received at least th...
sync_io-pattern counterparts to async-I/O-pattern object types in parent namespace ipc::transport.
Function< void(Asio_waitable_native_handle *hndl_of_interest, bool ev_of_interest_snd_else_rcv, Task_ptr &&on_active_ev_func)> Event_wait_func
In sync_io pattern, concrete type storing user-supplied function invoked by pattern-implementing ipc:...
Native_handle disowned_native_handle(Own_native_handle &&src) noexcept
Returns Native_handle constructed by relieving an existing auto-closing Own_native_handle of its owne...
flow::async::Task Task
Short-hand for polymorphic function (a-la std::function<>) that takes no arguments and returns nothin...
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.
boost::asio::const_buffer Blob_const
Short-hand for an immutable blob somewhere in memory, stored as exactly a void const * and a size_t.
flow::util::String_view String_view
Short-hand for Flow's String_view.
boost::scope::unique_resource< Native_handle, Native_handle::Closer, Native_handle > Own_native_handle
A unique_resource wrapper for Native_handle useful to avoid descriptor leaks and double-closing; can ...
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Cumulative stats from the incoming-direction (receive) pipe of a Blob_receiver or Native_handle_recei...
uint64_t m_total_bytes
Total user blob bytes received (sum of in-message blob sizes).
uint64_t m_total_msgs
Total user messages fully received and delivered.
uint64_t m_idle_timeouts
Number of idle-timeout events. For a {Blob|Native}_handle_receiver as of this writing this cannot exc...
uint64_t m_total_low_lvl_bytes
Total low-level bytes received (user blob bytes plus protocol headers and framing).
uint64_t m_msgs_with_hndls
Number of messages received that carried at least one native handle. Currently at most 1 handle per m...
uint64_t m_auto_pings
Number of auto-pings received from the opposing sender.
flow::util::stat::Histogram_counter m_histo_payload_sz
Payload-size histogram for received user messages.
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.