blob: 42931b5ea05286dbf47a2e857dc5f4ca03877a11 [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 super::*;
use std::sync::Mutex;
use tokio_util::sync::DropGuard;
const ITEMS: usize = 16;
/// A request-local reader. Demand cancellation and executor cleanup are distinct.
/// Read through EOF to drain successful output; completion cancels undrained work.
pub struct Subscription {
accepted: Accepted,
items: Items,
}
/// Shared bounded queue mechanics, independent of query or mutation settlement.
pub(super) struct Items {
receiver: mpsc::Receiver<Value>,
shared: Arc<Shared>,
_cancel_on_drop: DropGuard,
}
impl Subscription {
pub fn id(&self) -> Uuid {
self.accepted.id
}
/// Receive ordered items. Producer completion permits draining already
/// accepted items even if a native handler retained an old Context/future.
pub async fn recv(&mut self) -> Option<Value> {
self.items.recv().await
}
/// Requests stop and closes demand. It does not acknowledge executor cleanup.
pub fn cancel(&mut self) {
self.items.cancel();
}
/// Close demand before awaiting the original owner's independent business
/// and cleanup observations, so undrained backpressure cannot deadlock it.
pub async fn completion(mut self) -> QueryCompletion {
self.cancel();
while self.items.receiver.try_recv().is_ok() {}
self.accepted.completion().await
}
}
impl Items {
pub(super) async fn recv(&mut self) -> Option<Value> {
if let Ok(value) = self.receiver.try_recv() {
return Some(value);
}
if self.shared.closed.is_cancelled() {
return None;
}
tokio::select! {
biased;
value = self.receiver.recv() => value,
() = self.shared.closed.cancelled() => self.receiver.try_recv().ok(),
}
}
fn cancel(&mut self) {
self.shared.stop.cancel();
self.receiver.close();
}
}
struct Shared {
// Only producer close and synchronous queue insertion take this gate.
sender: Mutex<Option<mpsc::Sender<Value>>>,
closed: CancellationToken,
stop: CancellationToken,
output: Arc<jsonschema::Validator>,
}
#[derive(Clone)]
pub(super) struct Emitter(Arc<Shared>);
impl Emitter {
pub(super) fn close(&self) {
let mut sender = self.0.sender.lock().expect("subscription gate poisoned");
self.0.closed.cancel();
sender.take();
}
pub(super) fn emit(&self, value: Value) -> BoxFuture<'static, Result<()>> {
if self.0.closed.is_cancelled() || self.0.stop.is_cancelled() {
return Box::pin(async { Err("Host subscription is closed".into()) });
}
if let Err(error) = self.0.output.validate(&value) {
let error = error.to_string();
return Box::pin(async move { Err(error) });
}
let shared = self.0.clone();
Box::pin(async move {
// Do not give an unpolled future a sender that could keep EOF open.
let sender = shared
.sender
.lock()
.expect("subscription gate poisoned")
.as_ref()
.cloned()
.ok_or("Host subscription is closed")?;
let capacity = tokio::select! {
biased;
() = shared.stop.cancelled() => return Err("Host subscription was cancelled".into()),
() = shared.closed.cancelled() => return Err("Host subscription is closed".into()),
capacity = sender.reserve_owned() => capacity.map_err(|_| "Host subscription reader is closed".to_string())?,
};
let gate = shared.sender.lock().expect("subscription gate poisoned");
if gate.is_none() || shared.stop.is_cancelled() {
return Err("Host subscription is closed".into());
}
// Linearize insertion with producer close; no item can follow EOF.
capacity.send(value);
Ok(())
})
}
}
pub(super) fn channel(
accepted: Accepted,
output: Arc<jsonschema::Validator>,
stop: CancellationToken,
) -> (Subscription, Emitter) {
let (items, emitter) = items(output, stop);
(Subscription { accepted, items }, emitter)
}
/// The caller chooses the cancellation boundary. Mutation demand uses a child
/// token, so dropping its reader never cancels the original execution Scope.
pub(super) fn items(
output: Arc<jsonschema::Validator>,
stop: CancellationToken,
) -> (Items, Emitter) {
let (sender, receiver) = mpsc::channel(ITEMS);
let guard = stop.clone().drop_guard();
let shared = Arc::new(Shared {
sender: Mutex::new(Some(sender)),
closed: CancellationToken::new(),
stop,
output,
});
(
Items {
receiver,
shared: shared.clone(),
_cancel_on_drop: guard,
},
Emitter(shared),
)
}