Skip to content

Commit e9ba03b

Browse files
author
Nathan Memmott
committed
Add new SAH and WFS locking modes
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.
1 parent b03688d commit e9ba03b

File tree

1 file changed

+88
-39
lines changed

1 file changed

+88
-39
lines changed

index.bs

Lines changed: 88 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -124,34 +124,35 @@ Issue: We should consider having further normative restrictions on file names th
124124
never be allowed using this API, rather than leaving it entirely up to underlying file
125125
systems.
126126

127+
A <dfn>lock type</dfn> is a [=string=] that may exclusively be "`open`",
128+
"`exclusive`", "`writable-siloed`", "`sync-access-handle-read-only`",
129+
"`sync-access-handle-read-write-unsafe`".
130+
127131
A <dfn export id=file>file entry</dfn> additionally consists of
128132
<dfn for="file entry" export>binary data</dfn> (a [=byte sequence=]), a
129133
<dfn for="file entry">modification timestamp</dfn> (a number representing the number of milliseconds since the <a spec=FileAPI>Unix Epoch</a>),
130-
a <dfn for="file entry">lock</dfn> (a string that may exclusively be "`open`", "`taken-exclusive`" or "`taken-shared`")
131-
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).
134+
a <dfn for="file entry">lock</dfn> (a [=lock type=]),
135+
and a <dfn for="file entry">lock count</dfn> (a number representing the number of locks that are taken at a given point in time).
132136

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

137141
<div algorithm>
138-
To <dfn for="file entry" id=file-entry-lock-take>take a lock</dfn> with a |value| of
139-
"`exclusive`" or "`shared`" on a given [=file entry=] |file|:
142+
To <dfn for="file entry" id=file-entry-lock-take>take a lock</dfn> with a |lockType| (a [=lock type=])
143+
on a given [=file entry=] |file|:
140144

145+
1. [=Assert=]: |lockType| is not "`open`".
141146
1. Let |lock| be the |file|'s [=file entry/lock=].
142-
1. Let |count| be the |file|'s [=file entry/shared lock count=].
143-
1. If |value| is "`exclusive`":
144-
1. If |lock| is "`open`":
145-
1. Set lock to "`taken-exclusive`".
146-
1. Return "`success`".
147-
1. If |value| is "`shared`":
148-
1. If |lock| is "`open`":
149-
1. Set |lock| to "`taken-shared`".
150-
1. Set |count| to 1.
151-
1. Return "`success`".
152-
1. Otherwise, if |lock| is "`taken-shared`":
153-
1. Increase |count| by 1.
154-
1. Return "`success`".
147+
1. Let |count| be the |file|'s [=file entry/lock count=].
148+
1. If |lock| is "`open`":
149+
1. Set |lock| to |lockType|.
150+
1. Set |count| to 1.
151+
1. Return "`success`".
152+
1. If |lock| is not "`exclusive`":
153+
1. If |lock| equals |lockType|:
154+
1. Increase |count| by 1.
155+
1. Return "`success`".
155156
1. Return "`failure`".
156157

157158
Note: These steps have to be run on the [=file system queue=].
@@ -163,18 +164,16 @@ To <dfn for="file entry/lock">release</dfn> a [=file entry/lock=] on a given
163164
[=file entry=] |file|:
164165

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

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

174174
</div>
175175

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

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

422421
<xmp class=idl>
422+
enum FileSystemWritableFileStreamMode {
423+
"exclusive",
424+
"siloed",
425+
};
426+
423427
dictionary FileSystemCreateWritableOptions {
424428
boolean keepExistingData = false;
429+
FileSystemWritableFileStreamMode mode = "siloed";
430+
};
431+
432+
enum FileSystemSyncAccessHandleMode {
433+
"readwrite",
434+
"read-only",
435+
"readwrite-unsafe",
436+
};
437+
438+
dictionary FileSystemCreateSyncAccessHandleOptions {
439+
FileSystemSyncAccessHandleMode mode = "readwrite";
425440
};
426441

427442
[Exposed=(Window,Worker), SecureContext, Serializable]
428443
interface FileSystemFileHandle : FileSystemHandle {
429444
Promise<File> getFile();
430445
Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWritableOptions options = {});
431446
[Exposed=DedicatedWorker]
432-
Promise<FileSystemSyncAccessHandle> createSyncAccessHandle();
447+
Promise<FileSystemSyncAccessHandle> createSyncAccessHandle(optional FileSystemCreateSyncAccessHandleOptions options = {});
433448
};
434449
</xmp>
435450

