Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Only remove listeners left around by soljson #4092

Merged
merged 9 commits into from
Jun 11, 2021
Merged

Conversation

gnidan
Copy link
Contributor

@gnidan gnidan commented Jun 5, 2021

So, here's a hypothesis for the uncaughtException business in compile-solidity.

Solidity upgraded Emscripten from v1.35.4 to v1.37.21 in solc v0.5.1 (in this PR). Inspecting the Emscripten changelog, this issue was closed as part of v1.36.6. Looks like the fix here may have changed buggy behavior that left uncaughtExceptions to linger.

Since we support old Solidity and need to handle pre-v0.5.1 behavior, this PR proposes that we alter the listener removal by first capturing the listeners that existed before loading solc and then removing only those listeners that are new after loading solc.

This is a different proposed solution than #4091... I haven't done any digging re: that PR's hypothesis that we must remove listeners for unhandledRejection, but I suspect that may be a red herring. This PR should address the "exits violently" part of #4090 (as well as many other issues over the past however-long)

@gnidan
Copy link
Contributor Author

gnidan commented Jun 5, 2021

Another approach might be to look at the actual version of solc, but I went with this because the explicit set comparison will handle the problem more generally... and cover nightlies and such, so we won't need to identify safe/unsafe versions so rigorously.

@gnidan gnidan requested a review from davidmurdoch June 5, 2021 04:22
@cds-amal
Copy link
Member

cds-amal commented Jun 5, 2021

Maybe both unhandledRejection and uncaughtException listeners need to be removed. Here's a quick way to tickle solcjson's unhandledRejection.

reprod
use-truffle-core

truffle init

cat << EOF > ./migrations/1_initial_migration.js
const Migrations = artifacts.require("Migrations");

module.exports = function (deployer) {
  Promise.reject(4242);
  deployer.deploy(Migrations);
};
EOF

truffle test

Copy link
Member

@cds-amal cds-amal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this approach and think it should be extended. I tested it with a rejected promise in migrations and noted soljson's unhandledRejection listener results in the EMSCRITEN screen dump.

The repro steps for the bug is in a previous comment.

cds-amal added 3 commits June 6, 2021 11:38
while reviewing @gnidan's pr, I wanted to test the behavior of current
soljson as scaffolded by `truffle init` with a contrived
unhandledRejection. That is, adding `Promise.reject(4242)` to a
migrations deploy script.

depending on the version soljson registers for `uncaughtException` as
well as `unhandledRejection`. However BOTH handlers will abort and dump
emscripten large generated source to stdout. yuck.
This is controversial, but I think our use case, and space requires
erring on the side of caution.

[See this discusion](nodejs/node#22822) for
more perspective.
  - [x] truffle migrate
  - [ ] truffle test
  - [ ] truffle compile
  - [ ] truffle debug
  - [ ] truffle console
  - [ ] truffle migrate
  - [ ] others?
@cds-amal cds-amal self-requested a review June 10, 2021 22:19
Copy link
Member

@cds-amal cds-amal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some minor nits.

Co-authored-by: amal <cds.sudama@gmail.com>
@cds-amal cds-amal self-requested a review June 10, 2021 23:38
@cds-amal
Copy link
Member

Nice one @gnidan !

Comment on lines 94 to 100
const newListeners = listeners.filter(
(listener) => !markedListeners[eventName].has(listener),
);

if (execeptionHandler) {
process.removeListener("uncaughtException", execeptionHandler);
for (const listener of newListeners) {
process.removeListener(eventName, listener);
}
Copy link
Member

@davidmurdoch davidmurdoch Jun 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be condensed to:

for (const eventName in markedListeners) {
  const marked = markedListeners[eventName];
  process.listeners(eventName).forEach((listener) => {
    if (!marked.has(listener)) {
      process.removeListener(eventName, listener);
    }
  });
}

And if we change markedListeners to a Map<string, Array<AnyFunction> it could be:

for (const [eventName, marked] of markedListeners) {
  process.listeners(eventName).forEach((listener) => {
    if (!marked.has(listener)) {
      process.removeListener(eventName, listener);
    }
  });
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Borrowed the first one :) Thanks @davidmurdoch!

@gnidan
Copy link
Contributor Author

gnidan commented Jun 11, 2021

(I feel like there's a joke here that begins "how many engineers does it take to add a mutex lock?" :)

Thanks @cds-amal and @davidmurdoch! Will merge when green.

Comment on lines +92 to +97
for (const eventName in markedListeners) {
const marked = markedListeners[eventName];
for (const listener of process.listeners(eventName)) {
if (!marked.has(listener)) {
process.removeListener(eventName, listener);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! Thank you!

@gnidan gnidan merged commit 30e9926 into develop Jun 11, 2021
@gnidan gnidan deleted the fix/listener-removal branch June 11, 2021 10:25
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants