• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2 
3 mod callback;
4 mod executor;
5 mod promise;
6 
7 use std::fmt::{self, Debug, Formatter};
8 use std::pin::Pin;
9 use std::sync::Arc;
10 
11 use futures::future::Future;
12 use futures::task::{Context, Poll, Waker};
13 use parking_lot::Mutex;
14 
15 use self::callback::{Abort, Request as RequestCallback, UnaryRequest as UnaryRequestCallback};
16 use self::executor::SpawnTask;
17 use self::promise::{Action as ActionPromise, Batch as BatchPromise};
18 use crate::call::server::RequestContext;
19 use crate::call::{BatchContext, Call, MessageReader};
20 use crate::cq::CompletionQueue;
21 use crate::error::{Error, Result};
22 use crate::server::RequestCallContext;
23 
24 pub(crate) use self::executor::{Executor, Kicker, UnfinishedWork};
25 pub use self::promise::BatchType;
26 
27 /// A handle that is used to notify future that the task finishes.
28 pub struct NotifyHandle<T> {
29     result: Option<Result<T>>,
30     waker: Option<Waker>,
31     stale: bool,
32 }
33 
34 impl<T> NotifyHandle<T> {
new() -> NotifyHandle<T>35     fn new() -> NotifyHandle<T> {
36         NotifyHandle {
37             result: None,
38             waker: None,
39             stale: false,
40         }
41     }
42 
43     /// Set the result and notify future if necessary.
set_result(&mut self, res: Result<T>) -> Option<Waker>44     fn set_result(&mut self, res: Result<T>) -> Option<Waker> {
45         self.result = Some(res);
46 
47         self.waker.take()
48     }
49 }
50 
51 type Inner<T> = Mutex<NotifyHandle<T>>;
52 
new_inner<T>() -> Arc<Inner<T>>53 fn new_inner<T>() -> Arc<Inner<T>> {
54     Arc::new(Mutex::new(NotifyHandle::new()))
55 }
56 
57 /// Get the future status without the need to poll.
58 ///
59 /// If the future is polled successfully, this function will return None.
60 /// Not implemented as method as it's only for internal usage.
check_alive<T>(f: &CqFuture<T>) -> Result<()>61 pub fn check_alive<T>(f: &CqFuture<T>) -> Result<()> {
62     let guard = f.inner.lock();
63     match guard.result {
64         None => Ok(()),
65         Some(Err(Error::RpcFailure(ref status))) => {
66             Err(Error::RpcFinished(Some(status.to_owned())))
67         }
68         Some(Ok(_)) | Some(Err(_)) => Err(Error::RpcFinished(None)),
69     }
70 }
71 
72 /// A future object for task that is scheduled to `CompletionQueue`.
73 pub struct CqFuture<T> {
74     inner: Arc<Inner<T>>,
75 }
76 
77 impl<T> CqFuture<T> {
new(inner: Arc<Inner<T>>) -> CqFuture<T>78     fn new(inner: Arc<Inner<T>>) -> CqFuture<T> {
79         CqFuture { inner }
80     }
81 }
82 
83 impl<T> Future for CqFuture<T> {
84     type Output = Result<T>;
85 
poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>86     fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
87         let mut guard = self.inner.lock();
88         if guard.stale {
89             panic!("Resolved future is not supposed to be polled again.");
90         }
91 
92         if let Some(res) = guard.result.take() {
93             guard.stale = true;
94             return Poll::Ready(res);
95         }
96 
97         // So the task has not been finished yet, add notification hook.
98         if guard.waker.is_none() || !guard.waker.as_ref().unwrap().will_wake(cx.waker()) {
99             guard.waker = Some(cx.waker().clone());
100         }
101 
102         Poll::Pending
103     }
104 }
105 
106 /// Future object for batch jobs.
107 pub type BatchFuture = CqFuture<Option<MessageReader>>;
108 
109 /// A result holder for asynchronous execution.
110 // This enum is going to be passed to FFI, so don't use trait or generic here.
111 pub enum CallTag {
112     Batch(BatchPromise),
113     Request(RequestCallback),
114     UnaryRequest(UnaryRequestCallback),
115     Abort(Abort),
116     Action(ActionPromise),
117     Spawn(Arc<SpawnTask>),
118 }
119 
120 impl CallTag {
121     /// Generate a Future/CallTag pair for batch jobs.
batch_pair(ty: BatchType) -> (BatchFuture, CallTag)122     pub fn batch_pair(ty: BatchType) -> (BatchFuture, CallTag) {
123         let inner = new_inner();
124         let batch = BatchPromise::new(ty, inner.clone());
125         (CqFuture::new(inner), CallTag::Batch(batch))
126     }
127 
128     /// Generate a CallTag for request job. We don't have an eventloop
129     /// to pull the future, so just the tag is enough.
request(ctx: RequestCallContext) -> CallTag130     pub fn request(ctx: RequestCallContext) -> CallTag {
131         CallTag::Request(RequestCallback::new(ctx))
132     }
133 
134     /// Generate a Future/CallTag pair for action call that only cares if the result is
135     /// successful.
action_pair() -> (CqFuture<bool>, CallTag)136     pub fn action_pair() -> (CqFuture<bool>, CallTag) {
137         let inner = new_inner();
138         let action = ActionPromise::new(inner.clone());
139         (CqFuture::new(inner), CallTag::Action(action))
140     }
141 
142     /// Generate a CallTag for abort call before handler is called.
abort(call: Call) -> CallTag143     pub fn abort(call: Call) -> CallTag {
144         CallTag::Abort(Abort::new(call))
145     }
146 
147     /// Generate a CallTag for unary request job.
unary_request(ctx: RequestContext, rc: RequestCallContext) -> CallTag148     pub fn unary_request(ctx: RequestContext, rc: RequestCallContext) -> CallTag {
149         let cb = UnaryRequestCallback::new(ctx, rc);
150         CallTag::UnaryRequest(cb)
151     }
152 
153     /// Get the batch context from result holder.
batch_ctx(&self) -> Option<&BatchContext>154     pub fn batch_ctx(&self) -> Option<&BatchContext> {
155         match *self {
156             CallTag::Batch(ref prom) => Some(prom.context()),
157             CallTag::UnaryRequest(ref cb) => Some(cb.batch_ctx()),
158             CallTag::Abort(ref cb) => Some(cb.batch_ctx()),
159             _ => None,
160         }
161     }
162 
163     /// Get the request context from the result holder.
request_ctx(&self) -> Option<&RequestContext>164     pub fn request_ctx(&self) -> Option<&RequestContext> {
165         match *self {
166             CallTag::Request(ref prom) => Some(prom.context()),
167             CallTag::UnaryRequest(ref cb) => Some(cb.request_ctx()),
168             _ => None,
169         }
170     }
171 
172     /// Resolve the CallTag with given status.
resolve(self, cq: &CompletionQueue, success: bool)173     pub fn resolve(self, cq: &CompletionQueue, success: bool) {
174         match self {
175             CallTag::Batch(prom) => prom.resolve(success),
176             CallTag::Request(cb) => cb.resolve(cq, success),
177             CallTag::UnaryRequest(cb) => cb.resolve(cq, success),
178             CallTag::Abort(_) => {}
179             CallTag::Action(prom) => prom.resolve(success),
180             CallTag::Spawn(notify) => self::executor::resolve(notify, success),
181         }
182     }
183 }
184 
185 impl Debug for CallTag {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result186     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
187         match *self {
188             CallTag::Batch(ref ctx) => write!(f, "CallTag::Batch({:?})", ctx),
189             CallTag::Request(_) => write!(f, "CallTag::Request(..)"),
190             CallTag::UnaryRequest(_) => write!(f, "CallTag::UnaryRequest(..)"),
191             CallTag::Abort(_) => write!(f, "CallTag::Abort(..)"),
192             CallTag::Action(_) => write!(f, "CallTag::Action"),
193             CallTag::Spawn(_) => write!(f, "CallTag::Spawn"),
194         }
195     }
196 }
197 
198 #[cfg(test)]
199 mod tests {
200     use std::sync::mpsc::*;
201     use std::sync::*;
202     use std::thread;
203 
204     use super::*;
205     use crate::env::Environment;
206     use futures::executor::block_on;
207 
208     #[test]
test_resolve()209     fn test_resolve() {
210         let env = Environment::new(1);
211 
212         let (cq_f1, tag1) = CallTag::action_pair();
213         let (cq_f2, tag2) = CallTag::action_pair();
214         let (tx, rx) = mpsc::channel();
215 
216         let handler = thread::spawn(move || {
217             tx.send(block_on(cq_f1)).unwrap();
218             tx.send(block_on(cq_f2)).unwrap();
219         });
220 
221         assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty);
222         tag1.resolve(&env.pick_cq(), true);
223         assert!(rx.recv().unwrap().is_ok());
224 
225         assert_eq!(rx.try_recv().unwrap_err(), TryRecvError::Empty);
226         tag2.resolve(&env.pick_cq(), false);
227         match rx.recv() {
228             Ok(Ok(false)) => {}
229             res => panic!("expect Ok(false), but got {:?}", res),
230         }
231 
232         handler.join().unwrap();
233     }
234 }
235