diff --git a/index.bs b/index.bs index 881fb19..8e81aec 100644 --- a/index.bs +++ b/index.bs @@ -124,50 +124,97 @@ Issue: We should consider having further normative restrictions on file names th never be allowed using this API, rather than leaving it entirely up to underlying file systems. +A lock type is a [=string=] that may exclusively be "`open`", +"`exclusive`", or "`shared`". + +A file lock is a [=struct=] with the following [=struct/items=]: + +: type +:: A [=lock type=]. +: globals +:: A [=/list=] of [=realm/global objects=] representing the current holders of the lock. + A file entry additionally consists of binary data (a [=byte sequence=]), a modification timestamp (a number representing the number of milliseconds since the Unix Epoch), -a lock (a string that may exclusively be "`open`", "`taken-exclusive`" or "`taken-shared`") -and a shared lock count (a number representing the number shared locks that are taken at a given point in time). +a lock (a [=file lock=]), a pending lock (a [=file lock=]), +and pending steps (a [=/list=] of sets of steps). A user agent has an associated file system queue which is the result of [=starting a new parallel queue=]. This queue is to be used for all file system operations.
-To take a [=file entry/lock=] with a |value| of -"`exclusive`" or "`shared`" on a given [=file entry=] |file|: - -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. Return "`failure`". +To take a lock on a given [=file entry=] |file| +with a |lockType| (a [=lock type=]), a set of steps |resultSteps|, a [=realm/global object=] |global|, +and a |lockResult| (a [=string=]): + +1. [=Assert=]: |lockType| is not "`open`". +1. Let |pendingLock| be the |file|'s [=file entry/pending lock=]. +1. Let |pendingSteps| be the |file|'s [=file entry/pending steps=]. +1. Let |activeLock| be the |file|'s [=file entry/lock=]. +1. If |pendingLock|'s [=file lock/type=] is not "`open`": + 1. Set |activeLock| to |pendingLock|. +1. Let |activeLockType| be the |activeLock|'s [=file lock/type=]. +1. If |activeLockType| is not "`open`": + 1. If |activeLockType| is not equal to |lockType| or is "`exclusive`": + 1. Let |evictedStatus| be the result of [=file lock/evicting all globals=] on |activeLock|. + 1. If |evictedStatus| is "`failure`": + 1. Set |lockResult| to `failure`. + 1. Run |resultSteps|. + 1. Return. + 1. [=list/Empty=] |pendingLock|'s [=file lock/globals=]. + 1. [=list/Empty=] |pendingSteps|. + 1. Set |activeLock| to |pendingLock|. +1. Set |lockResult| to `success`. +1. Set the |activeLock|'s [=file lock/type=] to |lockType|. +1. [=list/Append=] |global| to |activeLock|'s [=file lock/globals=]. +1. If |activeLock| is the |pendingLock|: + 1. [=list/Append=] |resultSteps| to |pendingSteps|. +1. Otherwise: + 1. Run |resultSteps|. Note: These steps have to be run on the [=file system queue=].
+
+To evict all globals for a [=file entry/lock=] |lock|. + +1. [=set/For each=] |global| of |lock|'s [=file lock/globals=]: + 1. If |global| is a {{Window}} object whose [=associated Document=] is [=Document/fully active=]: + 1. Return "`failure`". + 1. If |global| is a {{WorkerGlobalScope}} object whose [=WorkerGlobalScope/closing=] flag is false and whose {{worker}} is not a [[html/workers#the-worker's-lifetime|suspendable worker]]: + 1. Return "`failure`". +1. [=set/For each=] |global| of |lock|'s [=file lock/globals=]: + 1. The user agent must initiate [[html/document-lifecycle#destroying-documents|destroying]] the {{Window}}'s [=associated Document=]. +1. Return "`success`". + +
+
To release a [=file entry/lock=] on a given -[=file entry=] |file|: +[=/file entry=] |file| with a [=realm/global object=] |global|: -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. Let |lock| be the |file|'s [=file entry/lock=]. +1. Let |lockGlobals| be the |lock|'s [=file lock/globals=]. +1. [=Assert=]: |lock|'s [=file lock/type=] is not "`open`". +1. [=Assert=]: |lockGlobals|'s [=list/contains=] |global|. +1. [=list/Remove=] the first [=list/item=] in |lockGlobals| equal to |global|. +1. If |lockGlobals| [=list/is empty=]: + 1. Let |pendingLock| be the |file|'s [=file entry/pending lock=]. + 1. Let |pendingLockGlobals| be the |pendingLock|'s [=file lock/globals=]. + 1. If |pendingLock|'s [=file lock/type=] is "`open`": + 1. [=Assert=]: |pendingLockGlobals| [=list/is empty=]. + 1. Set |lock|'s [=file lock/type=] to "`open`". + 1. Return. + 1. [=Assert=]: |pendingLockGlobals|'s [=list/size=] is greater than 0. + 1. Set |file|'s [=file entry/lock=] to |pendingLock|. + 1. Set |pendingLock|'s [=file lock/type=] to "`open`". + 1. [=list/Empty=] |pendingLock|'s [=file lock/globals=]. + 1. [=set/For each=] |resultSteps| of |file|'s [=file entry/pending steps=]: + 1. Run |resultSteps|. + 1. [=list/Empty=] |file|'s [=file entry/pending steps=]. Note: These steps have to be run on the [=file system queue=]. @@ -538,7 +585,7 @@ The getFile() 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/lock/take|takes a shared lock=] on the + 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. @@ -575,20 +622,20 @@ The createWritable(|options|) method |result| with a "{{NotFoundError}}" {{DOMException}} and abort these steps. 1. [=Assert=]: |entry| is a [=file entry=]. - 1. Let |lockResult| be the result of [=file entry/lock/take|taking a lock=] - with "`shared`" 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 |stream| be the result of creating a new `FileSystemWritableFileStream` - for |entry| in |realm|. - 1. If |options|["{{FileSystemCreateWritableOptions/keepExistingData}}"] - is true: - 1. Set |stream|'s [=[[buffer]]=] to a copy of |entry|'s - [=file entry/binary data=]. - 1. [=/Resolve=] |result| with |stream|. + 1. Let |lockResult| be the empty string. + 1. [=file entry/Take a lock=] on |entry| with lock type "`shared`", global |global|, + lock result |lockResult|, and the following result steps: + 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 |stream| be the result of creating a new `FileSystemWritableFileStream` + for |entry| in |realm| and global |global|. + 1. If |options|["{{FileSystemCreateWritableOptions/keepExistingData}}"] + is true: + 1. Set |stream|'s [=[[buffer]]=] to a copy of |entry|'s + [=file entry/binary data=]. + 1. [=/Resolve=] |result| with |stream|. 1. Return |result|. @@ -603,7 +650,7 @@ The createWritable(|options|) 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/lock/take|takes an exclusive lock=] on the + 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}} @@ -645,16 +692,16 @@ The createSyncAccessHandle() method s |result| with a "{{NotFoundError}}" {{DOMException}} and abort these steps. 1. [=Assert=]: |entry| is a [=file entry=]. - 1. Let |lockResult| be the result of [=file entry/lock/take|taking a lock=] - with "`exclusive`" 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 |lockResult| be the empty string. + 1. [=file entry/Take a lock=] on |entry| with lock type "`exclusive`", global |global|, + lock result |lockResult|, and the following result steps: + 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 creating a new `FileSystemSyncAccessHandle` - for |entry| in |realm|. - 1. [=/Resolve=] |result| with |handle|. + 1. Let |handle| be the result of creating a new `FileSystemSyncAccessHandle` + for |entry| in |realm|. + 1. [=/Resolve=] |result| with |handle|. 1. Return |result|. @@ -1151,7 +1198,7 @@ Similarly, when piping a {{ReadableStream}} into a {{FileSystemWritableFileStrea
To create a new `FileSystemWritableFileStream` -given a [=file entry=] |file| in a [=/Realm=] |realm|: +given a [=file entry=] |file| in a [=/Realm=] |realm| with [=realm/global object=] |global|: 1. Let |stream| be a [=new=] {{FileSystemWritableFileStream}} in |realm|. 1. Set |stream|'s [=FileSystemWritableFileStream/[[file]]=] to |file|. @@ -1182,7 +1229,7 @@ given a [=file entry=] |file| in a [=/Realm=] |realm|: 1. [=Enqueue the following steps=] to the [=file system queue=]: 1. [=file entry/lock/release|Release the lock=] on - |stream|'s [=FileSystemWritableFileStream/[[file]]=]. + |stream|'s [=FileSystemWritableFileStream/[[file]]=] with global |global|. 1. [=Queue a storage task=] with |file|'s [=relevant global object=] to [=/resolve=] |closeResult| with `undefined`. @@ -1190,7 +1237,7 @@ given a [=file entry=] |file| in a [=/Realm=] |realm|: 1. Let |abortAlgorithm| be these steps: 1. [=enqueue steps|Enqueue this step=] to the [=file system queue=]: 1. [=file entry/lock/release|Release the lock=] on - |stream|'s [=FileSystemWritableFileStream/[[file]]=]. + |stream|'s [=FileSystemWritableFileStream/[[file]]=] with global |global|. 1. Let |highWaterMark| be 1. 1. Let |sizeAlgorithm| be an algorithm that returns `1`. 1. [=WritableStream/Set up=] |stream| with close() method steps are: 1. Set |lockReleased| to false. 1. Let |file| be [=this=]'s [=FileSystemSyncAccessHandle/[[file]]=]. 1. [=Enqueue the following steps=] to the [=file system queue=]: - 1. [=file entry/lock/release|Release the lock=] on |file|. + 1. [=file entry/lock/release|Release the lock=] on |file| with [=this=]'s [=relevant global object=]. 1. Set |lockReleased| to true. 1. [=Pause=] until |lockReleased| is true.