Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Josh Holbrook
Josh Holbrook

Posted on

Matanuska ADR 006 - Runtime Exit

This article is a repost of an ADR fromMatanuska BASIC, my attempt to write a BASIC interpreter in TypeScript.

Context

Matanuska supports anexit command, which exits the interpreter.

Currently, Matanuska's architecture supports exiting in the CLI through an exit handler configured in theCli class, and a special exception calledExit. Currently, theExit exception only supports successful exits.

While implementing theexit command in Matanuska, I first opted to emit an event onRuntime (which would now be anEventEmitter), listened to that event in theCommander, and implemented aHost#exit method to actually callprocess.exit.

These two mechanisms are redundant and inconsistent. We would like to find one mechanism for exiting the application in a non-error context.

Why an Error? Why in Cli?

Exiting has to be implemented inCli because it contains the top-level error handling code. This is where we decide how to report on various Errors which may be thrown by other parts of the application, and how to exit.

The motivation for the exit handler override is entirely testing. When testing theCli abstraction, I want it to "exit" without actually ending the process. In the relevant tests, I override the exit handler that asserts the expected exit code and throws a test-only Error to stop execution and signal the exit.

The motivation for anExit Error type is that it allows for error handling to implement graceful shutdown - that is, error handling in the rest of the application can call its "finally" blocks to cleanly spin down resources before an exit. It's also inspired byclick's API - the actual needs were unknown, but given I was implementing a CLI framework, following click's lead seemed reasonable.

A behavior to keep in mind is that theExit error type's message is written to output. This is so thatExit can be used to share help text (and similar use cases) when doing options parsing in theConfig class.

Why Host#exit?

As Matanuska has evolved, it's become clear that theHost abstraction owns much more than just logging - in fact, it owns all "os-level" actions. This includes things like getting the current UNIX user and the current working directory. Through this lens, it's appropriate for it to handle exits as well.

This also offers a clear, consistent mechanism for overriding exit behavior - that is, overriding the Host. TheCli class already supports a customHost, and the tests include aMockConsoleHost used for these purposes. It would be natural to extendMockConsoleHost to implement a test-only behavior for exits as well.

Why an EventEmitter?

The event emitter interface is largely motivated by an interest in delegating exit behavior to theCommander.

Given youare going to delegate to theCommander, the alternative to an event is injecting it as a dependency to theRuntime. Events allow the runtime to be unaware of the commander, at the cost of the commander being unable to "yield" data back to the runtime.

Unfortunately, there are already reasons to inject the commander into the runtime. In particular, the commander handles prompting, because it handles thereadline interface. This decision was made because readline requires asynchronous initialization, and because it's higher level than what the host provides. That decision isn't set in stone, but itis really convenient.

While it hasn't been implemented yet, the runtime will need to request input from the commandereventually - so we might as well inject it now and avoid two interfaces.

But we could also inject the host into the runtime, and have it callHost#exit directly. In fact, the host is already injected, just not used.

The alternative to this is implementing proxy methods on the commander whenever the runtime needs to access anything from the host. But the host contains alot of functionality, and effectively adding all of the host's functionality to the commander muddies its interface.

Decision

  1. TheHost will be injected into theRuntime, where itsexit method will be called directly. This will follow a pattern which should become more common over time.
  2. TheRuntime willnot inherit fromEventEmitter, instead preferring to call methods on an injectedCommander instance. This will create one consistent way to call back to theCommander that supports "yielding".
  3. ConsoleHost#exit will throw anExit error. This will allow for graceful shutdown behavior, while using the host as the common path for exits within the interpreter. 4. TheExit error will be extended to take an exit code. This will allow for its use with intentional non-zero exits.
  4. Cli will continue to handle actual exit behavior. This will include overriding theexit handler in tests.
  5. MockConsoleHost#exit will throw aMockExit error, maintaining the current structure of the tests.

In other words, when the runtime handles anOpCode.Exit, the following will occur:

  1. The runtime will callHost#exit with the exit code.
  2. The host will throw anExit error with the exit code.
  3. The error will be caught and handled in theCli class.

Note a subtlety in testing: bothHost#exit and the CLI exit handlershould throw an error to stop execution. This is to ensure that they short-circuit execution in tests as they do in practice. The code has been factored to include a return after the relevant calls, whichshould protect against that, but it's an easy footgun. This could be addressed through the typing system by returningnever instead ofvoid, but this is unimplemented in the interest of maximizing flexibility.

Therefore, bothMockConsoleHost#exit and the test exit handler throw aMockExit. A consequence of this is that it's not possible to distinguish between a triggered exit and a "clean exit" - but the tests don't cover that distinction, instead simply asserting the exit code as 0.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I'm an Alaskan software developer with expertise in data, distributed systems and site reliability.
  • Location
    Alaska
  • Education
    M.S. Mechanical Engineering
  • Work
    Staff Engineer
  • Joined

More fromJosh Holbrook

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp