Skip to content

Commit

Permalink
Add new SAH and WFS locking modes
Browse files Browse the repository at this point in the history
Adds new locking modes for sync access handles and writable file
streams. Updates "file entry/take a lock" and "file entry/lock/release"
to support these new modes.
  • Loading branch information
Nathan Memmott committed Dec 20, 2023
1 parent 41b97ba commit 564df68
Showing 1 changed file with 68 additions and 18 deletions.
86 changes: 68 additions & 18 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ never be allowed using this API, rather than leaving it entirely up to underlyin
systems.

A <dfn>lock type</dfn> is a [=string=] that may exclusively be "`open`",
"`exclusive`", or "`shared`".
"`exclusive`", "`writable-siloed`", "`sync-access-handle-read-only`",
"`sync-access-handle-read-write-unsafe`".

A <dfn export id=file>file entry</dfn> additionally consists of
<dfn for="file entry" export>binary data</dfn> (a [=byte sequence=]), a
Expand Down Expand Up @@ -170,8 +171,7 @@ Note: These steps have to be run on the [=file system queue=].

</div>

Note: Locks help prevent concurrent modifications to a file. A {{FileSystemWritableFileStream}}
requires a shared lock, while a {{FileSystemSyncAccessHandle}} requires an exclusive one.
Note: Locks help prevent concurrent modifications to a file that are incompatible.

A <dfn export id=directory>directory entry</dfn> additionally consists of a [=/set=] of
<dfn for="directory entry">children</dfn>, which are themselves [=/file system entries=].
Expand Down Expand Up @@ -417,16 +417,32 @@ The <dfn method for=FileSystemHandle>isSameEntry(|other|)</dfn> method steps are
## The {{FileSystemFileHandle}} interface ## {#api-filesystemfilehandle}

<xmp class=idl>
enum FileSystemWritableFileStreamMode {
"exclusive",
"siloed",
};

dictionary FileSystemCreateWritableOptions {
boolean keepExistingData = false;
FileSystemWritableFileStreamMode mode = "siloed";
};

enum FileSystemSyncAccessHandleMode {
"readwrite",
"read-only",
"readwrite-unsafe",
};

dictionary FileSystemCreateSyncAccessHandleOptions {
FileSystemSyncAccessHandleMode mode = "readwrite";
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemFileHandle : FileSystemHandle {
Promise<File> getFile();
Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWritableOptions options = {});
[Exposed=DedicatedWorker]
Promise<FileSystemSyncAccessHandle> createSyncAccessHandle();
Promise<FileSystemSyncAccessHandle> createSyncAccessHandle(optional FileSystemCreateSyncAccessHandleOptions options = {});
};
</xmp>

Expand Down Expand Up @@ -535,10 +551,12 @@ The <dfn method for=FileSystemFileHandle>getFile()</dfn> method steps are:
the temporary file starts out empty,
otherwise the existing file is first copied to this temporary file.

Creating a {{FileSystemWritableFileStream}} [=file entry/take a lock|takes a shared lock=] on the
[=file entry=] [=locate an entry|locatable=] with |fileHandle|'s [=FileSystemHandle/locator=].
This prevents the creation of {{FileSystemSyncAccessHandle|FileSystemSyncAccessHandles}}
for the entry, until the stream is closed.
Creating a {{FileSystemWritableFileStream}} [=file entry/take a lock|takes a lock=] on the
[=file entry=] [=locate an entry|locatable=] with |fileHandle|'s [=FileSystemHandle/locator=]
and a |lockType| determined by {{FileSystemCreateWritableOptions/mode}}. Until the stream is
closed, both {{FileSystemCreateWritableOptions/mode|modes}} prevent any file operation or the
creation of a file primitive on the [=file entry=], but "`siloed`" will allow the creation of other
{{FileSystemWritableFileStream}} in "`siloed`" {{FileSystemCreateWritableOptions/mode}}.
</div>

<p class=XXX>See <a href=https://github.com/WICG/file-system-access/issues/67>WICG/file-system-access issue #67</a>
Expand Down Expand Up @@ -572,8 +590,15 @@ The <dfn method for=FileSystemFileHandle>createWritable(|options|)</dfn> method
|result| with a "{{NotFoundError}}" {{DOMException}} and abort these steps.
1. [=Assert=]: |entry| is a [=file entry=].

