Bug 28702 - Workers do not exit after async operations
Summary: Workers do not exit after async operations
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore JavaScript (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-08-24 22:38 PDT by Andrew Wilson
Modified: 2009-08-27 11:57 PDT (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrew Wilson 2009-08-24 22:38:06 PDT
The dedicated worker code has a mechanism in place where DedicatedWorkerContext reports pending activity back to the parent Worker object every time it receives a message. The idea is that if the parent worker object is no longer reachable, and the worker thread goes idle, WebKit will shut down the worker thread because it has been orphaned.

The problem is that this mechanism (WorkerMessagingProxy::reportPendingActivity()) only triggers when a message is received - if the worker performs some async operation, there's nothing to notify the parent worker object when the async operation completes and the worker goes idle again unless another message is sent to the worker.

The net result is (effectively) a memory leak - the worker context hangs around in an unreachable state until the parent page closes, because it appears that the page always has pending activity.

BTW - the HTML5 spec does not make any guarantees about when an unreachable Worker is shut down - once a Worker is unreachable, it's up to the UA to decide whether to shut it down or not.
Comment 1 Alexey Proskuryakov 2009-08-27 10:01:42 PDT
I think that we had code that was supposed to prevent this; some of that was apparently removed in <http://trac.webkit.org/changeset/46507#file6>. I'm not sure if we ever had this completely right though.
Comment 2 Andrew Wilson 2009-08-27 10:24:18 PDT
(In reply to comment #1)
> I think that we had code that was supposed to prevent this; some of that was
> apparently removed in <http://trac.webkit.org/changeset/46507#file6>. I'm not
> sure if we ever had this completely right though.

That code wasn't removed - it was just moved out of WorkerScriptController (since it's not needed for SharedWorkers) and into DedicatedWorkerContext.

The original mechanism to shutdown orphaned workers still works - the problem is that when we added async APIs to WorkerGlobalScope (setTimeout(), XHR) we didn't add any additional mechanism to report pending activity when those async processes ended.
Comment 3 Alexey Proskuryakov 2009-08-27 10:42:38 PDT
(In reply to comment #2)
> That code wasn't removed - it was just moved out of WorkerScriptController
> (since it's not needed for SharedWorkers) and into DedicatedWorkerContext.

The code reported pending activity after any JavaScript evaluation - but in its new place, it only reports after importScripts().
Comment 4 Andrew Wilson 2009-08-27 11:12:13 PDT
(In reply to comment #3)
> (In reply to comment #2)
> > That code wasn't removed - it was just moved out of WorkerScriptController
> > (since it's not needed for SharedWorkers) and into DedicatedWorkerContext.
> 
> The code reported pending activity after any JavaScript evaluation - but in its
> new place, it only reports after importScripts().

When I looked at the code, it did not appear to me that XHR and setTimeout() callbacks were routed through WorkerScriptController. Only the initial script load/evaluation went through that code - async callbacks went through the normal message loop event processing mechanism.

If this is incorrect, I'll update the code to do the right thing - just point me at where setTimeout handlers invoke WorkerScriptController::evaluate(). I only found two calls to evaluate() - in WorkerThread::workerThread (initial worker script load) and in WorkerContext::importScripts() so I don't see how pending activity could have been reported in these other cases because WorkerScriptController isn't involved.
Comment 5 Andrew Wilson 2009-08-27 11:57:06 PDT
After talking with ap on IRC:

1) The worker code refactoring broke setTimeout("code to evaluate") - ScheduledAction.cpp invokes scriptController->evaluate(), and we no longer call reportPendingActivity() from there (responsibility for doing this was moved to the caller).

2) The setTimeout(function() {...}) code was already broken, since it never went through WorkerScriptController.

3) XHR callbacks have always been broken AFAIK since they just invoke their listener code directly and have never gone through WorkerScriptController.

It seems like the right solution is to have WorkerRunLoop::run() report pending activity after each event is processed - any other solution seems fragile since it requires extra work every time we add a new async operation (DB transactions, appcache notifications, etc).