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

Add Access Handles to spec #21

Merged
merged 15 commits into from
Sep 29, 2022
74 changes: 17 additions & 57 deletions AccessHandle.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
- [New data access surface](#new-data-access-surface)
- [Locking semantics](#locking-semantics)
- [Open Questions](#open-questions)
- [Naming](#naming)
- [Assurances on non-awaited consistency](#assurances-on-non-awaited-consistency)
- [Trying It Out](#trying-it-out)
- [Appendix](#appendix)
- [AccessHandle IDL](#accesshandle-idl)
- [References & acknowledgements](#references--acknowledgements)
Expand Down Expand Up @@ -82,7 +82,7 @@ A few examples of what could be done with *AccessHandles*:
relying on the new surface's performance and direct buffered access to
offload sound segments to disk instead of holding them in memory.
* Provide a fast and persistent [Emscripten](https://emscripten.org/)
filesystem to act as generic and easily accessible storage for Wasm.
file system to act as generic and easily accessible storage for Wasm.

## Non-goals

Expand Down Expand Up @@ -122,18 +122,16 @@ API](https://docs.google.com/document/d/1cOdnvuNIWWyJHz1uu8K_9DEgntMtedxfCzShI7d

```javascript
// In all contexts
// For details on the `mode` parameter see "Exposing AccessHandles on all
// filesystems" below
const handle = await file.createAccessHandle({ mode: "in-place" });
await handle.writable.getWriter().write(buffer);
const reader = handle.readable.getReader({ mode: "byob" });
const accessHandle = await fileHandle.createAccessHandle();
await accessHandle.writable.getWriter().write(buffer);
const reader = accessHandle.readable.getReader({ mode: "byob" });
// Assumes seekable streams, and SharedArrayBuffer support are available
await reader.read(buffer, { at: 1 });

// Only in a worker context
const handle = await file.createSyncAccessHandle();
const writtenBytes = handle.write(buffer);
const readBytes = handle.read(buffer, { at: 1 });
const accessHandle = await fileHandle.createSyncAccessHandle();
const writtenBytes = accessHandle.write(buffer);
const readBytes = accessHandle.read(buffer, { at: 1 });
```

As mentioned above, a new *createAccessHandle()* method would be added to
Expand All @@ -158,13 +156,13 @@ default reader and writer with a *seek()* method.
### Locking semantics

```javascript
const handle1 = await file.createAccessHandle({ mode: "in-place" });
const accessHandle1 = await fileHandle.createAccessHandle();
try {
const handle2 = await file.createAccessHandle({ mode: "in-place" });
const accessHandle2 = await fileHandle.createAccessHandle();
} catch (e) {
// This catch will always be executed, since there is an open access handle
}
await handle1.close();
await accessHandle1.close();
// Now a new access handle may be created
```

Expand All @@ -184,36 +182,6 @@ observe changes done through the new API, even if a lock is still being held.

## Open Questions

### Naming

The exact name of the new methods hasn’t been defined. The current placeholder
for data access is *createAccessHandle()* and *createSyncAccessHandle()*.
*createUnflushedStreams()* and *createDuplexStream()* have been suggested.

### Exposing AccessHandles on all filesystems

This proposal only currently considers additions to OPFS, but it would probably
be worthwhile to expand the new functionality to arbitrary file handles. While
the exact behavior of *AccessHandles* outside of OPFS would need to be defined
in detail, it's almost certain that the one described in this proposal should
not be the default. To avoid setting it as such, we propose adding an optional
*mode* string parameter to *createAccessHandle()* and
*createSyncAccessHandle()*. Some possible values *mode* could take are:

* 'shared': The current behavior seen in File System Access API in general,
there is no locking and modifications are atomic (meaning that they would
only actually change the file when the *AccessHandle* is closed). This mode
would be a safe choice as a default value.
* 'exclusive': An exclusive write lock is taken on the file, but modifications
are still atomic. This is a useful mode for developers that want to
coordinate various writing threads but still want "all or nothing" writes.
* 'in-place': The behavior described in this proposal, allowing developers to
use high performance access to files at the cost of not having atomic writes.
It's possible that this mode would only be allowed in OPFS.

Both the naming and semantics of the *mode* parameter have to be more concretely
defined.

### Assurances on non-awaited consistency

It would be possible to clearly specify the behavior of an immediate async read
Expand Down Expand Up @@ -249,19 +217,11 @@ interface FileSystemFileHandle : FileSystemHandle {
Promise<File> getFile();
Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWritableOptions options = {});

Promise<FileSystemAccessHandle> createAccessHandle(optional FileSystemFileHandleCreateAccessHandleOptions options = {});
Promise<FileSystemAccessHandle> createAccessHandle();
[Exposed=DedicatedWorker]
Promise<FileSystemSyncAccessHandle> createSyncAccessHandle(optional FileSystemFileHandleCreateAccessHandleOptions options = {});
};

dictionary FileSystemFileHandleCreateAccessHandleOptions {
AccessHandleMode mode;
Promise<FileSystemSyncAccessHandle> createSyncAccessHandle();
};

// For more details and possible modes, see "Exposing AccessHandles on all
// filesystems" above
enum AccessHandleMode { "in-place" };

interface FileSystemAccessHandle {
// Assumes seekable streams are available. The
// Seekable extended attribute is ad-hoc notation for this proposal.
Expand All @@ -282,18 +242,18 @@ interface FileSystemAccessHandle {
[Exposed=DedicatedWorker]
a-sully marked this conversation as resolved.
Show resolved Hide resolved
interface FileSystemSyncAccessHandle {
unsigned long long read([AllowShared] BufferSource buffer,
FilesystemReadWriteOptions options);
optional FileSystemReadWriteOptions options);
unsigned long long write([AllowShared] BufferSource buffer,
FilesystemReadWriteOptions options);
optional FileSystemReadWriteOptions options);

Promise<undefined> truncate([EnforceRange] unsigned long long size);
Promise<unsigned long long> getSize();
Promise<undefined> flush();
Promise<undefined> close();
};

dictionary FilesystemReadWriteOptions {
[EnforceRange] unsigned long long at;
dictionary FileSystemReadWriteOptions {
[EnforceRange] unsigned long long at = 0;
};
```

Expand Down
Loading