Bug 157702 - Web Inspector: Filtering huge data grids should yield occasionally so the UI remains responsive
Summary: Web Inspector: Filtering huge data grids should yield occasionally so the UI ...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Web Inspector (show other bugs)
Version: WebKit Nightly Build
Hardware: All All
: P2 Normal
Assignee: Matt Baker
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2016-05-13 19:32 PDT by Matt Baker
Modified: 2016-05-25 13:23 PDT (History)
11 users (show)

See Also:


Attachments
[Patch] Proposed Fix (11.37 KB, patch)
2016-05-13 20:13 PDT, Matt Baker
no flags Details | Formatted Diff | Diff
[Patch] Proposed Fix (21.42 KB, patch)
2016-05-14 15:52 PDT, Matt Baker
no flags Details | Formatted Diff | Diff
[Patch] Proposed Fix (22.50 KB, patch)
2016-05-14 16:05 PDT, Matt Baker
no flags Details | Formatted Diff | Diff
[Patch] Proposed Fix (22.33 KB, patch)
2016-05-15 14:29 PDT, Matt Baker
no flags Details | Formatted Diff | Diff
[Patch] Proposed Fix (21.89 KB, patch)
2016-05-16 14:18 PDT, Matt Baker
no flags Details | Formatted Diff | Diff
Proposed Fix v2 (25.30 KB, patch)
2016-05-16 16:31 PDT, BJ Burg
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews113 for mac-yosemite (1.59 MB, application/zip)
2016-05-16 17:21 PDT, Build Bot
no flags Details
Archive of layout-test-results from ews103 for mac-yosemite (1.41 MB, application/zip)
2016-05-16 17:36 PDT, Build Bot
no flags Details
For Landing (25.56 KB, patch)
2016-05-17 12:44 PDT, BJ Burg
buildbot: commit-queue-
Details | Formatted Diff | Diff
Archive of layout-test-results from ews102 for mac-yosemite (1000.98 KB, application/zip)
2016-05-17 13:06 PDT, Build Bot
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Matt Baker 2016-05-13 19:32:37 PDT
* SUMMARY
Filtering huge data grids should yield occasionally so the UI remains responsive. Ideally the Inspector would have a generic mechanism whereby a task that processes a large number of items (such as data grid nodes) could yield control back to the runloop after a specified amount of time, before resuming processing.
Comment 1 Radar WebKit Bug Importer 2016-05-13 19:32:57 PDT
<rdar://problem/26282898>
Comment 2 Matt Baker 2016-05-13 20:13:20 PDT
Created attachment 278914 [details]
[Patch] Proposed Fix
Comment 3 Joseph Pecoraro 2016-05-13 20:42:00 PDT
Comment on attachment 278914 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=278914&action=review

r=me, but we really should have tests that do basic coverage of the new class.

> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:26
> +WebInspector.BatchedTaskController = class BatchedTaskController extends WebInspector.Object

This could really use some tests!

  - tasks that don't end up yielding
  - tasks that do end up yielding
  - stopped by user and used again

> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:32
> +        console.assert(delegate && typeof delegate.batchedTaskControllerProcessItem === "function", "Missing delegate implementing 'batchedTaskControllerProcessItem'");

Nit: console.assert(timeLimitPerBatch > 0)

> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:50
> +        if (this._processing)
> +            return;

Nit: console.assert(!this._processing);

Otherwise seems we should just ignore the new tasks in this case so there might be a silent error somewhere if this is called multiple times.

And therefore probably a getter for processing would make sense.

> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:100
> +        while (!this._batchGenerator.next().done) {
> +            this._batchTimeoutIdentifier = setTimeout(() => { this._processBatch(); }, 0);
> +            return;
> +        }

Nit: "if" instead of "while" would be clearer.

> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:130
> +};

This class is really clean, nice!
Comment 4 Matt Baker 2016-05-13 21:50:15 PDT
Comment on attachment 278914 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=278914&action=review

>> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:26
>> +WebInspector.BatchedTaskController = class BatchedTaskController extends WebInspector.Object
> 
> This could really use some tests!
> 
>   - tasks that don't end up yielding
>   - tasks that do end up yielding
>   - stopped by user and used again

There are tests a plenty, I just ran webkit-patch from the wrong directory.
Comment 5 Timothy Hatcher 2016-05-14 07:50:07 PDT
Comment on attachment 278914 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=278914&action=review

Very cool! Nice use of generators.

> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:126
> +        let stoppedByUser = this._stoppedByUser;

stoppedByClient? The "user" might not be involved.

> Source/WebInspectorUI/UserInterface/Views/DataGrid.js:1749
> +        this._filterDidModifyNodeInCurrentBatch = false;

You should reset this before filtering starts too, to be on the safe side.
Comment 6 Matt Baker 2016-05-14 15:52:52 PDT
Created attachment 278949 [details]
[Patch] Proposed Fix
Comment 7 Matt Baker 2016-05-14 15:55:37 PDT
Comment on attachment 278914 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=278914&action=review

>> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:126
>> +        let stoppedByUser = this._stoppedByUser;
> 
> stoppedByClient? The "user" might not be involved.

processingCancelled?
Comment 8 Matt Baker 2016-05-14 16:05:03 PDT
Created attachment 278950 [details]
[Patch] Proposed Fix
Comment 9 Timothy Hatcher 2016-05-14 17:52:47 PDT
Comment on attachment 278950 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=278950&action=review

> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:91
> +        this._processingCancelled = true;

_processingStopped?
Comment 10 Matt Baker 2016-05-14 18:32:18 PDT
Comment on attachment 278950 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=278950&action=review

>> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:91
>> +        this._processingCancelled = true;
> 
> _processingStopped?

I think cancelled makes it clear that the operation was stopped before finishing.
Comment 11 Timothy Hatcher 2016-05-14 19:12:36 PDT
Comment on attachment 278950 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=278950&action=review

>>> Source/WebInspectorUI/UserInterface/Controllers/BatchedTaskController.js:91
>>> +        this._processingCancelled = true;
>> 
>> _processingStopped?
> 
> I think cancelled makes it clear that the operation was stopped before finishing.

Then maybe the method should match? cancelProcessing()
Comment 12 Matt Baker 2016-05-15 14:29:16 PDT
Created attachment 278980 [details]
[Patch] Proposed Fix
Comment 13 Matt Baker 2016-05-15 14:29:52 PDT
Went with startTask/cancelTask.
Comment 14 BJ Burg 2016-05-16 11:04:40 PDT
Comment on attachment 278980 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=278980&action=review

I have an idea for how to make this even cleaner, will post a patch to save you some time.

> Source/WebInspectorUI/UserInterface/Views/DataGrid.js:1734
> +    batchedTaskControllerProcessItem(node)

Delegate methods need to pass the delegating instance as the first argument.
Comment 15 Matt Baker 2016-05-16 14:18:55 PDT
Created attachment 279045 [details]
[Patch] Proposed Fix
Comment 16 Matt Baker 2016-05-16 14:20:34 PDT
I think this patch should be considered as-is. Adding support for multiple tasks would require a token to be returned from startTask, and passed to delegate methods. The same thing can be accomplished by instantiating multiple YieldableTask objects.
Comment 17 BJ Burg 2016-05-16 16:31:01 PDT
Created attachment 279065 [details]
Proposed Fix v2
Comment 18 BJ Burg 2016-05-16 16:35:30 PDT
Comment on attachment 279065 [details]
Proposed Fix v2

I went a slightly different direction here, because I think it will be easier to use:
- YieldableTasks don't require a controller, but still use the delegate approach
- They cannot be reused
- The items iterator is passed at construction time

Also addressed some style and correctness things:
- Don't use 'batch' anywhere unless referring to something coalesced, like events
- Check to see if cancelled before *and* after processing an item
- delegate methods pass the delegating object as first parameter
- try naming generator functions as createIteratorForXXX, use call instead of bind.
- Delay notifying delegate about finishing when task is cancelled

