Skip to content

Commit d77b260

Browse files
committed
Refactored downlink read task for clarity.
1 parent 1bc18c9 commit d77b260

File tree

1 file changed

+152
-88
lines changed

1 file changed

+152
-88
lines changed

runtime/swim_runtime/src/downlink/mod.rs

Lines changed: 152 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::downlink::failure::BadFrameResponse;
2020
use crate::timeout_coord::{VoteResult, Voter};
2121
use backpressure::DownlinkBackpressure;
2222
use bitflags::bitflags;
23-
use bytes::BytesMut;
23+
use bytes::{Bytes, BytesMut};
2424
use futures::future::{join, select, Either};
2525
use futures::stream::SelectAll;
2626
use futures::{Future, FutureExt, Sink, SinkExt, Stream, StreamExt};
@@ -32,12 +32,12 @@ use swim_messages::protocol::{
3232
RawResponseMessageDecoder, ResponseMessage,
3333
};
3434
use swim_model::address::RelativeAddress;
35-
use swim_model::Text;
35+
use swim_model::{BytesStr, Text};
3636
use swim_utilities::future::{immediate_or_join, immediate_or_start, SecondaryResult};
3737
use swim_utilities::io::byte_channel::{ByteReader, ByteWriter};
3838
use swim_utilities::trigger;
3939
use tokio::sync::mpsc;
40-
use tokio::time::{timeout, timeout_at, Instant};
40+
use tokio::time::{timeout, Instant};
4141
use tokio_stream::wrappers::ReceiverStream;
4242
use tokio_util::codec::{Decoder, Encoder, FramedRead, FramedWrite};
4343
use tracing::{error, info, info_span, trace, warn, Instrument};
@@ -421,7 +421,7 @@ async fn attach_task<F>(
421421
}
422422

