Skip to content

Commit

Permalink
proc_macro: add support for sending messages without waiting for a reply
Browse files Browse the repository at this point in the history
This has minimal impact without changes from a later part, as it also requires
support from the ExecutionStrategy. Messages are able to return owning handles
without waiting by allocating an ID from the client thread, and passing it up
to the server to fill with a response. If the server panics, it will be an ICE,
and no further requests will be handled.
  • Loading branch information
mystor committed Jul 2, 2021
1 parent afc36cc commit b6f8dc9
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 98 deletions.
98 changes: 82 additions & 16 deletions library/proc_macro/src/bridge/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
use super::*;

use std::sync::atomic::Ordering;

trait OwnedHandle {
/// Create a new handle of this type from the client side, which may be used
/// later by the server.
///
/// Should only be called on the client.
fn next_raw_handle() -> handle::Handle;

/// Create an instance of the owning handle object for this raw handle. The
/// raw handle should've previously been created with `next_raw_handle`, and
/// the corresponding message should've been sent to the server.
///
/// Should only be called on the client.
fn from_raw_handle(handle: handle::Handle) -> Self;
}

macro_rules! define_handles {
(
'owned: $($oty:ident,)*
Expand Down Expand Up @@ -116,6 +133,25 @@ macro_rules! define_handles {
$oty(handle::Handle::decode(r, s))
}
}

impl<S: server::Types> server::InitOwnedHandle<HandleStore<server::MarkedTypes<S>>>
for Marked<S::$oty, $oty>
{
fn init_handle(self, raw_handle: handle::Handle, s: &mut HandleStore<server::MarkedTypes<S>>) {
s.$oty.init(raw_handle, self);
}
}

impl OwnedHandle for $oty {
fn next_raw_handle() -> handle::Handle {
let counter = HandleCounters::get().$oty.fetch_add(1, Ordering::SeqCst);
handle::Handle::new(counter as u32).expect("`proc_macro` handle counter overflowed")
}

fn from_raw_handle(handle: handle::Handle) -> $oty {
$oty(handle)
}
}
)*

$(
Expand Down Expand Up @@ -242,27 +278,57 @@ impl fmt::Debug for Span {
}
}

