Skip to content

Commit 8045e34

Browse files
committed
add comments covering various lower/lift combos in FACT generator
Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent 840af68 commit 8045e34

File tree

1 file changed

+56
-8
lines changed

1 file changed

+56
-8
lines changed

crates/environ/src/fact/trampoline.rs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
114114
)
115115
}
116116

117-
let start_adapter = |module: &mut Module, param_globals| {
117+
let async_start_adapter = |module: &mut Module, param_globals| {
118118
let sig = module.types.async_start_signature(&adapter.lift);
119119
let ty = module.core_types.function(&sig.params, &sig.results);
120120
let result = module.funcs.push(Function::new(
@@ -131,7 +131,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
131131
result
132132
};
133133

134-
let return_adapter = |module: &mut Module, result_globals| {
134+
let async_return_adapter = |module: &mut Module, result_globals| {
135135
let sig = module.types.async_return_signature(&adapter.lift);
136136
let ty = module.core_types.function(&sig.params, &sig.results);
137137
let result = module.funcs.push(Function::new(
@@ -150,12 +150,29 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
150150

151151
match (adapter.lower.options.async_, adapter.lift.options.async_) {
152152
(false, false) => {
153+
// We can adapt sync->sync case with only minimal use of intrinsics,
154+
// e.g. resource enter and exit calls as needed.
153155
let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
154156
compiler.compile_sync_to_sync_adapter(adapter, &lower_sig, &lift_sig)
155157
}
156158
(true, true) => {
157-
let start = start_adapter(module, None);
158-
let return_ = return_adapter(module, None);
159+
// In the async->async case, we must compile a couple of helper functions:
160+
//
161+
// - `async-start`: copies the parameters from the caller to the callee
162+
// - `async-return`: copies the result from the callee to the caller
163+
//
164+
// Unlike synchronous calls, the above operations are asynchronous
165+
// and subject to backpressure. If the callee is not yet ready to
166+
// handle a new call, the `async-start` function will not be called
167+
// immediately. Instead, control will return to the caller,
168+
// allowing it to do other work while waiting for this call to make
169+
// progress. Once the callee indicates it is ready, `async-start`
170+
// will be called, and sometime later (possibly after various task
171+
// switch events), when the callee has produced a result, it will
172+
// call `async-return` via the `task.return` intrinsic, at which
173+
// point a `STATUS_RETURNED` event will be delivered to the caller.
174+
let start = async_start_adapter(module, None);
175+
let return_ = async_return_adapter(module, None);
159176
let (compiler, _, lift_sig) = compiler(module, adapter);
160177
compiler.compile_async_to_async_adapter(
161178
adapter,
@@ -165,6 +182,18 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
165182
);
166183
}
167184
(false, true) => {
185+
// Like the async->async case above, for the sync->async case we
186+
// also need `async-start` and `async-return` helper functions to
187+
// allow the callee to asynchronously "pull" the parameters and
188+
// "push" the results when it is ready.
189+
//
190+
// However, since the caller is using the synchronous ABI, the
191+
// parameters may have been passed via the stack rather than linear
192+
// memory. In that case, we use global variables to store them such
193+
// that they can be retrieved by the `async-start` function.
194+
// Similarly, the `async-return` function may write its result to
195+
// global variables from which the adapter function can read and
196+
// return them via the stack to the caller.
168197
let lower_sig = module.types.signature(&adapter.lower, Context::Lower);
169198
let param_globals = if lower_sig.params_indirect {
170199
None
@@ -196,8 +225,8 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
196225
)
197226
};
198227

199-
let start = start_adapter(module, param_globals.as_deref());
200-
let return_ = return_adapter(module, result_globals.as_deref());
228+
let start = async_start_adapter(module, param_globals.as_deref());
229+
let return_ = async_return_adapter(module, result_globals.as_deref());
201230
let (compiler, _, lift_sig) = compiler(module, adapter);
202231
compiler.compile_sync_to_async_adapter(
203232
adapter,
@@ -209,9 +238,28 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
209238
);
210239
}
211240
(true, false) => {
241+
// As with the async->async and sync->async cases above, for the
242+
// async->sync case we use `async-start` and `async-return` helper
243+
// functions. Here, those functions allow the host to enforce
244+
// backpressure in the case where the callee instance already has
245+
// another synchronous call in progress, in which case we can't
246+
// start a new one until the current one (and any others already
247+
// waiting in line behind it) has completed.
248+
//
249+
// In the case of backpressure, we'll return control to the caller
250+
// immediately so it can do other work. Later, once the callee is
251+
// ready, the host will call the `async-start` function to retrieve
252+
// the parameters and pass them to the callee. At that point, the
253+
// callee may block on a host call, at which point the host will
254+
// suspend the fiber it is running on and allow the caller (or any
255+
// other ready instance) to run concurrently with the blocked
256+
// callee. Once the callee finally returns, the host will call the
257+
// `async-return` function to write the result to the caller's
258+
// linear memory and deliver a `STATUS_RETURNED` event to the
259+
// caller.
212260
let lift_sig = module.types.signature(&adapter.lift, Context::Lift);
213-
let start = start_adapter(module, None);
214-
let return_ = return_adapter(module, None);
261+
let start = async_start_adapter(module, None);
262+
let return_ = async_return_adapter(module, None);
215263
let (compiler, ..) = compiler(module, adapter);
216264
compiler.compile_async_to_sync_adapter(
217265
adapter,

0 commit comments

Comments
 (0)