RESOLVED FIXED 157702
Web Inspector: Filtering huge data grids should yield occasionally so the UI remains responsive
https://bugs.webkit.org/show_bug.cgi?id=157702
Summary Web Inspector: Filtering huge data grids should yield occasionally so the UI ...
Matt Baker
Reported 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.
Attachments
[Patch] Proposed Fix (11.37 KB, patch)
2016-05-13 20:13 PDT, Matt Baker
no flags
[Patch] Proposed Fix (21.42 KB, patch)
2016-05-14 15:52 PDT, Matt Baker
no flags
[Patch] Proposed Fix (22.50 KB, patch)
2016-05-14 16:05 PDT, Matt Baker
no flags
[Patch] Proposed Fix (22.33 KB, patch)
2016-05-15 14:29 PDT, Matt Baker
no flags
[Patch] Proposed Fix (21.89 KB, patch)
2016-05-16 14:18 PDT, Matt Baker
no flags
Proposed Fix v2 (25.30 KB, patch)
2016-05-16 16:31 PDT, Blaze Burg
no flags
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
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
For Landing (25.56 KB, patch)
2016-05-17 12:44 PDT, Blaze Burg
buildbot: commit-queue-
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
Radar WebKit Bug Importer
Comment 1 2016-05-13 19:32:57 PDT
Matt Baker
Comment 2 2016-05-13 20:13:20 PDT
Created attachment 278914 [details] [Patch] Proposed Fix
Joseph Pecoraro
Comment 3 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!
Matt Baker
Comment 4 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.
Timothy Hatcher
Comment 5 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.
Matt Baker
Comment 6 2016-05-14 15:52:52 PDT
Created attachment 278949 [details] [Patch] Proposed Fix
Matt Baker
Comment 7 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?
Matt Baker
Comment 8 2016-05-14 16:05:03 PDT
Created attachment 278950 [details] [Patch] Proposed Fix
Timothy Hatcher
Comment 9 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?
Matt Baker
Comment 10 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.
Timothy Hatcher
Comment 11 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()
Matt Baker
Comment 12 2016-05-15 14:29:16 PDT
Created attachment 278980 [details] [Patch] Proposed Fix
Matt Baker
Comment 13 2016-05-15 14:29:52 PDT
Went with startTask/cancelTask.
Blaze Burg
Comment 14 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.
Matt Baker
Comment 15 2016-05-16 14:18:55 PDT
Created attachment 279045 [details] [Patch] Proposed Fix
Matt Baker
Comment 16 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.
Blaze Burg
Comment 17 2016-05-16 16:31:01 PDT
Created attachment 279065 [details] Proposed Fix v2
Blaze Burg
Comment 18 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.
Blaze Burg
Comment 19 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.
Timothy Hatcher
Comment 20 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.
Build Bot
Comment 21 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
Build Bot
Comment 22 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
Build Bot
Comment 23 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
Build Bot
Comment 24 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
Matt Baker
Comment 25 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.
Blaze Burg
Comment 26 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.
Blaze Burg
Comment 27 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.
Blaze Burg
Comment 28 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.
Blaze Burg
Comment 29 2016-05-17 12:44:26 PDT
Created attachment 279150 [details] For Landing
Build Bot
Comment 30 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.
Build Bot
Comment 31 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
Blaze Burg
Comment 32 2016-05-17 14:37:22 PDT
Note You need to log in before you can comment on or make changes to this bug.