@@ -100,12 +100,15 @@ class sync_write_handler {
100100 close_sockets (sockets_swapped_);
101101 }
102102
103- // / Add a socket for sync writes (called from client_session after handshake)
103+ // / Send the handshake and register a socket atomically with respect to sample writes.
104104 void add_socket (tcp_socket::native_handle_type handle, tcp_socket::protocol_type protocol,
105- bool reverse_byte_order) {
105+ bool reverse_byte_order, const asio::streambuf::const_buffers_type &header ) {
106106 std::lock_guard<std::mutex> lock (mutex_);
107107 auto sock = std::make_unique<tcp_socket>(io_ctx_, protocol, handle);
108108 apply_send_timeout (*sock);
109+ // An inlet may return from open_stream() as soon as these bytes arrive.
110+ // Keep sample writes locked out until the socket is registered below.
111+ asio::write (*sock, header);
109112 if (reverse_byte_order) {
110113 sockets_swapped_.push_back (std::move (sock));
111114 LOG_F (INFO , " Added sync socket (swapped endian), now have %zu native + %zu swapped" ,
@@ -259,7 +262,8 @@ class client_session : public std::enable_shared_from_this<client_session> {
259262 int request_protocol_version, const std::string &request_uid, err_t err);
260263
261264 // / Handler that gets called sending the feedheader has completed.
262- void handle_send_feedheader_outcome (err_t err, std::size_t n);
265+ void handle_send_feedheader_outcome (
266+ err_t err, std::size_t n, std::shared_ptr<consumer_queue> queue);
263267
264268 // / Transfers samples from the server's send buffer into the async send queues of IO threads
265269 void transfer_samples_thread (std::shared_ptr<client_session> /* keepalive*/ ,
@@ -706,18 +710,34 @@ void client_session::handle_read_feedparams(
706710 *outarch_ << *temp;
707711 }
708712
709- // send off the newly created feedheader
710- async_write (
711- sock_, feedbuf_.data (), [shared_this = shared_from_this ()](err_t err, std::size_t len) {
712- shared_this->handle_send_feedheader_outcome (err, len);
713+ if (max_buffered_ > 0 && serv->is_sync_mode ()) {
714+ // The synchronous writer sends the header under its sample-write lock,
715+ // then registers the socket before an immediate push can acquire it.
716+ auto protocol = sock_.local_endpoint ().protocol ();
717+ serv->sync_handler_ ->add_socket (
718+ sock_.release (), protocol, reverse_byte_order_, feedbuf_.data ());
719+ serv->unregister_inflight_session (this );
720+ return ;
721+ }
722+
723+ // Subscribe before sending the handshake: receiving its test patterns is
724+ // what lets the inlet return from open_stream(). Queue samples until the
725+ // header write completes so data cannot overtake the handshake.
726+ std::shared_ptr<consumer_queue> queue;
727+ if (max_buffered_ > 0 ) queue = serv->send_buffer_ ->new_consumer (max_buffered_);
728+ async_write (sock_, feedbuf_.data (),
729+ [shared_this = shared_from_this (), queue = std::move (queue)](
730+ err_t err, std::size_t len) mutable {
731+ shared_this->handle_send_feedheader_outcome (err, len, std::move (queue));
713732 });
714733 DLOG_F (2 , " %p sent test pattern samples" , this );
715734 } catch (std::exception &e) {
716735 LOG_F (WARNING , " Unexpected error while serializing the feed header: %s" , e.what ());
717736 }
718737}
719738
720- void client_session::handle_send_feedheader_outcome (err_t err, std::size_t n) {
739+ void client_session::handle_send_feedheader_outcome (
740+ err_t err, std::size_t n, std::shared_ptr<consumer_queue> queue) {
721741 try {
722742 if (err) return ;
723743
@@ -730,22 +750,6 @@ void client_session::handle_send_feedheader_outcome(err_t err, std::size_t n) {
730750 // convenient for unit tests
731751 if (max_buffered_ <= 0 ) return ;
732752
733- // If server is in sync mode, hand off the socket to the sync handler
734- if (serv->is_sync_mode ()) {
735- LOG_F (INFO , " Handing off socket to sync handler for zero-copy transfer (reverse_byte_order=%d)" ,
736- reverse_byte_order_);
737- auto protocol = sock_.local_endpoint ().protocol ();
738- // Release the socket from this io_context and add to sync handler
739- // See https://stackoverflow.com/q/52671836/73299
740- serv->sync_handler_ ->add_socket (sock_.release (), protocol, reverse_byte_order_);
741- // Unregister this session since we're handing off the socket
742- serv->unregister_inflight_session (this );
743- return ;
744- }
745-
746- // determine transfer parameters
747- auto queue = serv->send_buffer_ ->new_consumer (max_buffered_);
748-
749753 // determine the maximum chunk size
750754 int max_samples_per_chunk = std::numeric_limits<int >::max ();
751755 if (chunk_granularity_)
0 commit comments