macro_rules! define_client_side {
($($name:ident {
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
}),* $(,)?) => {
$(impl $name {
$(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
Bridge::with(|bridge| {
let mut b = bridge.cached_buffer.take();
macro_rules! client_send_impl {
(wait $name:ident :: $method:ident($($arg:ident),*) $(-> $ret_ty:ty)?) => {
Bridge::with(|bridge| {
let mut b = bridge.cached_buffer.take();

b.clear();
api_tags::Method::$name(api_tags::$name::$method).encode(&mut b, &mut ());
reverse_encode!(b; $($arg),*);

b = bridge.dispatch.call(b);

let r = Result::<_, PanicMessage>::decode(&mut &b[..], &mut ());

bridge.cached_buffer = b;

r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
})
};

(nowait $name:ident :: $method:ident($($arg:ident),*) $(-> $ret_ty:ty)?) => {
Bridge::with(|bridge| {
let mut b = bridge.cached_buffer.take();

b.clear();
api_tags::Method::$name(api_tags::$name::$method).encode(&mut b, &mut ());
reverse_encode!(b; $($arg),*);
b.clear();
api_tags::Method::$name(api_tags::$name::$method).encode(&mut b, &mut ());
reverse_encode!(b; $($arg),*);

b = bridge.dispatch.call(b);
$(
let raw_handle = <$ret_ty as OwnedHandle>::next_raw_handle();
raw_handle.encode(&mut b, &mut ());
)?

let r = Result::<_, PanicMessage>::decode(&mut &b[..], &mut ());
b = bridge.dispatch.call(b);

bridge.cached_buffer = b;
let r = Result::<(), PanicMessage>::decode(&mut &b[..], &mut ());

r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
})
bridge.cached_buffer = b;

r.unwrap_or_else(|e| panic::resume_unwind(e.into()));
$(<$ret_ty as OwnedHandle>::from_raw_handle(raw_handle))?
})
};
}

macro_rules! define_client_side {
($($name:ident {
$($wait:ident fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
}),* $(,)?) => {
$(impl $name {
$(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? {
client_send_impl!($wait $name :: $method ($($arg),*) $(-> $ret_ty)?)
})*
})*
}
Expand Down
6 changes: 5 additions & 1 deletion library/proc_macro/src/bridge/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ impl<T> OwnedStore<T> {
pub(super) fn alloc(&mut self, x: T) -> Handle {
let counter = self.counter.fetch_add(1, Ordering::SeqCst);
let handle = Handle::new(counter as u32).expect("`proc_macro` handle counter overflowed");
assert!(self.data.insert(handle, x).is_none());
self.init(handle, x);
handle
}

pub(super) fn init(&mut self, h: Handle, x: T) {
assert!(self.data.insert(h, x).is_none());
}

pub(super) fn take(&mut self, h: Handle) -> T {
self.data.remove(&h).expect("use-after-free in `proc_macro` handle")
}
Expand Down
181 changes: 103 additions & 78 deletions library/proc_macro/src/bridge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ use std::thread;
/// // ...
/// Literal {
/// // ...
/// fn character(ch: char) -> MySelf::Literal;
/// wait fn character(ch: char) -> MySelf::Literal;
/// // ...
/// fn span(my_self: &MySelf::Literal) -> MySelf::Span;
/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span);
/// wait fn span(my_self: &MySelf::Literal) -> MySelf::Span;
/// nowait fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span);
/// },
/// // ...
/// }
Expand All @@ -49,119 +49,125 @@ use std::thread;
/// a trait or a trait impl, where the trait has associated types
/// for each of the API types. If non-associated types are desired,
/// a module name (`self` in practice) can be used instead of `Self`.
///
/// If the `nowait` modifier is used, the server implementation may not
/// panic, and the client will continue immediately without waiting for
/// a response from the server when in multithreaded mode. If a return
/// type is present, it must be an owning IPC handle. Other return types
/// are not supported with `nowait`.
macro_rules! with_api {
($S:ident, $self:ident, $m:ident) => {
$m! {
FreeFunctions {
fn drop($self: $S::FreeFunctions);
fn track_env_var(var: &str, value: Option<&str>);
nowait fn drop($self: $S::FreeFunctions);
nowait fn track_env_var(var: &str, value: Option<&str>);
},
TokenStream {
fn drop($self: $S::TokenStream);
fn clone($self: &$S::TokenStream) -> $S::TokenStream;
fn new() -> $S::TokenStream;
fn is_empty($self: &$S::TokenStream) -> bool;
fn from_str(src: &str) -> $S::TokenStream;
fn to_string($self: &$S::TokenStream) -> String;
fn from_token_tree(
nowait fn drop($self: $S::TokenStream);
nowait fn clone($self: &$S::TokenStream) -> $S::TokenStream;
nowait fn new() -> $S::TokenStream;
wait fn is_empty($self: &$S::TokenStream) -> bool;
wait fn from_str(src: &str) -> $S::TokenStream;
wait fn to_string($self: &$S::TokenStream) -> String;
nowait fn from_token_tree(
tree: TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>,
) -> $S::TokenStream;
fn into_iter($self: $S::TokenStream) -> $S::TokenStreamIter;
nowait fn into_iter($self: $S::TokenStream) -> $S::TokenStreamIter;
},
TokenStreamBuilder {
fn drop($self: $S::TokenStreamBuilder);
fn new() -> $S::TokenStreamBuilder;
fn push($self: &mut $S::TokenStreamBuilder, stream: $S::TokenStream);
fn build($self: $S::TokenStreamBuilder) -> $S::TokenStream;
nowait fn drop($self: $S::TokenStreamBuilder);
nowait fn new() -> $S::TokenStreamBuilder;
nowait fn push($self: &mut $S::TokenStreamBuilder, stream: $S::TokenStream);
nowait fn build($self: $S::TokenStreamBuilder) -> $S::TokenStream;
},
TokenStreamIter {
fn drop($self: $S::TokenStreamIter);
fn clone($self: &$S::TokenStreamIter) -> $S::TokenStreamIter;
fn next(
nowait fn drop($self: $S::TokenStreamIter);
nowait fn clone($self: &$S::TokenStreamIter) -> $S::TokenStreamIter;
wait fn next(
$self: &mut $S::TokenStreamIter,
) -> Option<TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>>;
},
Group {
fn drop($self: $S::Group);
fn clone($self: &$S::Group) -> $S::Group;
fn new(delimiter: Delimiter, stream: $S::TokenStream) -> $S::Group;
fn delimiter($self: &$S::Group) -> Delimiter;
fn stream($self: &$S::Group) -> $S::TokenStream;
fn span($self: &$S::Group) -> $S::Span;
fn span_open($self: &$S::Group) -> $S::Span;
fn span_close($self: &$S::Group) -> $S::Span;
fn set_span($self: &mut $S::Group, span: $S::Span);
nowait fn drop($self: $S::Group);
nowait fn clone($self: &$S::Group) -> $S::Group;
nowait fn new(delimiter: Delimiter, stream: $S::TokenStream) -> $S::Group;
wait fn delimiter($self: &$S::Group) -> Delimiter;
nowait fn stream($self: &$S::Group) -> $S::TokenStream;
wait fn span($self: &$S::Group) -> $S::Span;
wait fn span_open($self: &$S::Group) -> $S::Span;
wait fn span_close($self: &$S::Group) -> $S::Span;
nowait fn set_span($self: &mut $S::Group, span: $S::Span);
},
Punct {
fn new(ch: char, spacing: Spacing) -> $S::Punct;
fn as_char($self: $S::Punct) -> char;
fn spacing($self: $S::Punct) -> Spacing;
fn span($self: $S::Punct) -> $S::Span;
fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct;
wait fn new(ch: char, spacing: Spacing) -> $S::Punct;
wait fn as_char($self: $S::Punct) -> char;
wait fn spacing($self: $S::Punct) -> Spacing;
wait fn span($self: $S::Punct) -> $S::Span;
wait fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct;
},
Ident {
fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident;
fn span($self: $S::Ident) -> $S::Span;
fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident;
wait fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident;
wait fn span($self: $S::Ident) -> $S::Span;
wait fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident;
},
Literal {
fn drop($self: $S::Literal);
fn clone($self: &$S::Literal) -> $S::Literal;
fn from_str(s: &str) -> Result<$S::Literal, ()>;
fn debug_kind($self: &$S::Literal) -> String;
fn symbol($self: &$S::Literal) -> String;
fn suffix($self: &$S::Literal) -> Option<String>;
fn integer(n: &str) -> $S::Literal;
fn typed_integer(n: &str, kind: &str) -> $S::Literal;
fn float(n: &str) -> $S::Literal;
fn f32(n: &str) -> $S::Literal;
fn f64(n: &str) -> $S::Literal;
fn string(string: &str) -> $S::Literal;
fn character(ch: char) -> $S::Literal;
fn byte_string(bytes: &[u8]) -> $S::Literal;
fn span($self: &$S::Literal) -> $S::Span;
fn set_span($self: &mut $S::Literal, span: $S::Span);
fn subspan(
nowait fn drop($self: $S::Literal);
nowait fn clone($self: &$S::Literal) -> $S::Literal;
wait fn from_str(s: &str) -> Result<$S::Literal, ()>;
wait fn debug_kind($self: &$S::Literal) -> String;
wait fn symbol($self: &$S::Literal) -> String;
wait fn suffix($self: &$S::Literal) -> Option<String>;
nowait fn integer(n: &str) -> $S::Literal;
nowait fn typed_integer(n: &str, kind: &str) -> $S::Literal;
nowait fn float(n: &str) -> $S::Literal;
nowait fn f32(n: &str) -> $S::Literal;
nowait fn f64(n: &str) -> $S::Literal;
nowait fn string(string: &str) -> $S::Literal;
nowait fn character(ch: char) -> $S::Literal;
nowait fn byte_string(bytes: &[u8]) -> $S::Literal;
wait fn span($self: &$S::Literal) -> $S::Span;
nowait fn set_span($self: &mut $S::Literal, span: $S::Span);
wait fn subspan(
$self: &$S::Literal,
start: Bound<usize>,
end: Bound<usize>,
) -> Option<$S::Span>;
},
SourceFile {
fn drop($self: $S::SourceFile);
fn clone($self: &$S::SourceFile) -> $S::SourceFile;
fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool;
fn path($self: &$S::SourceFile) -> String;
fn is_real($self: &$S::SourceFile) -> bool;
nowait fn drop($self: $S::SourceFile);
nowait fn clone($self: &$S::SourceFile) -> $S::SourceFile;
wait fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool;
wait fn path($self: &$S::SourceFile) -> String;
wait fn is_real($self: &$S::SourceFile) -> bool;
},
MultiSpan {
fn drop($self: $S::MultiSpan);
fn new() -> $S::MultiSpan;
fn push($self: &mut $S::MultiSpan, span: $S::Span);
nowait fn drop($self: $S::MultiSpan);
nowait fn new() -> $S::MultiSpan;
wait fn push($self: &mut $S::MultiSpan, span: $S::Span);
},
Diagnostic {
fn drop($self: $S::Diagnostic);
fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic;
fn sub(
wait fn drop($self: $S::Diagnostic);
wait fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic;
wait fn sub(
$self: &mut $S::Diagnostic,
level: Level,
msg: &str,
span: $S::MultiSpan,
);
fn emit($self: $S::Diagnostic);
wait fn emit($self: $S::Diagnostic);
},
Span {
fn debug($self: $S::Span) -> String;
fn source_file($self: $S::Span) -> $S::SourceFile;
fn parent($self: $S::Span) -> Option<$S::Span>;
fn source($self: $S::Span) -> $S::Span;
fn start($self: $S::Span) -> LineColumn;
fn end($self: $S::Span) -> LineColumn;
fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
fn source_text($self: $S::Span) -> Option<String>;
fn save_span($self: $S::Span) -> usize;
fn recover_proc_macro_span(id: usize) -> $S::Span;
wait fn debug($self: $S::Span) -> String;
wait fn source_file($self: $S::Span) -> $S::SourceFile;
wait fn parent($self: $S::Span) -> Option<$S::Span>;
wait fn source($self: $S::Span) -> $S::Span;
wait fn start($self: $S::Span) -> LineColumn;
wait fn end($self: $S::Span) -> LineColumn;
wait fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
wait fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
wait fn source_text($self: $S::Span) -> Option<String>;
wait fn save_span($self: $S::Span) -> usize;
wait fn recover_proc_macro_span(id: usize) -> $S::Span;
},
}
};
Expand Down Expand Up @@ -232,9 +238,18 @@ impl<'a> !Send for BridgeConfig<'a> {}
mod api_tags {
use super::rpc::{DecodeMut, Encode, Reader, Writer};

macro_rules! should_wait_impl {
(wait) => {
true
};
(nowait) => {
false
};
}

macro_rules! declare_tags {
($($name:ident {
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
$($wait:ident fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
}),* $(,)?) => {
$(
pub(super) enum $name {
Expand All @@ -248,6 +263,16 @@ mod api_tags {
$($name($name)),*
}
rpc_encode_decode!(enum Method { $($name(m)),* });

impl Method {
pub(super) fn should_wait(&self) -> bool {
match self {
$($(
Method::$name($name::$method) => should_wait_impl!($wait),
)*)*
}
}
}
}
}
with_api!(self, self, declare_tags);
Expand Down
Loading

0 comments on commit b6f8dc9

Please sign in to comment.