This gets us multiple tasks per object for free, more or less, in case we need to do that in the future. It also simplifies the lifetime of tasks so they cannot be accidentally leaked or reused.
Comment 19 BJ Burg 2016-05-16 16:36:51 PDT
(In reply to comment #16)
> I think this patch should be considered as-is. Adding support for multiple
> tasks would require a token to be returned from startTask, and passed to
> delegate methods. The same thing can be accomplished by instantiating
> multiple YieldableTask objects.

Right. The only thing I needed to change to get this was the name, really. Also, passing the delegating object in case there is more than one task. The rest is pretty much the same.
Comment 20 Timothy Hatcher 2016-05-16 17:00:25 PDT
Comment on attachment 279065 [details]
Proposed Fix v2

View in context: https://bugs.webkit.org/attachment.cgi?id=279065&action=review

> Source/WebInspectorUI/UserInterface/Base/YieldableTask.js:72
> +        function* createIteratorForProcessingItems()

I think "createIteratorFor" is confusing. I would call this "processingItemsIterator".

> Source/WebInspectorUI/UserInterface/Base/YieldableTask.js:93
> +                    this._willYield(returnedItems, elapsedTime);
> +                    yield;

Newline before?

> Source/WebInspectorUI/UserInterface/Base/YieldableTask.js:106
> +        this._pendingItemsIterator = createIteratorForProcessingItems.call(this);

I suppose this is why you used create. I am surprised call() works… maybe I need to read up on generators.

> Source/WebInspectorUI/UserInterface/Base/YieldableTask.js:137
> +    _willYield(processedItems, elapsedTime)

_didYield? It is confusing since this calls yieldableTaskDidYield.

> Source/WebInspectorUI/UserInterface/Views/DataGrid.js:1744
> +        function *createIteratorForNodesToBeFiltered()

I would call this nodesToBeFilteredIterator.

> Source/WebInspectorUI/UserInterface/Views/DataGrid.js:1760
> +        let options = {
> +            workInterval: 100,
> +        };

Inline it. Having it broken out makes me hate options arguments.
Comment 21 Build Bot 2016-05-16 17:21:30 PDT
Comment on attachment 279065 [details]
Proposed Fix v2

Attachment 279065 [details] did not pass mac-debug-ews (mac):
Output: http://webkit-queues.webkit.org/results/1333465

New failing tests:
inspector/unit-tests/yieldable-task.html
Comment 22 Build Bot 2016-05-16 17:21:33 PDT
Created attachment 279072 [details]
Archive of layout-test-results from ews113 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-debug-ews.
Bot: ews113  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 23 Build Bot 2016-05-16 17:36:36 PDT
Comment on attachment 279065 [details]
Proposed Fix v2

Attachment 279065 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.webkit.org/results/1333569

New failing tests:
inspector/unit-tests/yieldable-task.html
Comment 24 Build Bot 2016-05-16 17:36:39 PDT
Created attachment 279074 [details]
Archive of layout-test-results from ews103 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: ews103  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 25 Matt Baker 2016-05-16 19:47:52 PDT
(In reply to comment #19)
> (In reply to comment #16)
> > I think this patch should be considered as-is. Adding support for multiple
> > tasks would require a token to be returned from startTask, and passed to
> > delegate methods. The same thing can be accomplished by instantiating
> > multiple YieldableTask objects.
> 
> Right. The only thing I needed to change to get this was the name, really.
> Also, passing the delegating object in case there is more than one task. The
> rest is pretty much the same.

Both of those items were addressed in the patch under comment 15.
Comment 26 BJ Burg 2016-05-17 09:49:22 PDT
(In reply to comment #25)
> (In reply to comment #19)
> > (In reply to comment #16)
> > > I think this patch should be considered as-is. Adding support for multiple
> > > tasks would require a token to be returned from startTask, and passed to
> > > delegate methods. The same thing can be accomplished by instantiating
> > > multiple YieldableTask objects.
> > 
> > Right. The only thing I needed to change to get this was the name, really.
> > Also, passing the delegating object in case there is more than one task. The
> > rest is pretty much the same.
> 
> Both of those items were addressed in the patch under comment 15.

Ugh! For some reason I didn't get any emails about all the subsequent patches that you wrote after the first one. Sorry for wasting your time; I thought you were working on other things at the time.
Comment 27 BJ Burg 2016-05-17 09:50:17 PDT
(In reply to comment #24)
> Created attachment 279074 [details]
> Archive of layout-test-results from ews103 for mac-yosemite
> 
> The attached test failures were seen while running run-webkit-tests on the
> mac-ews.
> Bot: ews103  Port: mac-yosemite  Platform: Mac OS X 10.10.5

Looks like the DidFinish callback is getting called twice, probably because more than one place is trying to send that during teardown.
Comment 28 BJ Burg 2016-05-17 12:41:03 PDT
Comment on attachment 279065 [details]
Proposed Fix v2

View in context: https://bugs.webkit.org/attachment.cgi?id=279065&action=review

>> Source/WebInspectorUI/UserInterface/Base/YieldableTask.js:106
>> +        this._pendingItemsIterator = createIteratorForProcessingItems.call(this);
> 
> I suppose this is why you used create. I am surprised call() works… maybe I need to read up on generators.

Invoking a generator function creates a new iterator instance with its own state. This is basically a factory method. Once we have more examples in the code base, we should have a team discussion about the style to use for generators.

Ref:

"A generator is a special type of function that works as a factory for iterators."
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators

> Source/WebInspectorUI/UserInterface/Base/YieldableTask.js:120
> +        setTimeout(() => { this._didFinish(); }, 0);

It turns out this is unnecessary and responsible for the test flakiness. I will remove it before landing.

>> Source/WebInspectorUI/UserInterface/Base/YieldableTask.js:137
>> +    _willYield(processedItems, elapsedTime)
> 
> _didYield? It is confusing since this calls yieldableTaskDidYield.

There's no way to notify after the actual yield as the iterator's context will be suspended. So we set up state as if the yield happened, and then notify.
Comment 29 BJ Burg 2016-05-17 12:44:26 PDT
Created attachment 279150 [details]
For Landing
Comment 30 Build Bot 2016-05-17 13:06:40 PDT
Comment on attachment 279150 [details]
For Landing

Attachment 279150 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.webkit.org/results/1337857

Number of test failures exceeded the failure limit.
Comment 31 Build Bot 2016-05-17 13:06:43 PDT
Created attachment 279155 [details]
Archive of layout-test-results from ews102 for mac-yosemite

The attached test failures were seen while running run-webkit-tests on the mac-ews.
Bot: ews102  Port: mac-yosemite  Platform: Mac OS X 10.10.5
Comment 32 BJ Burg 2016-05-17 14:37:22 PDT
Committed r201047: <http://trac.webkit.org/changeset/201047>