1. Let |lockType| be the empty string.
1. Let |mode| be |options|["{{FileSystemCreateWritableOptions/mode}}"].
1. If |mode| is "`exclusive`":
1. Set |lockType| to "`exclusive`".
1. Otherwise:
1. [=Assert=]: |mode| is "`siloed`".
1. Set |lockType| to "`writable-siloed`".
1. Let |lockResult| be the result of [=file entry/take a lock|taking a lock=]
with "`shared`" on |entry|.
with |lockType| on |entry|.

1. [=Queue a storage task=] with |global| to run these steps:
1. If |lockResult| is "`failure`", [=/reject=] |result| with a
Expand All @@ -600,11 +625,12 @@ The <dfn method for=FileSystemFileHandle>createWritable(|options|)</dfn> method
[=file entry=] [=locate an entry|locatable=] by |fileHandle|'s [=FileSystemHandle/locator=].
To ensure the changes are reflected in this file, the handle can be flushed.

Creating a {{FileSystemSyncAccessHandle}} [=file entry/take a lock|takes an exclusive lock=] on the
[=file entry=] [=locate an entry|locatable=] with |fileHandle|'s [=FileSystemHandle/locator=].
This prevents the creation of further {{FileSystemSyncAccessHandle|FileSystemSyncAccessHandles}}
or {{FileSystemWritableFileStream|FileSystemWritableFileStreams}}
for the entry, until the access handle is closed.
Creating a {{FileSystemSyncAccessHandle}} [=file entry/take a lock|takes a lock=] on the
[=file entry=] [=locate an entry|locatable=] with |fileHandle|'s [=FileSystemHandle/locator=]
and a |lockType| determined by {{FileSystemCreateSyncAccessHandleOptions/mode}}. Until the access handle is
closed, all {{FileSystemCreateSyncAccessHandleOptions/mode|modes}} prevent any file operation or the
creation of a file primitive on the [=file entry=], but "`read-only`" and "`readwrite-unsafe`" will allow the creation of other
{{FileSystemSyncAccessHandle}} in their respective {{FileSystemCreateSyncAccessHandleOptions/mode|modes}}.

The returned {{FileSystemSyncAccessHandle}} offers synchronous methods. This allows for higher performance
on contexts where asynchronous operations come with high overhead, e.g., WebAssembly.
Expand All @@ -614,7 +640,7 @@ The <dfn method for=FileSystemFileHandle>createWritable(|options|)</dfn> method
</div>

<div algorithm>
The <dfn method for=FileSystemFileHandle>createSyncAccessHandle()</dfn> method steps are:
The <dfn method for=FileSystemFileHandle>createSyncAccessHandle(|options|)</dfn> method steps are:

1. Let |result| be [=a new promise=].
1. Let |locator| be [=this=]'s [=FileSystemHandle/locator=].
Expand Down Expand Up @@ -642,15 +668,28 @@ The <dfn method for=FileSystemFileHandle>createSyncAccessHandle()</dfn> method s
|result| with a "{{NotFoundError}}" {{DOMException}} and abort these steps.
1. [=Assert=]: |entry| is a [=file entry=].

1. Let |lockType| be the empty string.
1. Let |writeAccess| be the empty string.
1. Let |mode| be |options|["{{FileSystemCreateSyncAccessHandleOptions/mode}}"].
1. If |mode| is "`readwrite`":
1. Set |lockType| to "`exclusive`".
1. Set |writeAccess| to "`writable`".
1. Otherwise, if |mode| is "`read-only`":
1. Set |lockType| to "`sync-access-handle-read-only`".
1. Set |writeAccess| to "`not-writable`".
1. Otherwise:
1. [=Assert=]: |mode| is "`readwrite-unsafe`".
1. Set |lockType| to "`sync-access-handle-read-write-unsafe`".
1. Set |writeAccess| to "`writable`".
1. Let |lockResult| be the result of [=file entry/take a lock|taking a lock=]
with "`exclusive`" on |entry|.
with |lockType| on |entry|.

1. [=Queue a storage task=] with |global| to run these steps:
1. If |lockResult| is "`failure`", [=/reject=] |result| with a
"{{NoModificationAllowedError}}" {{DOMException}} and abort these steps.

