Skip to content

Revisit unrefed handles and beforeExit internals #3665

Closed
@Fishrock123

Description

@Fishrock123

The original issue is at #1264

The synopsis is that because of how uv_run works, unrefed handles show up as active still for the first run, and don't appear as no-longer active until the second run.

In the past, running an unrefed timer in beforeExit would infinitely loop, since the second run would always be in the next beforeExit, consequentially calling 'beforeExit' again and scheduling another time, looping infinitely. This was fixed in #3407 by making unrefed timers of the same timeout use the previous handle, which is then properly unreferenced.

As described, one would expect the event loop / beforeExit code to look something like so:

bool more;
uv_run_mode run_mode = UV_RUN_ONCE;
do {

  more = uv_run(env->event_loop(), run_mode);
  if (more == false) {
    EmitBeforeExit(env);

    // Emit `beforeExit` if the loop became alive either after emitting
    // event, or after running some callbacks.
    more = uv_loop_alive(env->event_loop());
    run_mode = UV_RUN_NOWAIT;
  } else {
    run_mode = UV_RUN_ONCE;
  }
} while (more == true);

However in reality it looks more like this:
(

node/src/node.cc

Lines 4063 to 4078 in 471aa5a

bool more;
do {
v8::platform::PumpMessageLoop(default_platform, isolate);
more = uv_run(env->event_loop(), UV_RUN_ONCE);
if (more == false) {
v8::platform::PumpMessageLoop(default_platform, isolate);
EmitBeforeExit(env);
// Emit `beforeExit` if the loop became alive either after emitting
// event, or after running some callbacks.
more = uv_loop_alive(env->event_loop());
if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
more = true;
}
} while (more == true);
)

bool more;
do {
  more = uv_run(env->event_loop(), UV_RUN_ONCE);

  if (more == false) {
    EmitBeforeExit(env);

    // Emit `beforeExit` if the loop became alive either after emitting
    // event, or after running some callbacks.
    more = uv_loop_alive(env->event_loop());
    if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
      more = true;
  }
} while (more == true);

If you look closely at the actual version, you'll notice that uv_run ends up actually being called at least 2 times on a beforeExit re-entry anyways, which logically register the timer and unref. However it does not seem to work like that.

Sniff test says it may be some discrepancy within uv_run modes?

cc @indutny, @trevnorris, @saghul

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedIssues that need assistance from volunteers or PRs that need help to proceed.lib / srcIssues and PRs related to general changes in the lib or src directory.libuvIssues and PRs related to the libuv dependency or the uv binding.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions