Skip to content

Importing OpenTUI stops Effect from properly running teardown hooks #248

@limwa

Description

@limwa

Description

As the title says, simply importing @opentui/core disables all teardown hooks on Effect (or any other library, for that matter).

Example

import { Effect } from "effect";
import { BunRuntime } from "@effect/platform-bun";

// Import opentui with side-effects
import "@opentui/core";

const program = Effect.async<void, Error>((resume) => {
    console.log("Running effect...");

    // Complete the effect after 10s
    const id = setTimeout(() => {
        console.log("Effect completed normally!");
        resume(Effect.void);
    }, 10000);

    // Return an effect to run if the fiber is interrupted
    return Effect.sync(() => {
        console.log("Effect was interrupted!");
        clearTimeout(id);
    });
});

BunRuntime.runMain(program);

Expected behavior

If I press CTRL+C while this program is running, I would expect the logs to be as follows:

Running effect...
^CEffect was interrupted!

Observed behavior

If I press CTRL+C while this program is running, the log is as follows:

Running effect...
^C

Proposed fixes

I've already determined that the cause is the following code snippet:

singleton("ProcessExitSignals", () => {
;["SIGINT", "SIGTERM", "SIGQUIT", "SIGABRT"].forEach((signal) => {
process.on(signal, () => {
process.exit()
})
})
})

I'm only speaking for myself here, but I was not expecting that importing @opentui/core would have these kinds of side effects, and I initially thought it was a problem with my effect code.

Here are some proposals for dealing with this issue:

  1. Use process.exitCode = 0; instead of process.exit().
  • This does not immediately cause bun to exit, and effect runs all teardown hooks, as expected. This is a simple fix that works for effect, and users can still use process.on("beforeExit", ...) and process.on("exit", ...).
  • However, as far as I am aware, after setting exitCode, it is impossible to stop the runtime from terminating. As such, this might still be a problem for some users who might want to ignore SIGINT and some other signals.
  • Furthermore, this fix still doesn't eliminate side effects when importing @opentui/core.
  1. OR: do not add the event handlers for the process exit signals.
  • If this is needed for some sort of cleanup, or to allow the process to be shut down when these signals are received, I would propose that a new option can be added to the render function, which would allow users to specify what to do when these signals are received.
  • This option could have as the default implementation (signalType: ...) => process.exitCode = 0;.
  1. OR: put this singleton under an environment variable.

(I'd like to note that I'm not really familiar with the opentui codebase, and I don't know how much effort these ideas would take to implement. These are just what I thought of when writing this issue!)

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions