Should `Symbol.disposable` implementations generally be idempotant?
lucacasonato opened this issue · comments
We're currently determining what resources to implement Symbol.disposable
for in Deno. It has come up as a question whether the Symbol.disposable
/ Symbol.asyncDisposable
implementations for resources like subprocesses should be idempotant or not. As far as I can tell, the readme and spec does not give guidance on this right now, but looking at the examples and Web Platform resources, I think it should probably be.
An example:
{
await using process = new Deno.Command("echo", { args: ["hello"] }).spawn();
const output = await process.output(); // wait for the process to die, and collect the output
// here `process` goes out of scope, and `await process[Symbol.asyncDispose]()` is called.
}
In this example, process
is an object with a [Symbol.asyncDispose]
method that internally calls kill(pid)
and then waits for the process to shut down (await process.status
). This does mean that if the process is already shut down (either because the user killed the process earlier, or because the process naturally died), kill(pid)
will error because the process has already been reaped. Alternatively, we could catch this error when calling kill(pid)
.
What is the champions' guidance here? It would be great if we could record this guidance in the README and/or as a note in the spec.
The best practice is that calling [Symbol.dispose]()
or [Symbol.asyncDispose]()
twice on the same object should not throw, though if the object transitions from a disposed state back into a live state (such as reopening a closed connection, etc.) it could potentially throw upon a subsequent dispose.
This is already indicated within the specification text in the following two locations:
- https://tc39.es/proposal-explicit-resource-management/#sec-disposable-interface
If called more than once on the same object, the function should not throw an exception. However, this requirement is not enforced.
- https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposable-interface
If called more than once on the same object, the function should not throw an exception. However, this requirement is not enforced.
Great, thank you! I missed that. I'll close this then :)