@@ -538,10 +553,12 @@ The <dfn method for=FileSystemFileHandle>getFile()</dfn> method steps are:
538553
the temporary file starts out empty,
539554
otherwise the existing file is first copied to this temporary file.
540555

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

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

595+
1. Let |lockType| be the empty string.
596+
1. Let |mode| be |options|["{{FileSystemCreateWritableOptions/mode}}"].
597+
1. If |mode| is "`exclusive`":
598+
1. Set |lockType| to "`exclusive`".
599+
1. Otherwise:
600+
1. [=Assert=]: |mode| is "`siloed`".
601+
1. Set |lockType| to "`writable-siloed`".
578602
1. Let |lockResult| be the result of [=file entry/take a lock|taking a lock=]
579-
with "`shared`" on |entry|.
603+
with |lockType| on |entry|.
580604

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

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

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

619644
<div algorithm>
620-
The <dfn method for=FileSystemFileHandle>createSyncAccessHandle()</dfn> method steps are:
645+
The <dfn method for=FileSystemFileHandle>createSyncAccessHandle(|options|)</dfn> method steps are:
621646

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

673+
1. Let |lockType| be the empty string.
674+
1. Let |writeAccess| be the empty string.
675+
1. Let |mode| be |options|["{{FileSystemCreateSyncAccessHandleOptions/mode}}"].
676+
1. If |mode| is "`readwrite`":
677+
1. Set |lockType| to "`exclusive`".
678+
1. Set |writeAccess| to "`writable`".
679+
1. Otherwise, if |mode| is "`read-only`":
680+
1. Set |lockType| to "`sync-access-handle-read-only`".
681+
1. Set |writeAccess| to "`not-writable`".
682+
1. Otherwise:
683+
1. [=Assert=]: |mode| is "`readwrite-unsafe`".
684+
1. Set |lockType| to "`sync-access-handle-read-write-unsafe`".
685+
1. Set |writeAccess| to "`writable`".
648686
1. Let |lockResult| be the result of [=file entry/take a lock|taking a lock=]
649-
with "`exclusive`" on |entry|.
687+
with |lockType| on |entry|.
650688

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

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

659697
1. Return |result|.
@@ -1440,6 +1478,9 @@ A {{FileSystemSyncAccessHandle}} has an associated <dfn for=FileSystemSyncAccess
14401478
A {{FileSystemSyncAccessHandle}} has an associated <dfn for=FileSystemSyncAccessHandle>\[[state]]</dfn>,
14411479
a string that may exclusively be "`open`" or "`closed`".
14421480

1481+
A {{FileSystemSyncAccessHandle}} has an associated <dfn for=FileSystemSyncAccessHandle>\[[writeAccess]]</dfn>,
1482+
a [=string=] that may exclusively be "`writable`" or "`not-writable`".
1483+
14431484
A {{FileSystemSyncAccessHandle}} is an object that is capable of reading from/writing to,
14441485
as well as obtaining and changing the size of, a single file.
14451486

@@ -1451,11 +1492,13 @@ A {{FileSystemSyncAccessHandle}} has a <dfn for="FileSystemSyncAccessHandle">fil
14511492
<div algorithm>
14521493
To
14531494
<dfn local-lt="creating a new FileSystemSyncAccessHandle">create a new `FileSystemSyncAccessHandle`</dfn>
1454-
given a [=file entry=] |file| in a [=/Realm=] |realm|:
1495+
given a [=file entry=] |file| and a [=string=] |writeAccess| in a [=/Realm=] |realm|:
14551496

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

14611504
</div>
@@ -1518,6 +1561,8 @@ The <dfn method for=FileSystemSyncAccessHandle>write(|buffer|, {{FileSystemReadW
15181561

15191562
1. If [=this=]'s [=[[state]]=] is "`closed`",
15201563
[=throw=] an "{{InvalidStateError}}" {{DOMException}}.
1564+
1. If [=this=]'s [=[[writeAccess]]=]' is "`not-writable`",
1565+
[=throw=] a "{{NoModificationAllowedError}}" {{DOMException}}.
15211566
1. Let |writePosition| be |options|["{{FileSystemReadWriteOptions/at}}"] if
15221567
|options|["{{FileSystemReadWriteOptions/at}}"] [=map/exists=]; otherwise
15231568
[=this=]'s [=FileSystemSyncAccessHandle/file position cursor=].
@@ -1578,6 +1623,8 @@ The <dfn method for=FileSystemSyncAccessHandle>truncate(|newSize|)</dfn> method
15781623

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

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

0 commit comments

Comments
 (0)