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 Nov 17, 2023
1 parent b03688d commit 0eb043a
Showing 1 changed file with 84 additions and 29 deletions.
113 changes: 84 additions & 29 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -127,31 +127,29 @@ systems.
A <dfn export id=file>file entry</dfn> additionally consists of
<dfn for="file entry" export>binary data</dfn> (a [=byte sequence=]), a
<dfn for="file entry">modification timestamp</dfn> (a number representing the number of milliseconds since the <a spec=FileAPI>Unix Epoch</a>),
a <dfn for="file entry">lock</dfn> (a string that may exclusively be "`open`", "`taken-exclusive`" or "`taken-shared`")
a <dfn for="file entry">lock</dfn> (a [=string=] that may exclusively be "`open`", "`exclusive`", "`writableSiloed`",
"`syncAccessHandleReadOnly`", "`syncAccessHandleReadWriteUnsafe`")
and a <dfn for="file entry">shared lock count</dfn> (a number representing the number of shared locks that are taken at a given point in time).

A user agent has an associated <dfn>file system queue</dfn> which is the
result of [=starting a new parallel queue=]. This queue is to be used for all
file system operations.

<div algorithm>
To <dfn for="file entry" id=file-entry-lock-take>take a lock</dfn> with a |value| of
"`exclusive`" or "`shared`" on a given [=file entry=] |file|:
To <dfn for="file entry" id=file-entry-lock-take>take a lock</dfn> with a |lockType| (a [=string=])
on a given [=file entry=] |file|:

1. [=Assert=]: |lockType| is "`exclusive`", "`writableSiloed`", "`syncAccessHandleReadOnly`", or "`syncAccessHandleReadWriteUnsafe`".
1. Let |lock| be the |file|'s [=file entry/lock=].
1. Let |count| be the |file|'s [=file entry/shared lock count=].
1. If |value| is "`exclusive`":
1. If |lock| is "`open`":
1. Set lock to "`taken-exclusive`".
1. Return "`success`".
1. If |value| is "`shared`":
1. If |lock| is "`open`":
1. Set |lock| to "`taken-shared`".
1. Set |count| to 1.
1. Return "`success`".
1. Otherwise, if |lock| is "`taken-shared`":
1. Increase |count| by 1.
1. Return "`success`".
1. If |lock| is null:
1. Set |lock| to |lockType|.
1. Set |count| to 1.
1. Return "`success`".
1. If |lock| is not "`exclusive`":
1. If |lock| equals |lockType|:
1. Increase |count| by 1.
1. Return "`success`".
1. Return "`failure`".

Note: These steps have to be run on the [=file system queue=].
Expand All @@ -164,10 +162,9 @@ To <dfn for="file entry/lock">release</dfn> a [=file entry/lock=] on a given

1. Let |lock| be the |file|'s associated [=file entry/lock=].
1. Let |count| be the |file|'s [=file entry/shared lock count=].
1. If |lock| is "`taken-shared`":
1. Decrease |count| by 1.
1. If |count| is 0, set |lock| to "`open`".
1. Otherwise, set |lock| to "`open`".
1. [=Assert=]: |count| is greater than 0.
1. Decrease |count| by 1.
1. If |count| is 0, set |lock| to null.

Note: These steps have to be run on the [=file system queue=].

Expand Down Expand Up @@ -420,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 @@ -538,10 +551,13 @@ 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 the creation on the entry
of a {{FileSystemWritableFileStream}} in a different {{FileSystemCreateWritableOptions/mode}}
or a {{FileSystemSyncAccessHandle}}, but "`exclusive`" will also prevent the creation of
{{FileSystemWritableFileStream}} in "`exclusive`" {{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 @@ -575,8 +591,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 "`writableSiloed`".
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 Down Expand Up @@ -609,6 +632,14 @@ The <dfn method for=FileSystemFileHandle>createWritable(|options|)</dfn> method
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 the creation on the entry
of a {{FileSystemSyncAccessHandle}} in a different {{FileSystemCreateSyncAccessHandleOptions/mode}}
or a {{FileSystemWritableFileStream}}, but "`read-write`" will also prevent the creation of
{{FileSystemSyncAccessHandle}} in "`read-write`" {{FileSystemCreateSyncAccessHandleOptions/mode}}.

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 @@ -617,7 +648,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 @@ -645,15 +676,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 "`syncAccessHandleReadOnly`".
1. Set |writeAccess| to "`not-writable`".
1. Otherwise:
1. [=Assert=]: |mode| is "`readwrite-unsafe`".
1. Set |lockType| to "`syncAccessHandleReadWriteUnsafe`".
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 @@ -1440,6 +1484,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 @@ -1451,11 +1498,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 "`notWritable`".
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 @@ -1518,6 +1567,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 @@ -1578,6 +1629,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 @@ -1634,6 +1687,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 0eb043a

Please sign in to comment.