Allocate just one ClosureTable entry per self-send#2663
Allocate just one ClosureTable entry per self-send#2663mergify[bot] merged 65 commits intomasterfrom
ClosureTable entry per self-send#2663Conversation
undortunatrely I still see ``` trace: Warning: `stdenv.lib` is deprecated and will be removed in the next release. Please use `lib` instead. For more information see NixOS/nixpkgs#108938 ```
... but I don't see the `ping! ...` log line :-(
|
This PR does not affect the produced WebAssembly code. |
but I cannot see yet how oneshot is involved
still no closures lost by oneshot
These are copies from `run-drun/ok/count-callbacks.*`, but strangely running `accept` won't refresh them!?
Fun thing is that these appear for other tests than the one _designed to trigger_ it. The plot thickens...
must be a Motoko test from an inner directory
this triggers for ``` make -C test/run-drun commit-on-await.only make -C test/run-drun await-without-yield.only ``` but strangely not for ``` make -C test/run-drun oneshot-callbacks.only ``` It remains to be investigated why this is the case. Also I see `ping` being rejected (or at least the reject callback being called) for one-shot sends.
this is the current trace: oneshot-callbacks: [tc] [comp] [comp-ref] [valid] [valid-ref] [ic-ref-run] --- oneshot-callbacks.ic-ref-run (expected) +++ oneshot-callbacks.ic-ref-run (actual) @@ -4,19 +4,67 @@ ← replied: () → update go() debug.print: go 0: 0 +debug.print: remember_closure: 0 +debug.print: remember_closure: 1 debug.print: ping! 2 +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT debug.print: go 1: 1 +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: remember_closure: 1 +debug.print: remember_closure: 0 debug.print: ping! 2 -← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.26-14.38" +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: go 2: 1 +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: IN REJECT callback +← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.98-14.110" → update go() debug.print: go 0: 1 +debug.print: remember_closure: 0 +debug.print: remember_closure: 2 debug.print: ping! 3 +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT debug.print: go 1: 2 +debug.print: recall_closure: 2 +debug.print: recall_closure: 2 OUT +debug.print: remember_closure: 2 +debug.print: remember_closure: 0 debug.print: ping! 3 -← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.26-14.38" +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 2 +debug.print: recall_closure: 2 OUT +debug.print: go 2: 2 +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: IN REJECT callback +← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.98-14.110" → update go() debug.print: go 0: 2 +debug.print: remember_closure: 0 +debug.print: remember_closure: 3 debug.print: ping! 4 +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT debug.print: go 1: 3 +debug.print: recall_closure: 3 +debug.print: recall_closure: 3 OUT +debug.print: remember_closure: 3 +debug.print: remember_closure: 0 debug.print: ping! 4 -← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.26-14.38" +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 3 +debug.print: recall_closure: 3 OUT +debug.print: go 2: 3 +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: IN REJECT callback +← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.98-14.110" Some tests failed: oneshot-callbacks.mo I *think* I understand this by now :-)
…leak This doesn't handle the case yet where the reject traps, but the testcase doesn't implement that just yet. Here comes the evidence trace: ``` oneshot-callbacks: [tc] [comp] [comp-ref] [valid] [valid-ref] [ic-ref-run] --- oneshot-callbacks.ic-ref-run (expected) +++ oneshot-callbacks.ic-ref-run (actual) @@ -4,19 +4,73 @@ ← replied: () → update go() debug.print: go 0: 0 +debug.print: remember_closure: 0 +debug.print: remember_closure: 1 debug.print: ping! 2 +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT debug.print: go 1: 1 +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: remember_closure: 1 +debug.print: remember_closure: 0 debug.print: ping! 2 -← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.26-14.38" +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: go 2: 1 +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: IN SELF REJECT callback +← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.98-14.110" → update go() -debug.print: go 0: 1 -debug.print: ping! 3 -debug.print: go 1: 2 -debug.print: ping! 3 -← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.26-14.38" +debug.print: go 0: 0 +debug.print: remember_closure: 1 +debug.print: remember_closure: 0 +debug.print: ping! 2 +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: go 1: 1 +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: remember_closure: 0 +debug.print: remember_closure: 1 +debug.print: ping! 2 +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: go 2: 1 +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: IN SELF REJECT callback +← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.98-14.110" → update go() -debug.print: go 0: 2 -debug.print: ping! 4 -debug.print: go 1: 3 -debug.print: ping! 4 -← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.26-14.38" +debug.print: go 0: 0 +debug.print: remember_closure: 0 +debug.print: remember_closure: 1 +debug.print: ping! 2 +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: go 1: 1 +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: remember_closure: 1 +debug.print: remember_closure: 0 +debug.print: ping! 2 +debug.print: RECALLING, but this may trap and gets undone +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: go 2: 1 +debug.print: recall_closure: 0 +debug.print: recall_closure: 0 OUT +debug.print: recall_closure: 1 +debug.print: recall_closure: 1 OUT +debug.print: IN SELF REJECT callback +← rejected (RC_CANISTER_REJECT): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: assertion failed at oneshot-callbacks.mo:14.98-14.110" Some tests failed: oneshot-callbacks.mo ```
instead, perpetuate the argument directly
|
We have several problems currently. For a) the solution is easy, pop the one slot in the cleanup callback. |
|
To avoid serializing/deserializing the result of the future, if wonder if it would make sense to transfer it in a fourth slot of that array? |
Yep, I was thinking overwriting the third position of the array (i.e. the future) since that is not needed any more. But sending the payload back in heap changes the semantics a bit, so I'd only do that for single, small results. Update: this is #2679 now. |
this means _the other side_ never owns a table slot and must simply peek into the array to fetch the future's closure cleanup becomes the same as a foreign send's
review feedback
and implement `ic_call` and `ic_self_call` in terms of it
crusso
left a comment
There was a problem hiding this comment.
LGTM.
(Good you refactored the ic_(self)_call too - wasn't actually asking for that ;->)
crusso
left a comment
There was a problem hiding this comment.
LGTM.
(Good you refactored the ic_(self)_call too - wasn't actually asking for that ;->)
(One formatting change only)
This is a wholesale rename, in the spirit of #2663 (comment)
|
Thanks for this! I had quite some trouble figuring out what’s happening based on the patch only, but the comment in #2663 (comment) helped. Maybe worth expanding the code comments a bit, so that future readers (who don’t have the benefit of the PR discussion) can make sense of it? Probably one comprehensive GHC-style code node that explains how async self-calls works would be useful. |
This PR cleans up the
ClosureTableentries in the last-resort cleanup callback. Since potentially all call and reply state changes of a canister can be rolled back by carefully engineering the traps, the initiator of a message send must only allocate exactly one slot in the closure table. Formerly self-sends would leak a slot when the receiver trapped and the reject callback too. See #2663 (comment).Motivation: fixes #2640