423423
#[derive(Debug, PartialEq, Eq)]
424-
enum ReadTaskState {
424+
enum ReadTaskDlState {
425425
Init,
426426
Linked,
427427
Synced,
@@ -519,6 +519,15 @@ impl<D: Decoder> Stream for DownlinkReceiver<D> {
519519
}
520520
}
521521

522+
enum ReadTaskEvent {
523+
Message(ResponseMessage<BytesStr, Bytes, Bytes>),
524+
ReadFailed(std::io::Error),
525+
MessagesStopped,
526+
NewConsumer(ByteWriter, DownlinkOptions),
527+
ConsumerChannelStopped,
528+
ConsumersTimedOut,
529+
}
530+
522531
/// Consumes incoming messages from the remote lane and passes them to the consumers.
523532
async fn read_task<I, H>(
524533
input: ByteReader,
@@ -539,135 +548,190 @@ where
539548

540549
let mut consumer_stream = ReceiverStream::new(consumers);
541550

542-
let mut state = ReadTaskState::Init;
551+
let make_timeout = || tokio::time::sleep_until(Instant::now() + config.empty_timeout);
552+
let mut task_state = pin!(Some(make_timeout()));
553+
let mut dl_state = ReadTaskDlState::Init;
543554
let mut current = BytesMut::new();
544555
let mut awaiting_synced: Vec<DownlinkSender> = vec![];
545556
let mut awaiting_linked: Vec<DownlinkSender> = vec![];
546557
let mut registered: Vec<DownlinkSender> = vec![];
547558

548-
let mut empty_timestamp = Some(Instant::now() + config.empty_timeout);
549-
let result = loop {
550-
let next_item = if let Some(timestamp) = empty_timestamp {
551-
if voted {
552-
consumer_stream.next().await.map(Either::Right)
553-
} else if let Ok(result) = timeout_at(timestamp, consumer_stream.next()).await {
554-
result.map(Either::Right)
555-
} else {
559+
let result: Result<(), H::Report> = loop {
560+
let (event, is_active) = match task_state.as_mut().as_pin_mut() {
561+
Some(sleep) if !voted => (
562+
tokio::select! {
563+
biased;
564+
_ = sleep => {
565+
task_state.set(None);
566+
ReadTaskEvent::ConsumersTimedOut
567+
},
568+
maybe_consumer = consumer_stream.next() => {
569+
if let Some((consumer, options)) = maybe_consumer {
570+
ReadTaskEvent::NewConsumer(consumer, options)
571+
} else {
572+
ReadTaskEvent::ConsumerChannelStopped
573+
}
574+
},
575+
maybe_message = messages.next() => {
576+
match maybe_message {
577+
Some(Ok(msg)) => ReadTaskEvent::Message(msg),
578+
Some(Err(err)) => ReadTaskEvent::ReadFailed(err),
579+
_ => ReadTaskEvent::MessagesStopped,
580+
}
581+
}
582+
},
583+
false,
584+
),
585+
_ => {
586+
let get_next = async {
587+
tokio::select! {
588+
maybe_consumer = consumer_stream.next() => {
589+
if let Some((consumer, options)) = maybe_consumer {
590+
ReadTaskEvent::NewConsumer(consumer, options)
591+
} else {
592+
ReadTaskEvent::ConsumerChannelStopped
593+
}
594+
},
595+
maybe_message = messages.next() => {
596+
match maybe_message {
597+
Some(Ok(msg)) => ReadTaskEvent::Message(msg),
598+
Some(Err(err)) => ReadTaskEvent::ReadFailed(err),
599+
_ => ReadTaskEvent::MessagesStopped,
600+
}
601+
}
602+
}
603+
};
604+
if flushed {
605+
trace!("Waiting without flush.");
606+
(get_next.await, true)
607+
} else {
608+
trace!("Waiting with flush.");
609+
let flush = join(flush_all(&mut awaiting_synced), flush_all(&mut registered));
610+
let next_with_flush = immediate_or_join(get_next, flush);
611+
let (next, flush_result) = next_with_flush.await;
612+
let is_active = if flush_result.is_some() {
613+
trace!("Flush completed.");
614+
flushed = true;
615+
if registered.is_empty() && awaiting_synced.is_empty() {
616+
trace!("Number of subscribers dropped to 0.");
617+
task_state.set(Some(make_timeout()));
618+
false
619+
} else {
620+
true
621+
}
622+
} else {
623+
true
624+
};
625+
(next, is_active)
626+
}
627+
}
628+
};
629+
630+
match event {
631+
ReadTaskEvent::ConsumersTimedOut => {
556632
info!("No consumers connected within the timeout period. Voting to stop.");
557633
if stop_voter.vote() == VoteResult::Unanimous {
558634
// No consumers registered within the timeout and the write task has voted to stop.
559635
break Ok(());
560636
} else {
561637
voted = true;
562-
consumer_stream.next().await.map(Either::Right)
563-
}
564-
}
565-
} else if flushed {
566-
trace!("Waiting without flush.");
567-
match select(consumer_stream.next(), messages.next()).await {
568-
Either::Left((consumer, _)) => consumer.map(Either::Right),
569-
Either::Right((msg, _)) => msg.map(Either::Left),
570-
}
571-
} else {
572-
trace!("Waiting with flush.");
573-
let get_next = select(consumer_stream.next(), messages.next());
574-
let flush = join(flush_all(&mut awaiting_synced), flush_all(&mut registered));
575-
let next_with_flush = immediate_or_join(get_next, flush);
576-
let (next, flush_result) = next_with_flush.await;
577-
if flush_result.is_some() {
578-
trace!("Flush completed.");
579-
if registered.is_empty() && awaiting_synced.is_empty() {
580-
trace!("Number of subscribers dropped to 0.");
581-
empty_timestamp = Some(Instant::now() + config.empty_timeout);
582638
}
583-
flushed = true;
584-
}
585-
match next {
586-
Either::Left((consumer, _)) => consumer.map(Either::Right),
587-
Either::Right((msg, _)) => msg.map(Either::Left),
588639
}
589-
};
590-
591-
match next_item {
592-
Some(Either::Left(Ok(ResponseMessage { envelope, .. }))) => match envelope {
640+
ReadTaskEvent::Message(ResponseMessage { envelope, .. }) => match envelope {
593641
Notification::Linked => {
594642
trace!("Entering Linked state.");
595-
state = ReadTaskState::Linked;
596-
link(&mut awaiting_linked, &mut awaiting_synced, &mut registered).await;
597-
if awaiting_synced.is_empty() && registered.is_empty() {
598-
empty_timestamp = Some(Instant::now() + config.empty_timeout);
643+
dl_state = ReadTaskDlState::Linked;
644+
if is_active {
645+
link(&mut awaiting_linked, &mut awaiting_synced, &mut registered).await;
646+
if awaiting_synced.is_empty() && registered.is_empty() {
647+
trace!("Number of subscribers dropped to 0.");
648+
task_state.set(Some(make_timeout()));
649+
}
599650
}
600651
}
601652
Notification::Synced => {
602653
trace!("Entering Synced state.");
603-
state = ReadTaskState::Synced;
604-
if I::SINGLE_FRAME_STATE {
605-
sync_current(&mut awaiting_synced, &mut registered, &current).await;
606-
} else {
607-
sync_only(&mut awaiting_synced, &mut registered).await;
608-
}
609-
if registered.is_empty() {
610-
empty_timestamp = Some(Instant::now() + config.empty_timeout);
654+
dl_state = ReadTaskDlState::Synced;
655+
if is_active {
656+
if I::SINGLE_FRAME_STATE {
657+
sync_current(&mut awaiting_synced, &mut registered, &current).await;
658+
} else {
659+
sync_only(&mut awaiting_synced, &mut registered).await;
660+
}
661+
if registered.is_empty() {
662+
trace!("Number of subscribers dropped to 0.");
663+
task_state.set(Some(make_timeout()));
664+
}
611665
}
612666
}
667+
Notification::Unlinked(message) => {
668+
trace!("Stopping after unlinked: {msg:?}", msg = message);
669+
break Ok(());
670+
}
613671
Notification::Event(bytes) => {
614-
trace!("Dispatching an event.");
672+
trace!("Updating the current value.");
615673
current.clear();
616674
if let Err(e) = interpretation.interpret_frame_data(bytes, &mut current) {
617675
if let BadFrameResponse::Abort(report) = failure_handler.failed_with(e) {
618676
break Err(report);
619677
}
620678
}
621-
send_current(&mut registered, &current).await;
622-
if !I::SINGLE_FRAME_STATE {
623-
send_current(&mut awaiting_synced, &current).await;
624-
}
625-
if registered.is_empty() && awaiting_synced.is_empty() {
626-
trace!("Number of subscribers dropped to 0.");
627-
empty_timestamp = Some(Instant::now() + config.empty_timeout);
628-
flushed = true;
629-
} else {
630-
flushed = false;
679+
if is_active {
680+
send_current(&mut registered, &current).await;
681+
if !I::SINGLE_FRAME_STATE {
682+
send_current(&mut awaiting_synced, &current).await;
683+
}
684+
if registered.is_empty() && awaiting_synced.is_empty() {
685+
trace!("Number of subscribers dropped to 0.");
686+
task_state.set(Some(make_timeout()));
687+
flushed = true;
688+
} else {
689+
flushed = false;
690+
}
631691
}
632692
}
633-
Notification::Unlinked(message) => {
634-
trace!("Stopping after unlinked: {msg:?}", msg = message);
635-
break Ok(());
636-
}
637693
},
638-
Some(Either::Right((writer, options))) => {
639-
if voted {
640-
if stop_voter.rescind() == VoteResult::Unanimous {
641-
info!("Attempted to rescind stop vote but shutdown had already started.");
642-
break Ok(());
643-
} else {
644-
voted = false;
645-
}
646-
}
694+
ReadTaskEvent::ReadFailed(err) => {
695+
error!(
696+
"Failed to read a frame from the input: {error}",
697+
error = err
698+
);
699+
break Ok(());
700+
}
701+
ReadTaskEvent::NewConsumer(writer, options) => {
647702
let mut dl_writer = DownlinkSender::new(writer, options);
648-
if matches!(state, ReadTaskState::Init) {
703+
let added = if matches!(dl_state, ReadTaskDlState::Init) {
649704
trace!("Attaching a new subscriber to be linked.");
650-
empty_timestamp = None;
651705
awaiting_linked.push(dl_writer);
706+
true
652707
} else if dl_writer.send(DownlinkNotification::Linked).await.is_ok() {
653708
trace!("Attaching a new subscriber to be synced.");
654-
empty_timestamp = None;
655709
awaiting_synced.push(dl_writer);
710+
true
711+
} else {
712+
false
713+
};
714+
if added {
715+
task_state.set(None);
716+
if voted {
717+
if stop_voter.rescind() == VoteResult::Unanimous {
718+
info!(
719+
"Attempted to rescind stop vote but shutdown had already started."
720+
);
721+
break Ok(());
722+
} else {
723+
voted = false;
724+
}
725+
}
656726
}
657727
}
658-
Some(Either::Left(Err(err))) => {
659-
error!(
660-
"Failed to read a frame from the input: {error}",
661-
error = err
662-
);
663-
break Ok(());
664-
}
665728
_ => {
666729
trace!("Instructed to stop.");
667730
break Ok(());
668731
}
669732
}
670733
};
734+
671735
trace!("Read task stopping and unlinked all subscribers");
672736
unlink(awaiting_linked).await;
673737
unlink(awaiting_synced).await;

0 commit comments

Comments
 (0)