1. Let |handle| be the result of <a>creating a new `FileSystemSyncAccessHandle`</a>
for |entry| in |realm|.
with |entry| and |writeAccess| in |realm|.
1. [=/Resolve=] |result| with |handle|.

1. Return |result|.
Expand Down Expand Up @@ -1437,6 +1476,9 @@ A {{FileSystemSyncAccessHandle}} has an associated <dfn for=FileSystemSyncAccess
A {{FileSystemSyncAccessHandle}} has an associated <dfn for=FileSystemSyncAccessHandle>\[[state]]</dfn>,
a string that may exclusively be "`open`" or "`closed`".

A {{FileSystemSyncAccessHandle}} has an associated <dfn for=FileSystemSyncAccessHandle>\[[writeAccess]]</dfn>,
a [=string=] that may exclusively be "`writable`" or "`not-writable`".

A {{FileSystemSyncAccessHandle}} is an object that is capable of reading from/writing to,
as well as obtaining and changing the size of, a single file.

Expand All @@ -1448,11 +1490,13 @@ A {{FileSystemSyncAccessHandle}} has a <dfn for="FileSystemSyncAccessHandle">fil
<div algorithm>
To
<dfn local-lt="creating a new FileSystemSyncAccessHandle">create a new `FileSystemSyncAccessHandle`</dfn>
given a [=file entry=] |file| in a [=/Realm=] |realm|:
given a [=file entry=] |file| and a [=string=] |writeAccess| in a [=/Realm=] |realm|:

1. [=Assert=]: |writeAccess| is "`writable`" or "`not-writable`".
1. Let |handle| be a [=new=] {{FileSystemSyncAccessHandle}} in |realm|.
1. Set |handle|'s [=FileSystemSyncAccessHandle/[[file]]=] to |file|.
1. Set |handle|'s [=FileSystemSyncAccessHandle/[[state]]=] to "`open`".
1. Set |handle|'s [=FileSystemSyncAccessHandle/[[writeAccess]]=] to |writeAccess|.
1. Return |handle|.

</div>
Expand Down Expand Up @@ -1515,6 +1559,8 @@ The <dfn method for=FileSystemSyncAccessHandle>write(|buffer|, {{FileSystemReadW

1. If [=this=]'s [=[[state]]=] is "`closed`",
[=throw=] an "{{InvalidStateError}}" {{DOMException}}.
1. If [=this=]'s [=[[writeAccess]]=]' is "`not-writable`",
[=throw=] a "{{NoModificationAllowedError}}" {{DOMException}}.
1. Let |writePosition| be |options|["{{FileSystemReadWriteOptions/at}}"] if
|options|["{{FileSystemReadWriteOptions/at}}"] [=map/exists=]; otherwise
[=this=]'s [=FileSystemSyncAccessHandle/file position cursor=].
Expand Down Expand Up @@ -1575,6 +1621,8 @@ The <dfn method for=FileSystemSyncAccessHandle>truncate(|newSize|)</dfn> method

1. If [=this=]'s [=[[state]]=] is "`closed`",
[=throw=] an "{{InvalidStateError}}" {{DOMException}}.
1. If [=this=]'s [=[[writeAccess]]=]' is "`not-writable`",
[=throw=] a "{{NoModificationAllowedError}}" {{DOMException}}.
1. Let |fileContents| be a copy of [=this=]'s
[=FileSystemSyncAccessHandle/[[file]]=]'s [=file entry/binary data=].
1. Let |oldSize| be the [=byte sequence/length=] of [=this=]'s
Expand Down Expand Up @@ -1631,6 +1679,8 @@ The <dfn method for=FileSystemSyncAccessHandle>flush()</dfn> method steps are:

1. If [=this=]'s [=[[state]]=] is "`closed`",
[=throw=] an "{{InvalidStateError}}" {{DOMException}}.
1. If [=this=]'s [=[[writeAccess]]=]' is "`not-writable`",
[=throw=] a "{{NoModificationAllowedError}}" {{DOMException}}.
1. Attempt to transfer all cached modifications of the file's content to the
file system's underlying storage device.

Expand Down

0 comments on commit 564df68

Please sign in to comment.