-
Notifications
You must be signed in to change notification settings - Fork 19
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
Define a parallel queue for all file system operations #95
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks better, but not quite there yet. I'll be out next week, but happy to look at this again in February.
index.bs
Outdated
1. Increase |count| by one. | ||
1. [=Queue a task=] to |replyTaskSource| to run |onLockTakenAlgorithm| | ||
and return. | ||
1. [=Reject=] |p| with a "{{NoModificationAllowedError}}" {{DOMException}}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have to queue a task for this as well. But maybe it makes more sense that you don't pass the promise in to this algorithm and you have a single callback algorithm that you then pass success/failure to or some such?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense! The original algorithm returned true or false, but it now runs the passed-in algorithm with "success
" and "failure
" explicitly, which seems more clear. Let me know if there's a better way to do this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mainly lots of nits, but this looks to be heading in the right direction to me!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With some more task queueing I think this can land.
Updated to post all file system operations to a "file system queue", as we discussed offline. Lock take/release is once again sync (and must be called from that queue). Please feel free to nit-pick this as much as you please, since this pattern will be applied to all the other methods. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The overall setup here seems sound from a quick skim. The main problem is that "reject", "resolve", and "create a new X" are operations that need to happen on the main thread. And as such you need to do the "queue a storage task" dance for them.
Note that you don't necessarily need to replace each individually. You might be able to restructure things a bit so do all the in parallel work first and safe the results in variables and then queue a storage task that does the rejecting/resolving as per the variables.
Also, what kind of exception can "request access" throw, out of curiosity? Since we get that exception while in parallel, forwarding it probably doesn't entirely work. If we could normalize that to it not returning "granted" that would be better.
Oh I see... Question - does this work? If not, why not?
Unfortunately I think this is needed, since it can reject with a |
Because you still end up in parallel when doing "resolve", etc. You need some things to happen "in parallel" and then after those things you queue a task for things to happen on the main thread. Such as resolving a promise or creating a JS object.
Hmm, I guess we should redesign this somewhat whereby "request access" returns some kind of internal value and we then later turn that into the appropriate exception. |
ea97146
to
aff3e07
Compare
Ah yes, looking at this with fresh eyes I see the problem. I've updated a few methods - if that pattern looks good, I'll copy it everywhere else
This is a bit tricky because the WICG spec considers file system access a "powerful feature", and powerful features have permission request algorithms that are allowed to throw. I've added an explicit warning that this spec doesn't comply with that pattern, which seems like it should be good enough? Anyways, I've updated the access checks to return either a PermissionState or an error code that can be turned into a DomException (though I wonder if returning (as opposed to throwing) a DomException directly is an allowed pattern?) If this all seems reasonable, I'll file an issue to update the WICG spec (and update all the instances in this spec which assume these algorithms can throw) |
index.bs
Outdated
<p class=warning> Dependent specifications may consider this API a | ||
[=powerful feature=]. However, unlike other [=powerful features=] whose | ||
[=permission request algorithm=] may throw, [=/file system entry=]'s | ||
[=file system entry/query access=] and [=file system entry/request access=] | ||
algorithms must run [=in parallel=] on the [=file system queue=] and are | ||
therefore not allowed to throw. Instead, the caller is expected to [=/reject=] | ||
as appropriate should these algorithms return an [=exception/error name=]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This really should go in WICG spec, but since we're doing something a bit funky it might be useful to have here, too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they run in parallel the caller will also be in parallel. They wouldn't be able to immediately reject something.
But also I'm not a big fan of this design. Perhaps these should return a struct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they run in parallel the caller will also be in parallel. They wouldn't be able to immediately reject something.
Updated to say the caller is expected to [=queue a storage task=] to [=/reject=], as appropriate
... though while going through the whole spec I realized that there is one situation - the asynchronous iterator initialization steps - where there is no promise to reject. See the other comment
But also I'm not a big fan of this design. Perhaps these should return a struct?
Done. AFAICT a struct item cannot be "optional" (only algorithm parameters?) so "error name" is just empty most of the time
friendly ping @annevk. This infra PR is holding up the PRs specifying |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your patience. This is looking rather good. Except the access checks, those could do with some more design work.
index.bs
Outdated
<p class=warning> Dependent specifications may consider this API a | ||
[=powerful feature=]. However, unlike other [=powerful features=] whose | ||
[=permission request algorithm=] may throw, [=/file system entry=]'s | ||
[=file system entry/query access=] and [=file system entry/request access=] | ||
algorithms must run [=in parallel=] on the [=file system queue=] and are | ||
therefore not allowed to throw. Instead, the caller is expected to [=/reject=] | ||
as appropriate should these algorithms return an [=exception/error name=]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they run in parallel the caller will also be in parallel. They wouldn't be able to immediately reject something.
But also I'm not a big fan of this design. Perhaps these should return a struct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As promised, I've updated the whole spec to follow the pattern. Note that much of the diff is just formatting (e.g. adding an indentation)
I've manually double-checked that all promise rejections and resolutions now happen from the "storage task source" and all access checks are done in-parallel from the file system queue (though it's still possible I've missed something...)
index.bs
Outdated
<p class=warning> Dependent specifications may consider this API a | ||
[=powerful feature=]. However, unlike other [=powerful features=] whose | ||
[=permission request algorithm=] may throw, [=/file system entry=]'s | ||
[=file system entry/query access=] and [=file system entry/request access=] | ||
algorithms must run [=in parallel=] on the [=file system queue=] and are | ||
therefore not allowed to throw. Instead, the caller is expected to [=/reject=] | ||
as appropriate should these algorithms return an [=exception/error name=]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they run in parallel the caller will also be in parallel. They wouldn't be able to immediately reject something.
Updated to say the caller is expected to [=queue a storage task=] to [=/reject=], as appropriate
... though while going through the whole spec I realized that there is one situation - the asynchronous iterator initialization steps - where there is no promise to reject. See the other comment
But also I'm not a big fan of this design. Perhaps these should return a struct?
Done. AFAICT a struct item cannot be "optional" (only algorithm parameters?) so "error name" is just empty most of the time
index.bs
Outdated
1. Set |accessErrorName| to |accessResult|'s | ||
[=file system access result/error name=] if it is not | ||
« the empty string »; otherwise "{{NotAllowedError}}". | ||
1. [=Throw=] an |accessErrorName| {{DOMException}}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've specified that query access
requires going in parallel on the file system queue
, but if we get an exception there's no associated promise to reject...
... @mkruisselbrink do you know if this is this even necessary? Or is it enough to check query access
in just the "get the next iteration result" algorithm below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't seem to do anything based on these steps. Per https://webidl.spec.whatwg.org/#idl-async-iterable we can omit it I think.
cc @domenic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tentatively omitted it for now, pending input from others
index.bs
Outdated
1. [=/Reject=] |promise| with an |accessErrorName| {{DOMException}} | ||
and return |promise|. | ||
|
||
1. If |directory| is `null`, [=/reject=] |result| with a | ||
"{{NotFoundError}}" {{DOMException}} and abort these steps. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this is the only "behavioral" change in this PR. Presumably the "NotAllowedError" should take precedence over the "NotAllowedError", just like everywhere else
@@ -135,7 +176,7 @@ A <dfn export id=directory>directory entry</dfn> additionally consists of a [=/s | |||
<dfn for="directory entry">children</dfn>, which are themselves [=/file system entries=]. | |||
Each member is either a [=/file entry=] or a [=/directory entry=]. | |||
|
|||
A [=/file system entry=] |entry| should be [=list/contained=] in the [=children=] of at most one | |||
A [=/file system entry=] |entry| should be [=list/contained=] in the [=directory entry/children=] of at most one |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: this fixes a spec build failure
index.bs
Outdated
:: A [=string=] which must be either « the empty string » or an | ||
[=exception/error name=] listed in the [=error names table=]. | ||
If [=file system access result/permission state=] is | ||
"{{PermissionState/granted}}" this must be « the empty string » |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make it so that when permission is denied it has to be an error name so we don't have to do defaulting below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. This simplifies things quite a lot
Side note: is "iff" okay to use in specs? Happy to spell it out if not
index.bs
Outdated
1. Set |accessErrorName| to |accessResult|'s | ||
[=file system access result/error name=] if it is not | ||
« the empty string »; otherwise "{{NotAllowedError}}". | ||
1. [=Throw=] an |accessErrorName| {{DOMException}}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't seem to do anything based on these steps. Per https://webidl.spec.whatwg.org/#idl-async-iterable we can omit it I think.
cc @domenic
@annevk friendly ping (this is holding up a number of other changes). If this looks good, feel free to merge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found a couple remaining nits, generally looks good. Feel free to merge after addressing them. Though I should have time tomorrow if you'd rather I do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant to approve this to make that possible. Doh.
Thanks for the review! I'll merge this now so we can start making progress on the various PRs that are blocked on it. If you have more nits, I'm happy to address them in a follow-up Just an FYI that I also updated the permission checks to use the new |
Fixes #74 by specifying a parallel queue for all file system operations, not just locking (see https://github.com/whatwg/fs/pull/9/files#r1109679577)
Preview | Diff