-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Bug fix: Console child wait for outstanding promises #5654
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:D
.then(() => process.exit(0)) | ||
.then(returnStatus => { | ||
process.exitCode = returnStatus; | ||
return require("@truffle/promise-tracker").waitForOutstandingPromises(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gnidan, What does waitForOutstandingPromisees()
do here? It seems like a nop
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It... waits for outstanding promises so we can process.exit()
cleanly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's called with no params, so, isn't iterable
in the implementation just an empty array. Unless, I'm missing some setup in this scenario :/
export async function waitForOutstandingPromises<T extends Object>(
options?: WaitForOutstandingPromiseOptions<T>
) {
const { target } = options || {};
let { catchHandler } = options || {};
const iterable =
(target
? _outstandingPromiseInstanceMap.get(target)?.values()
: _outstandingPromiseMap.keys()) || [];
catchHandler = catchHandler || (() => {});
await Promise.all(Array.from(iterable, p => p.catch(catchHandler)));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, hrm. Something weird must be implicitly happening, because this works and matches the usage in cli.js that @benjamincburns put there.
Ben, can you teach us?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tl;dr: This is being used as expected here.
It’s called with no params, so, isn’t iterable in the implementation just an empty array?
When waitForOutstandingPromises
is called with no arguments (as it should be in all non-test use cases), the value of iterable
is only an empty array when there are no outstanding promises on which to wait.
The object destructuring assignment for target
coupled with the ternary operator probably makes this code horrendously unreadable. Sorry about that. Allow me to break things down a bit...
First off, on the line where we initialize target
:
const { target } = options || {};
We wind up with target
being assigned undefined
. This happens because options
is undefined
, so we fallback to the empty object literal and then attempt to destructure the target
property out of that empty object literal. The empty object literal has no target
property, so the destructuring assignment results with target
being initialized with a value of undefined
.
Given that detail, the following statement should be a bit more clear:
const iterable =
(target
? _outstandingPromiseInstanceMap.get(target)?.values()
: _outstandingPromiseMap.keys()) || [];
The value of target
is undefined
, so we branch to the "else" statement in the ternary operator, which assigns the return value of _outstandingPromiseMap.keys()
to the iterable
variable. This always assigns that return value, as _outstandingPromiseMap
is a module-scoped variable, and it's initialized as new Map<Promise<any>, boolean>()
on module load (see code here).
If there's any further uncertainty, be sure to check the tests for the promise-tracker
package as they're fairly comprehensive and they demonstrate and guard its usage nicely.
@gnidan @cds-amal @cliffoo please let me know if I can be of further assistance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @benjamincburns, my question relates to how _outstandingPromiseMap
is seeded with values. If nothing is inserted, then how does it know what to track? This PR only calls the tracking code (as a JS module) and doesn't have any code to set/identify/decorate what has to be awaited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also if it helps any, this is the function that does the actual task tracking.
The tracked
decorator injects this logic by returning a property descriptor that replaces the decorated method implementation with a closure over that impl that proxies calls to it and handles tracking of outstanding promises by wrapping any returned promises in special promise handlers that manage the state of _outstandingPromiseMap
and friends.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise the necessary code in question is already decorated.
dashboard-message-bus-client
depends onpromise-tracker
, and its internalsend
method is tracked here.
Thanks @benjamincburns, this is the piece I was missing. It was unclear from the code that it relied on an already coded setup.
There should probably be a comment to explain this in the waiting portion, this PR.
core/lib/console-child.js
should wait for outstanding promises to resolve before exiting, using promise-tracker, just likecore/cli.js
This issue is surfaced in dashboard decoding PR, where the browser doesn't have the necessary compilations to decode so it complains by asking the user to
truffle compile --all
, andcompile --all
in a truffle console (connected to dashboard network) doesn't do anything, because the dashboard message bus client in the event system doesn't get a chance to send anything over the bus due to the process exiting prematurely.@gnidan helped me debug this. It's improbable that I could dig this up myself.