Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to prevent consumer code from accessing the dispose method #69

Closed
PaulKiddle opened this issue Oct 7, 2021 · 4 comments
Closed

Comments

@PaulKiddle
Copy link

If I'm creating a resource to pass to other code, I don't necesarily want that code to be able to dispose the resource. Creation and disposal are, to me, concepts that are separate from the resource's actual usage, whereas the requirement of a 'dispose' symbol on the resource conflates the two concepts.

Have any other options been considered? Perhaps an iterator that yields the resource and disposes it afterwards (as developers are currently used to)

@rbuckton
Copy link
Collaborator

rbuckton commented Oct 7, 2021

I'd rather not introduce extra complexity that might deter usability. Explicit cleanup is already part of the public API of many runtimes and packages in JS already (see Motivations in the explainer). The majority of the languages which are referenced as prior art allow consumers to explicitly dispose:

  • C# — Via IDisposable.Dispose()
  • Java — Via AutoClosable.close()
  • C++ — Via delete
  • Python — Via __exit__

Iterators are only one of many mechanisms that employ resource cleanup (via return) and are very specialized in their use (either as a value producer or as a co-routine-like protocol).

That said, the provider of a resource can model their API in any way they choose. If you wish to yield values that are disposed at the end of iteration without providing access to cleanup, that is completely up to your design. For example:

function * resourceProducer() {
  const resource = ...; // some resource to yield. does not have a [Symbol.dispose]
  const cleanup = () => { ... }; // cleanup mechanism for resource

  using const holder = new Disposable(cleanup); // registers cleanup step 

  yield resource; // provides resource that can't be directly disposed.

  ...

} // `holder` is disposed when `resourceProducer()` exits.

// consume resources
for (const resource of resourceProducer()) {
  // resource not directly disposable

} // `resource` cleanup handled by `resourceProducer` when iterator is exhausted or `.return()`/`.throw()` is called.

The addition of using const and Symbol.dispose do not preclude you from modeling your API as fits your needs.

@PaulKiddle
Copy link
Author

PaulKiddle commented Oct 8, 2021

The problem with this is that the for of method of consuming a disposable resource, which is what I currently use for this use case, is (a) it's not clear that the code is dealing with a disposable as opposed to an iterable (b) there's no guarantee that only one value will be yielded (especially if the disposable comes from third party code).

Personally I would like to see something that is similar to for of but only performs one iteration before returning:

// resourceProvider is a generator
using (const resource of resourceProvider()){
  // This block is guaranteed to only run once, no matter what the provider does
}

A nice advantage of this is that you wouldn't have to introduce a new Disposable class or symbol to the language, since iterators will do the job - and can accomodate resources with or without a disposable method:

function * openFile(path){
  const fh = getFileHandle(path);
  try {
    yield new File(fh);
  } finally {
    releaseFileHandle(fh);
   // Equally this could be fh.destroy() if you want
  }
}

I understand you concern about not adding too much complexity, but I don't think my concern is trivial either - forcing data into a specific structure in order to use a particular language feature, even if that structure doesn't make sense from a logical or security point of view. I shouldn't have to leak information about how to dispose an object if it's not relevant to the consumer.

@rbuckton
Copy link
Collaborator

rbuckton commented Aug 5, 2022

I don't believe that resource management should be tied to iterators. Iterators are the wrong level of abstraction and only some iterators are "disposable" (via return).

I understand you concern about not adding too much complexity, but I don't think my concern is trivial either - forcing data into a specific structure in order to use a particular language feature, even if that structure doesn't make sense from a logical or security point of view. [...]

This is often exactly how new language features are supported in both JS and other languages. For an object in JS to be treated as a resource either using Symbol.dispose or as an iterator, you still need to indicate to the runtime how that disposal should be handled, which means introducing a specific structure to use that data. If we were to leverage iterators as you suggest, that would mean introducing a Symbol.iterator method to an object. However, Symbol.iterator has a meaning associated with it. The majority of Iterators/Iterables in JS are expected to be some form of sequence/list. They're generally expected to be used in for..of, array destructuring, array spread, etc. Your concern about forcing data into a structure that "doesn't make sense from a logical [...] point of view" is in direct opposition to using iterators for resource management.

Using iterators purely to manage lifetime adds even more responsibilities to an already overloaded concept. Some Iterators may be disposable, and some disposables may be iterated, but that primarily shows that these are two very loosely related but different concepts.

I'm also not sure how a "security point of view" figures into this discussion either. Neither [Symbol.dispose] nor Iterators would prevent a user from programmatically disposing a resource, and disposal itself isn't directly a security-related feature. Programmatic disposal is an intentional part of this proposal, and it would be nearly impossible to implement disposable containers without it.

@rbuckton
Copy link
Collaborator

rbuckton commented Sep 7, 2022

I will consider this issue as closed, but please let me know if you have any additional concerns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants