1use flume::{bounded, r#async::RecvFut, unbounded, Sender};
2use std::{collections::HashMap, fmt::Debug, future::Future, sync::Mutex};
3
4use sea_streamer_types::{
5 export::futures::FutureExt, Buffer, Message, MessageHeader, Producer as ProducerTrait, Receipt,
6 SeqNo, ShardId, SharedMessage, StreamErr, StreamKey, StreamResult, Timestamp,
7};
8
9use crate::{PartialHeader, StdioErr, StdioResult, BROADCAST, TIMESTAMP_FORMAT};
10
11lazy_static::lazy_static! {
12 static ref PRODUCERS: Mutex<Producers> = Default::default();
13 static ref THREAD: Mutex<Option<Sender<Signal>>> = Mutex::new(None);
14}
15
16#[derive(Debug, Default)]
17struct Producers {
18 sequences: HashMap<StreamKey, SeqNo>,
19}
20
21enum Signal {
22 SendRequest {
23 message: SharedMessage,
24 receipt: Sender<Receipt>,
25 loopback: bool,
26 },
27 Shutdown,
28}
29
30#[derive(Debug, Clone)]
31pub struct StdioProducer {
32 stream: Option<StreamKey>,
33 request: Sender<Signal>,
34 loopback: bool,
35}
36
37pub struct SendFuture {
38 fut: RecvFut<'static, Receipt>,
39}
40
41const ZERO: u64 = 0;
42
43pub(crate) fn init() {
44 let mut thread = THREAD.lock().expect("Failed to lock stdout thread");
45 if thread.is_none() {
46 let (sender, receiver) = unbounded();
47 let builder = std::thread::Builder::new().name("sea-streamer-stdio-stdout".into());
48 builder
49 .spawn(move || {
50 log::debug!("[{pid}] stdout thread spawned", pid = std::process::id());
51 let mut producers = PRODUCERS
53 .try_lock()
54 .expect("Should have no other thread trying to access Producers");
55 while let Ok(signal) = receiver.recv() {
56 match signal {
57 Signal::SendRequest {
58 mut message,
59 receipt,
60 loopback,
61 } => {
62 message.touch(); if message.message().size() != 0 {
78 let stream_key = message.stream_key();
79 let seq = producers.append(&stream_key);
80 println!(
81 "[{timestamp} | {stream} | {seq}] {payload}",
82 timestamp = message
83 .timestamp()
84 .format(TIMESTAMP_FORMAT)
85 .expect("Timestamp format error"),
86 stream = stream_key,
87 seq = seq,
88 payload = message
89 .message()
90 .as_str()
91 .expect("Should have already checked is valid string"),
92 );
93 if loopback {
94 let payload = message.message();
95 super::consumer::dispatch(
96 PartialHeader {
97 timestamp: Some(message.timestamp()),
98 stream_key: Some(stream_key),
99 sequence: Some(seq),
100 shard_id: Some(message.shard_id()),
101 },
102 payload.into_bytes(),
103 0,
104 );
105 }
106 }
107 let meta = message.take_header();
108 receipt.send(meta).ok();
110 }
111 Signal::Shutdown => break,
112 }
113 }
114 log::debug!("[{pid}] stdout thread exit", pid = std::process::id());
115 {
116 let mut thread = THREAD.lock().expect("Failed to lock stdout thread");
117 thread.take(); }
119 })
120 .unwrap();
121 thread.replace(sender);
122 }
123}
124
125pub(crate) fn shutdown() {
126 let thread = THREAD.lock().expect("Failed to lock stdout thread");
127 if let Some(sender) = thread.as_ref() {
128 sender
129 .send(Signal::Shutdown)
130 .expect("stdout thread might have been shutdown already");
131 }
132}
133
134pub(crate) fn shutdown_already() -> bool {
135 let thread = THREAD.lock().expect("Failed to lock stdout thread");
136 thread.is_none()
137}
138
139impl Producers {
140 fn append(&mut self, stream: &StreamKey) -> SeqNo {
142 if let Some(val) = self.sequences.get_mut(stream) {
143 let seq = *val;
144 *val += 1;
145 seq
146 } else {
147 self.sequences.insert(stream.to_owned(), 1);
148 0
149 }
150 }
151}
152
153impl Future for SendFuture {
154 type Output = StreamResult<MessageHeader, StdioErr>;
155
156 fn poll(
157 mut self: std::pin::Pin<&mut Self>,
158 cx: &mut std::task::Context<'_>,
159 ) -> std::task::Poll<Self::Output> {
160 match self.fut.poll_unpin(cx) {
161 std::task::Poll::Ready(res) => std::task::Poll::Ready(match res {
162 Ok(res) => Ok(res),
163 Err(err) => Err(StreamErr::Backend(StdioErr::RecvError(err))),
164 }),
165 std::task::Poll::Pending => std::task::Poll::Pending,
166 }
167 }
168}
169
170impl Debug for SendFuture {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 f.debug_struct("SendFuture").finish()
173 }
174}
175
176impl ProducerTrait for StdioProducer {
177 type Error = StdioErr;
178 type SendFuture = SendFuture;
179
180 fn send_to<S: Buffer>(&self, stream: &StreamKey, payload: S) -> StdioResult<Self::SendFuture> {
181 let payload = payload.as_str().map_err(StreamErr::Utf8Error)?.to_owned();
182 let (sender, receiver) = bounded(1);
184 let size = payload.len();
185 self.request
186 .send(Signal::SendRequest {
187 message: SharedMessage::new(
188 MessageHeader::new(
189 stream.to_owned(),
190 ShardId::new(ZERO),
191 ZERO,
192 Timestamp::now_utc(),
193 ),
194 payload.into_bytes(),
195 0,
196 size,
197 ),
198 receipt: sender,
199 loopback: self.loopback,
200 })
201 .map_err(|_| StreamErr::Backend(StdioErr::Disconnected))?;
202 Ok(SendFuture {
203 fut: receiver.into_recv_async(),
204 })
205 }
206
207 #[inline]
208 async fn end(mut self) -> StdioResult<()> {
209 self.flush().await
210 }
211
212 #[inline]
213 async fn flush(&mut self) -> StdioResult<()> {
214 self.send_to(&StreamKey::new(BROADCAST)?, "")?.await?;
218 Ok(())
219 }
220
221 fn anchor(&mut self, stream: StreamKey) -> StdioResult<()> {
222 if self.stream.is_none() {
223 self.stream = Some(stream);
224 Ok(())
225 } else {
226 Err(StreamErr::AlreadyAnchored)
227 }
228 }
229
230 fn anchored(&self) -> StdioResult<&StreamKey> {
231 if let Some(stream) = &self.stream {
232 Ok(stream)
233 } else {
234 Err(StreamErr::NotAnchored)
235 }
236 }
237}
238
239impl StdioProducer {
240 #[allow(clippy::new_without_default)]
241 pub fn new() -> Self {
242 Self::new_with(false)
243 }
244
245 pub(crate) fn new_with(loopback: bool) -> Self {
246 init();
247 let request = {
248 let thread = THREAD.lock().expect("Failed to lock stdout thread");
249 thread.as_ref().expect("Should have initialized").to_owned()
250 };
251 Self {
252 stream: None,
253 request,
254 loopback,
255 }
256 }
257}