use std::sync::Arc;
use std::thread::{self, ThreadId};
use futures::executor::{self, Notify, Spawn};
use futures::{Async, Future};
use super::lock::SpinLock;
use super::CallTag;
use crate::call::Call;
use crate::cq::CompletionQueue;
use crate::error::{Error, Result};
use crate::grpc_sys::{self, GrpcCallStatus};
type BoxFuture<T, E> = Box<dyn Future<Item = T, Error = E> + Send>;
type SpawnHandle = Option<Spawn<BoxFuture<(), ()>>>;
pub(crate) struct Kicker {
call: Call,
}
impl Kicker {
pub fn from_call(call: Call) -> Kicker {
Kicker { call }
}
pub fn kick(&self, tag: Box<CallTag>) -> Result<()> {
let _ref = self.call.cq.borrow()?;
unsafe {
let ptr = Box::into_raw(tag);
let status = grpc_sys::grpcwrap_call_kick_completion_queue(self.call.call, ptr as _);
if status == GrpcCallStatus::Ok {
Ok(())
} else {
Err(Error::CallFailure(status))
}
}
}
}
unsafe impl Sync for Kicker {}
impl Clone for Kicker {
fn clone(&self) -> Kicker {
let call = unsafe {
grpc_sys::grpc_call_ref(self.call.call);
self.call.call
};
let cq = self.call.cq.clone();
Kicker {
call: Call { call, cq },
}
}
}
struct NotifyContext {
kicked: bool,
kicker: Kicker,
}
impl NotifyContext {
fn notify(&mut self, tag: Box<CallTag>) {
match self.kicker.kick(tag) {
Err(Error::QueueShutdown) => return,
Err(e) => panic!("unexpected error when canceling call: {:?}", e),
_ => (),
}
}
}
#[derive(Clone)]
pub struct SpawnNotify {
ctx: Arc<SpinLock<NotifyContext>>,
handle: Arc<SpinLock<SpawnHandle>>,
worker_id: ThreadId,
}
impl SpawnNotify {
fn new(s: Spawn<BoxFuture<(), ()>>, kicker: Kicker, worker_id: ThreadId) -> SpawnNotify {
SpawnNotify {
worker_id,
handle: Arc::new(SpinLock::new(Some(s))),
ctx: Arc::new(SpinLock::new(NotifyContext {
kicked: false,
kicker,
})),
}
}
pub fn resolve(self, success: bool) {
assert!(success);
poll(&Arc::new(self.clone()), true);
}
}
impl Notify for SpawnNotify {
fn notify(&self, _: usize) {
if thread::current().id() == self.worker_id {
poll(&Arc::new(self.clone()), false)
} else {
let mut ctx = self.ctx.lock();
if ctx.kicked {
return;
}
ctx.notify(Box::new(CallTag::Spawn(self.clone())));
ctx.kicked = true;
}
}
}
fn poll(notify: &Arc<SpawnNotify>, woken: bool) {
let mut handle = notify.handle.lock();
if woken {
notify.ctx.lock().kicked = false;
}
if handle.is_none() {
return;
}
match handle.as_mut().unwrap().poll_future_notify(notify, 0) {
Err(_) | Ok(Async::Ready(_)) => {
handle.take();
return;
}
_ => {}
}
}
pub(crate) struct Executor<'a> {
cq: &'a CompletionQueue,
}
impl<'a> Executor<'a> {
pub fn new(cq: &CompletionQueue) -> Executor<'_> {
Executor { cq }
}
pub fn cq(&self) -> &CompletionQueue {
self.cq
}
pub fn spawn<F>(&self, f: F, kicker: Kicker)
where
F: Future<Item = (), Error = ()> + Send + 'static,
{
let s = executor::spawn(Box::new(f) as BoxFuture<_, _>);
let notify = Arc::new(SpawnNotify::new(s, kicker, self.cq.worker_id()));
poll(¬ify, false)
}
}