@@ -154,6 +154,9 @@ where
154
154
155
155
/// The local QuestionRef, set to None when it is destroyed.
156
156
self_ref : Option < Weak < RefCell < QuestionRef < VatId > > > > ,
157
+
158
+ /// If true, don't send a Finish message.
159
+ skip_finish : bool ,
157
160
}
158
161
159
162
impl < VatId > Question < VatId > {
@@ -163,6 +166,7 @@ impl<VatId> Question<VatId> {
163
166
param_exports : Vec :: new ( ) ,
164
167
is_tail_call : false ,
165
168
self_ref : None ,
169
+ skip_finish : false ,
166
170
}
167
171
}
168
172
}
@@ -210,20 +214,22 @@ impl<VatId> Drop for QuestionRef<VatId> {
210
214
unreachable ! ( )
211
215
} ;
212
216
if let Ok ( ref mut c) = * self . connection_state . connection . borrow_mut ( ) {
213
- let mut message = c. new_outgoing_message ( 5 ) ;
214
- {
215
- let root: message:: Builder = message. get_body ( ) . unwrap ( ) . init_as ( ) ;
216
- let mut builder = root. init_finish ( ) ;
217
- builder. set_question_id ( self . id ) ;
218
-
219
- // If we're still awaiting a return, then this request is being
220
- // canceled, and we're going to ignore any capabilities in the return
221
- // message, so set releaseResultCaps true. If we already received the
222
- // return, then we've already built local proxies for the caps and will
223
- // send Release messages when those are destroyed.
224
- builder. set_release_result_caps ( q. is_awaiting_return ) ;
217
+ if !q. skip_finish {
218
+ let mut message = c. new_outgoing_message ( 5 ) ;
219
+ {
220
+ let root: message:: Builder = message. get_body ( ) . unwrap ( ) . init_as ( ) ;
221
+ let mut builder = root. init_finish ( ) ;
222
+ builder. set_question_id ( self . id ) ;
223
+
224
+ // If we're still awaiting a return, then this request is being
225
+ // canceled, and we're going to ignore any capabilities in the return
226
+ // message, so set releaseResultCaps true. If we already received the
227
+ // return, then we've already built local proxies for the caps and will
228
+ // send Release messages when those are destroyed.
229
+ builder. set_release_result_caps ( q. is_awaiting_return ) ;
230
+ }
231
+ let _ = message. send ( ) ;
225
232
}
226
- let _ = message. send ( ) ;
227
233
}
228
234
229
235
if q. is_awaiting_return {
@@ -774,9 +780,10 @@ impl<VatId> ConnectionState<VatId> {
774
780
let answers_slots = & mut connection_state. answers . borrow_mut ( ) . slots ;
775
781
match answers_slots. entry ( answer_id) {
776
782
hash_map:: Entry :: Vacant ( _) => {
777
- return Err ( Error :: failed ( format ! (
778
- "Invalid question ID {answer_id} in Finish message."
779
- ) ) ) ;
783
+ // The `Finish` message targets a question ID that isn't present in our answer table.
784
+ // Probably, we sent a `Return` with `noFinishNeeded = true`, but the other side didn't
785
+ // recognize this hint and sent a `Finish` anyway, or the `Finish` was already in-flight at
786
+ // the time we sent the `Return`. We can silently ignore this.
780
787
}
781
788
hash_map:: Entry :: Occupied ( mut entry) => {
782
789
let answer = entry. get_mut ( ) ;
@@ -1008,6 +1015,9 @@ impl<VatId> ConnectionState<VatId> {
1008
1015
match questions. slots [ question_id as usize ] {
1009
1016
Some ( ref mut question) => {
1010
1017
question. is_awaiting_return = false ;
1018
+ if ret. get_no_finish_needed ( ) {
1019
+ question. skip_finish = true ;
1020
+ }
1011
1021
match question. self_ref {
1012
1022
Some ( ref question_ref) => match ret. which ( ) ? {
1013
1023
return_:: Results ( results) => {
@@ -1215,23 +1225,23 @@ impl<VatId> ConnectionState<VatId> {
1215
1225
let promised_answer = promised_answer?;
1216
1226
let question_id = promised_answer. get_question_id ( ) ;
1217
1227
1218
- match self . answers . borrow ( ) . slots . get ( & question_id) {
1219
- None => Err ( Error :: failed (
1220
- "PromisedAnswer.questionId is not a current question ." . to_string ( ) ,
1221
- ) ) ,
1228
+ let pipeline = match self . answers . borrow ( ) . slots . get ( & question_id) {
1229
+ None => Box :: new ( broken :: Pipeline :: new ( Error :: failed (
1230
+ "Pipeline call on a request that returned no capabilities or was already closed ." . to_string ( ) ,
1231
+ ) ) ) as Box < dyn PipelineHook > ,
1222
1232
Some ( base) => {
1223
- let pipeline = match base. pipeline {
1233
+ match base. pipeline {
1224
1234
Some ( ref pipeline) => pipeline. add_ref ( ) ,
1225
1235
None => Box :: new ( broken:: Pipeline :: new ( Error :: failed (
1226
1236
"Pipeline call on a request that returned not capabilities or was \
1227
1237
already closed."
1228
1238
. to_string ( ) ,
1229
1239
) ) ) as Box < dyn PipelineHook > ,
1230
- } ;
1231
- let ops = to_pipeline_ops ( promised_answer. get_transform ( ) ?) ?;
1232
- Ok ( pipeline. get_pipelined_cap ( & ops) )
1240
+ }
1233
1241
}
1234
- }
1242
+ } ;
1243
+ let ops = to_pipeline_ops ( promised_answer. get_transform ( ) ?) ?;
1244
+ Ok ( pipeline. get_pipelined_cap ( & ops) )
1235
1245
}
1236
1246
}
1237
1247
}
@@ -2356,11 +2366,15 @@ impl ResultsDone {
2356
2366
( false , Ok ( ( ) ) ) => {
2357
2367
let exports = {
2358
2368
let root: message:: Builder = message. get_body ( ) ?. get_as ( ) ?;
2359
- let message:: Return ( ret) = root. which ( ) ? else {
2369
+ let message:: Return ( Ok ( mut ret) ) = root. which ( ) ? else {
2360
2370
unreachable ! ( )
2361
2371
} ;
2372
+ if cap_table. is_empty ( ) {
2373
+ ret. set_no_finish_needed ( true ) ;
2374
+ finish_received. set ( true ) ;
2375
+ }
2362
2376
let crate :: rpc_capnp:: return_:: Results ( Ok ( payload) ) =
2363
- ret? . which ( ) ?
2377
+ ret. which ( ) ?
2364
2378
else {
2365
2379
unreachable ! ( )
2366
2380
} ;
0 commit comments