use crossbeam_queue::ArrayQueue; use mio::Token; use std::cell::UnsafeCell; use std::collections::VecDeque; use std::io::{self, Error, ErrorKind, Result}; use std::marker::PhantomPinned; use std::mem::ManuallyDrop; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Weak};
usecrate::ipccore::EventLoopHandle;
// This provides a safe-ish method for a thread to allocate // stack storage space for a result, then pass a (wrapped) // pointer to that location to another thread via // a CompletionWriter to eventually store a result into. struct Completion<T> {
item: UnsafeCell<Option<T>>,
writer: AtomicBool,
_pin: PhantomPinned, // disable rustc's no-alias
}
// Wait until the writer completes, then return the result. // This is intended to be a single-use function, once the writer // has completed any further attempts to wait will return None. fn wait(&self) -> Option<T> { // Wait for the writer to complete or be dropped. whileself.writer.load(Ordering::Acquire) {
std::thread::park();
} unsafe { (*self.item.get()).take() }
}
// Create a writer for the other thread to store the // expected result into. fn writer(&self) -> CompletionWriter<T> {
assert!(!self.writer.load(Ordering::Relaxed)); self.writer.store(true, Ordering::Release);
CompletionWriter {
ptr: selfas *const _ as *mut _,
waiter: std::thread::current(),
}
}
}
impl<T> Drop for Completion<T> { fn drop(&mutself) { // Wait for the outstanding writer to complete before // dropping, since the CompletionWriter references // memory owned by this object. whileself.writer.load(Ordering::Acquire) {
std::thread::park();
}
}
}
struct CompletionWriter<T> {
ptr: *mut Completion<T>, // Points to a Completion on another thread's stack
waiter: std::thread::Thread, // Identifies thread waiting for completion
}
impl<T> CompletionWriter<T> { fn set(self, value: T) { // Store the result into the Completion's memory. // Since `set` consumes `self`, rely on `Drop` to // mark the writer as done and wake the Completion's // thread. unsafe {
assert!((*self.ptr).writer.load(Ordering::Relaxed));
*(*self.ptr).item.get() = Some(value);
}
}
}
impl<T> Drop for CompletionWriter<T> { fn drop(&mutself) { // Mark writer as complete - if `set` was not called, // the waiter will receive `None`. unsafe {
(*self.ptr).writer.store(false, Ordering::Release);
} // Wake the Completion's thread. self.waiter.unpark();
}
}
// Safety: CompletionWriter holds a pointer to a Completion // residing on another thread's stack. The Completion always // waits for an outstanding writer if present, and CompletionWriter // releases the waiter and wakes the Completion's thread on drop, // so this pointer will always be live for the duration of a // CompletionWriter. unsafeimpl<T> Send for CompletionWriter<T> {}
// RPC message handler. Implemented by ClientHandler (for Client) // and ServerHandler (for Server). pub(crate) trait Handler { typeIn; type Out;
// Consume a request fn consume(&mutself, request: Self::In) -> Result<()>;
// Produce a response fn produce(&mutself) -> Result<Option<Self::Out>>;
}
// Client RPC definition. This supplies the expected message // request and response types. pubtrait Client { type ServerMessage; type ClientMessage;
}
// Server RPC definition. This supplies the expected message // request and response types. `process` is passed inbound RPC // requests by the ServerHandler to be responded to by the server. pubtrait Server { type ServerMessage; type ClientMessage;
// RPC Proxy that may be `clone`d for use by multiple owners/threads. // A Proxy `call` arranges for the supplied request to be transmitted // to the associated Server via RPC and blocks awaiting the response // via the associated `Completion`. // A ClientHandler normally lives until the last Proxy is dropped, but if the ClientHandler // encounters an internal error, `requests` will fail to upgrade, allowing // the proxy to report an error. #[derive(Debug)] pubstruct Proxy<Request, Response> {
handle: Option<(EventLoopHandle, Token)>,
requests: ManuallyDrop<RequestQueueSender<ProxyRequest<Request, Response>>>,
}
fn wake_connection(&self) { let (handle, token) = self
.handle
.as_ref()
.expect("proxy not connected to event loop");
handle.wake_connection(*token);
}
}
impl<Request, Response> Clone for Proxy<Request, Response> { fn clone(&self) -> Self { letmut clone = Self::new((*self.requests).clone()); let (handle, token) = self
.handle
.as_ref()
.expect("proxy not connected to event loop");
clone.connect_event_loop(handle.clone(), *token);
clone
}
}
impl<Request, Response> Drop for Proxy<Request, Response> { fn drop(&mutself) {
trace!("Proxy drop, waking EventLoop"); // Must drop `requests` before waking the connection, otherwise // the wake may be processed before the (last) weak reference is // dropped. let last_proxy = self.requests.live_proxies(); unsafe {
ManuallyDrop::drop(&mutself.requests);
} if last_proxy == 1 && self.handle.is_some() { self.wake_connection()
}
}
}
// Client-specific Handler implementation. // The IPC EventLoop Driver calls this to execute client-specific // RPC handling. Serialized messages sent via a Proxy are queued // for transmission when `produce` is called. // Deserialized messages are passed via `consume` to // trigger response completion by sending the response via a channel // connected to a ProxyResponse. pub(crate) struct ClientHandler<C: Client> {
in_flight: VecDeque<CompletionWriter<C::ClientMessage>>,
requests: Arc<RequestQueue<ProxyRequest<C::ServerMessage, C::ClientMessage>>>,
}
// If the weak count is zero, no proxies are attached and // no further proxies can be attached since every proxy // after the initial one is cloned from an existing instance. self.requests.check_live_proxies()?; // Try to get a new message matchself.requests.pop() {
Some((request, response_writer)) => {
trace!(" --> received request"); self.in_flight.push_back(response_writer);
Ok(Some(request))
}
None => {
trace!(" --> no request");
Ok(None)
}
}
}
}
#[allow(clippy::type_complexity)] pub(crate) fn make_client<C: Client>(
) -> Result<(ClientHandler<C>, Proxy<C::ServerMessage, C::ClientMessage>)> { let requests = Arc::new(RequestQueue::new(RPC_CLIENT_INITIAL_PROXIES)); let proxy_req = requests.new_sender(); let handler = ClientHandler::new(requests);
Ok((handler, Proxy::new(proxy_req)))
}
// Server-specific Handler implementation. // The IPC EventLoop Driver calls this to execute server-specific // RPC handling. Deserialized messages are passed via `consume` to the // associated `server` for processing. Server responses are then queued // for RPC to the associated client when `produce` is called. pub(crate) struct ServerHandler<S: Server> {
server: S,
in_flight: VecDeque<S::ClientMessage>,
}
impl<S: Server> Handler for ServerHandler<S> { typeIn = S::ServerMessage; type Out = S::ClientMessage;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.