diff options
author | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
---|---|---|
committer | Igor Aleksanov <[email protected]> | 2020-08-14 05:34:07 +0100 |
commit | c26c911ec1e6c2ad1dcb7d155a6a1d528839ad1a (patch) | |
tree | 7cff36c38234be0afb65273146d8247083a5cfeb /crates/proc_macro_srv/src/proc_macro/bridge/server.rs | |
parent | 3c018bf84de5c693b5ee1c6bec0fed3b201c2060 (diff) | |
parent | f1f73649a686dc6e6449afc35e0fa6fed00e225d (diff) |
Merge branch 'master' into add-disable-diagnostics
Diffstat (limited to 'crates/proc_macro_srv/src/proc_macro/bridge/server.rs')
-rw-r--r-- | crates/proc_macro_srv/src/proc_macro/bridge/server.rs | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs new file mode 100644 index 000000000..45d41ac02 --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs | |||
@@ -0,0 +1,323 @@ | |||
1 | //! lib-proc-macro server-side traits | ||
2 | //! | ||
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs | ||
4 | //! augmented with removing unstable features | ||
5 | |||
6 | use super::*; | ||
7 | |||
8 | // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. | ||
9 | use super::client::HandleStore; | ||
10 | |||
11 | /// Declare an associated item of one of the traits below, optionally | ||
12 | /// adjusting it (i.e., adding bounds to types and default bodies to methods). | ||
13 | macro_rules! associated_item { | ||
14 | (type TokenStream) => | ||
15 | (type TokenStream: 'static + Clone;); | ||
16 | (type TokenStreamBuilder) => | ||
17 | (type TokenStreamBuilder: 'static;); | ||
18 | (type TokenStreamIter) => | ||
19 | (type TokenStreamIter: 'static + Clone;); | ||
20 | (type Group) => | ||
21 | (type Group: 'static + Clone;); | ||
22 | (type Punct) => | ||
23 | (type Punct: 'static + Copy + Eq + Hash;); | ||
24 | (type Ident) => | ||
25 | (type Ident: 'static + Copy + Eq + Hash;); | ||
26 | (type Literal) => | ||
27 | (type Literal: 'static + Clone;); | ||
28 | (type SourceFile) => | ||
29 | (type SourceFile: 'static + Clone;); | ||
30 | (type MultiSpan) => | ||
31 | (type MultiSpan: 'static;); | ||
32 | (type Diagnostic) => | ||
33 | (type Diagnostic: 'static;); | ||
34 | (type Span) => | ||
35 | (type Span: 'static + Copy + Eq + Hash;); | ||
36 | (fn drop(&mut self, $arg:ident: $arg_ty:ty)) => | ||
37 | (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) }); | ||
38 | (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) => | ||
39 | (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() }); | ||
40 | ($($item:tt)*) => ($($item)*;) | ||
41 | } | ||
42 | |||
43 | macro_rules! declare_server_traits { | ||
44 | ($($name:ident { | ||
45 | $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* | ||
46 | }),* $(,)?) => { | ||
47 | pub trait Types { | ||
48 | $(associated_item!(type $name);)* | ||
49 | } | ||
50 | |||
51 | $(pub trait $name: Types { | ||
52 | $(associated_item!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* | ||
53 | })* | ||
54 | |||
55 | pub trait Server: Types $(+ $name)* {} | ||
56 | impl<S: Types $(+ $name)*> Server for S {} | ||
57 | } | ||
58 | } | ||
59 | with_api!(Self, self_, declare_server_traits); | ||
60 | |||
61 | pub(super) struct MarkedTypes<S: Types>(S); | ||
62 | |||
63 | macro_rules! define_mark_types_impls { | ||
64 | ($($name:ident { | ||
65 | $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* | ||
66 | }),* $(,)?) => { | ||
67 | impl<S: Types> Types for MarkedTypes<S> { | ||
68 | $(type $name = Marked<S::$name, client::$name>;)* | ||
69 | } | ||
70 | |||
71 | $(impl<S: $name> $name for MarkedTypes<S> { | ||
72 | $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? { | ||
73 | <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*)) | ||
74 | })* | ||
75 | })* | ||
76 | } | ||
77 | } | ||
78 | with_api!(Self, self_, define_mark_types_impls); | ||
79 | |||
80 | struct Dispatcher<S: Types> { | ||
81 | handle_store: HandleStore<S>, | ||
82 | server: S, | ||
83 | } | ||
84 | |||
85 | macro_rules! define_dispatcher_impl { | ||
86 | ($($name:ident { | ||
87 | $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* | ||
88 | }),* $(,)?) => { | ||
89 | // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. | ||
90 | pub trait DispatcherTrait { | ||
91 | // HACK(eddyb) these are here to allow `Self::$name` to work below. | ||
92 | $(type $name;)* | ||
93 | fn dispatch(&mut self, b: Buffer<u8>) -> Buffer<u8>; | ||
94 | } | ||
95 | |||
96 | impl<S: Server> DispatcherTrait for Dispatcher<MarkedTypes<S>> { | ||
97 | $(type $name = <MarkedTypes<S> as Types>::$name;)* | ||
98 | fn dispatch(&mut self, mut b: Buffer<u8>) -> Buffer<u8> { | ||
99 | let Dispatcher { handle_store, server } = self; | ||
100 | |||
101 | let mut reader = &b[..]; | ||
102 | match api_tags::Method::decode(&mut reader, &mut ()) { | ||
103 | $(api_tags::Method::$name(m) => match m { | ||
104 | $(api_tags::$name::$method => { | ||
105 | let mut call_method = || { | ||
106 | reverse_decode!(reader, handle_store; $($arg: $arg_ty),*); | ||
107 | $name::$method(server, $($arg),*) | ||
108 | }; | ||
109 | // HACK(eddyb) don't use `panic::catch_unwind` in a panic. | ||
110 | // If client and server happen to use the same `libstd`, | ||
111 | // `catch_unwind` asserts that the panic counter was 0, | ||
112 | // even when the closure passed to it didn't panic. | ||
113 | let r = if thread::panicking() { | ||
114 | Ok(call_method()) | ||
115 | } else { | ||
116 | panic::catch_unwind(panic::AssertUnwindSafe(call_method)) | ||
117 | .map_err(PanicMessage::from) | ||
118 | }; | ||
119 | |||
120 | b.clear(); | ||
121 | r.encode(&mut b, handle_store); | ||
122 | })* | ||
123 | }),* | ||
124 | } | ||
125 | b | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | with_api!(Self, self_, define_dispatcher_impl); | ||
131 | |||
132 | pub trait ExecutionStrategy { | ||
133 | fn run_bridge_and_client<D: Copy + Send + 'static>( | ||
134 | &self, | ||
135 | dispatcher: &mut impl DispatcherTrait, | ||
136 | input: Buffer<u8>, | ||
137 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | ||
138 | client_data: D, | ||
139 | ) -> Buffer<u8>; | ||
140 | } | ||
141 | |||
142 | pub struct SameThread; | ||
143 | |||
144 | impl ExecutionStrategy for SameThread { | ||
145 | fn run_bridge_and_client<D: Copy + Send + 'static>( | ||
146 | &self, | ||
147 | dispatcher: &mut impl DispatcherTrait, | ||
148 | input: Buffer<u8>, | ||
149 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | ||
150 | client_data: D, | ||
151 | ) -> Buffer<u8> { | ||
152 | let mut dispatch = |b| dispatcher.dispatch(b); | ||
153 | |||
154 | run_client(Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, client_data) | ||
155 | } | ||
156 | } | ||
157 | |||
158 | // NOTE(eddyb) Two implementations are provided, the second one is a bit | ||
159 | // faster but neither is anywhere near as fast as same-thread execution. | ||
160 | |||
161 | pub struct CrossThread1; | ||
162 | |||
163 | impl ExecutionStrategy for CrossThread1 { | ||
164 | fn run_bridge_and_client<D: Copy + Send + 'static>( | ||
165 | &self, | ||
166 | dispatcher: &mut impl DispatcherTrait, | ||
167 | input: Buffer<u8>, | ||
168 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | ||
169 | client_data: D, | ||
170 | ) -> Buffer<u8> { | ||
171 | use std::sync::mpsc::channel; | ||
172 | |||
173 | let (req_tx, req_rx) = channel(); | ||
174 | let (res_tx, res_rx) = channel(); | ||
175 | |||
176 | let join_handle = thread::spawn(move || { | ||
177 | let mut dispatch = |b| { | ||
178 | req_tx.send(b).unwrap(); | ||
179 | res_rx.recv().unwrap() | ||
180 | }; | ||
181 | |||
182 | run_client( | ||
183 | Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, | ||
184 | client_data, | ||
185 | ) | ||
186 | }); | ||
187 | |||
188 | for b in req_rx { | ||
189 | res_tx.send(dispatcher.dispatch(b)).unwrap(); | ||
190 | } | ||
191 | |||
192 | join_handle.join().unwrap() | ||
193 | } | ||
194 | } | ||
195 | |||
196 | pub struct CrossThread2; | ||
197 | |||
198 | impl ExecutionStrategy for CrossThread2 { | ||
199 | fn run_bridge_and_client<D: Copy + Send + 'static>( | ||
200 | &self, | ||
201 | dispatcher: &mut impl DispatcherTrait, | ||
202 | input: Buffer<u8>, | ||
203 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | ||
204 | client_data: D, | ||
205 | ) -> Buffer<u8> { | ||
206 | use std::sync::{Arc, Mutex}; | ||
207 | |||
208 | enum State<T> { | ||
209 | Req(T), | ||
210 | Res(T), | ||
211 | } | ||
212 | |||
213 | let mut state = Arc::new(Mutex::new(State::Res(Buffer::new()))); | ||
214 | |||
215 | let server_thread = thread::current(); | ||
216 | let state2 = state.clone(); | ||
217 | let join_handle = thread::spawn(move || { | ||
218 | let mut dispatch = |b| { | ||
219 | *state2.lock().unwrap() = State::Req(b); | ||
220 | server_thread.unpark(); | ||
221 | loop { | ||
222 | thread::park(); | ||
223 | if let State::Res(b) = &mut *state2.lock().unwrap() { | ||
224 | break b.take(); | ||
225 | } | ||
226 | } | ||
227 | }; | ||
228 | |||
229 | let r = run_client( | ||
230 | Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, | ||
231 | client_data, | ||
232 | ); | ||
233 | |||
234 | // Wake up the server so it can exit the dispatch loop. | ||
235 | drop(state2); | ||
236 | server_thread.unpark(); | ||
237 | |||
238 | r | ||
239 | }); | ||
240 | |||
241 | // Check whether `state2` was dropped, to know when to stop. | ||
242 | while Arc::get_mut(&mut state).is_none() { | ||
243 | thread::park(); | ||
244 | let mut b = match &mut *state.lock().unwrap() { | ||
245 | State::Req(b) => b.take(), | ||
246 | _ => continue, | ||
247 | }; | ||
248 | b = dispatcher.dispatch(b.take()); | ||
249 | *state.lock().unwrap() = State::Res(b); | ||
250 | join_handle.thread().unpark(); | ||
251 | } | ||
252 | |||
253 | join_handle.join().unwrap() | ||
254 | } | ||
255 | } | ||
256 | |||
257 | fn run_server< | ||
258 | S: Server, | ||
259 | I: Encode<HandleStore<MarkedTypes<S>>>, | ||
260 | O: for<'a, 's> DecodeMut<'a, 's, HandleStore<MarkedTypes<S>>>, | ||
261 | D: Copy + Send + 'static, | ||
262 | >( | ||
263 | strategy: &impl ExecutionStrategy, | ||
264 | handle_counters: &'static client::HandleCounters, | ||
265 | server: S, | ||
266 | input: I, | ||
267 | run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>, | ||
268 | client_data: D, | ||
269 | ) -> Result<O, PanicMessage> { | ||
270 | let mut dispatcher = | ||
271 | Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; | ||
272 | |||
273 | let mut b = Buffer::new(); | ||
274 | input.encode(&mut b, &mut dispatcher.handle_store); | ||
275 | |||
276 | b = strategy.run_bridge_and_client(&mut dispatcher, b, run_client, client_data); | ||
277 | |||
278 | Result::decode(&mut &b[..], &mut dispatcher.handle_store) | ||
279 | } | ||
280 | |||
281 | impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> { | ||
282 | pub fn run<S: Server>( | ||
283 | &self, | ||
284 | strategy: &impl ExecutionStrategy, | ||
285 | server: S, | ||
286 | input: S::TokenStream, | ||
287 | ) -> Result<S::TokenStream, PanicMessage> { | ||
288 | let client::Client { get_handle_counters, run, f } = *self; | ||
289 | run_server( | ||
290 | strategy, | ||
291 | get_handle_counters(), | ||
292 | server, | ||
293 | <MarkedTypes<S> as Types>::TokenStream::mark(input), | ||
294 | run, | ||
295 | f, | ||
296 | ) | ||
297 | .map(<MarkedTypes<S> as Types>::TokenStream::unmark) | ||
298 | } | ||
299 | } | ||
300 | |||
301 | impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> { | ||
302 | pub fn run<S: Server>( | ||
303 | &self, | ||
304 | strategy: &impl ExecutionStrategy, | ||
305 | server: S, | ||
306 | input: S::TokenStream, | ||
307 | input2: S::TokenStream, | ||
308 | ) -> Result<S::TokenStream, PanicMessage> { | ||
309 | let client::Client { get_handle_counters, run, f } = *self; | ||
310 | run_server( | ||
311 | strategy, | ||
312 | get_handle_counters(), | ||
313 | server, | ||
314 | ( | ||
315 | <MarkedTypes<S> as Types>::TokenStream::mark(input), | ||
316 | <MarkedTypes<S> as Types>::TokenStream::mark(input2), | ||
317 | ), | ||
318 | run, | ||
319 | f, | ||
320 | ) | ||
321 | .map(<MarkedTypes<S> as Types>::TokenStream::unmark) | ||
322 | } | ||
323 | } | ||