blob: fa44b04582e2f1e816b235e70ac139876de8f8cb [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use std::io::{self, Read, Write};
use tokio::{io::DuplexStream, sync::oneshot};
use tokio_util::io::SyncIoBridge;
/// Process-lifetime stdio workers own no Host, deployment or effect leases.
/// An open input pipe or blocked output must not hold Tokio shutdown hostage.
pub struct Stdio {
pub input: DuplexStream,
pub output: DuplexStream,
pub output_done: oneshot::Receiver<io::Result<()>>,
}
impl Stdio {
pub fn open() -> io::Result<Self> {
let (input, writer) = tokio::io::duplex(32 * 1024);
let mut writer = SyncIoBridge::new(writer);
let Output {
writer: output,
done: output_done,
} = Output::open("bridge-stdout", io::stdout())?;
std::thread::Builder::new()
.name("bridge-stdin".into())
.spawn(move || {
// EOF drops the duplex writer. The async caller also checks for
// a partial greeting before allowing any Host activation.
let _ = io::copy(&mut io::stdin().lock(), &mut writer);
})?;
Ok(Self {
input,
output,
output_done,
})
}
}
/// Blocking OS writes belong to process-lifetime threads, not Tokio's blocking
/// pool: abandoning a full pipe must not prevent the CLI runtime from exiting.
pub struct Output {
pub writer: DuplexStream,
pub done: oneshot::Receiver<io::Result<()>>,
}
impl Output {
pub fn open(name: &str, mut output: impl Write + Send + 'static) -> io::Result<Self> {
let (writer, reader) = tokio::io::duplex(32 * 1024);
let mut reader = SyncIoBridge::new(reader);
let (done, completion) = oneshot::channel();
std::thread::Builder::new()
.name(name.into())
.spawn(move || {
let result = (|| {
let mut bytes = [0; 16 * 1024];
loop {
let count = reader.read(&mut bytes)?;
if count == 0 {
return output.flush();
}
output.write_all(&bytes[..count])?;
output.flush()?;
}
})();
let _ = done.send(result);
})?;
Ok(Self {
writer,
done: completion,
})
}
}