Bug 72538 - WebWorker threads can outlive the QApplication instance
Summary: WebWorker threads can outlive the QApplication instance
Status: RESOLVED WONTFIX
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit Qt (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-11-16 13:11 PST by Andrew Wason
Modified: 2011-12-01 14:35 PST (History)
5 users (show)

See Also:


Attachments
sample code (1.34 KB, application/zip)
2011-11-16 13:11 PST, Andrew Wason
no flags Details
gdb backtrace showing running WorkerThreads during QApplication destruction (61.07 KB, text/plain)
2011-11-16 13:12 PST, Andrew Wason
no flags Details
wait for WorkerThread to terminate in stop() (2.30 KB, patch)
2011-11-17 14:48 PST, Andrew Wason
no flags Details | Formatted Diff | Diff
wait for WorkerThread only for Qt platform (2.33 KB, patch)
2011-11-23 18:12 PST, Andrew Wason
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Andrew Wason 2011-11-16 13:11:40 PST
Created attachment 115429 [details]
sample code

Currently when a QWebPage that has WebWorkers is destroyed, or when close() is called from the worker JS, the WorkerThread is quit (WebCore::Document::detach calls WebCore::ScriptExecutionContext::stopActiveDOMObjects which calls WebCore::Worker::stop) but not waited for. So there is a race condition, if the QApplication event loop quits and QApplication is destroyed, the WorkerThreads may still be running and may access internal Qt state and crash.

See bug 72155 for some discussion of this.

The attached sample code demonstrates this. It loads a page that spawns WebWorkers, then waits and quits QApplication. Setting a breakpoint in QCoreApplication::~QCoreApplication you can see a number of WorkerThreads are still active and have not finished shutting down.

Somewhere we should wait for the WorkerThreads to finish.
Comment 1 Andrew Wason 2011-11-16 13:12:22 PST
Created attachment 115431 [details]
gdb backtrace showing running WorkerThreads during QApplication destruction
Comment 2 Andrew Wason 2011-11-17 14:48:07 PST
Created attachment 115691 [details]
wait for WorkerThread to terminate in stop()
Comment 3 Simon Hausmann 2011-11-21 00:33:07 PST
How do the other ports handle this?
Comment 4 Andrew Wason 2011-11-21 07:38:26 PST
(In reply to comment #3)
> How do the other ports handle this?

It looks to me like they don't, they have the same problem. This patch is not port-specific so should fix it for all of them.

I just checked with the gtk port, spawned a bunch of workers and put a breakpoint in _exit and when I quit GtkLauncher many WorkerThreads were still active.

But I think not waiting for WorkerThreads is a bigger problem for the Qt port since the threads seem to be using some Qt objects after QApplication is destroyed leading to periodic crashes.
Comment 5 Alexey Proskuryakov 2011-11-21 14:59:17 PST
Making thread stopping slow and synchronous does not seem so great. IIRC the design is that even though the thread continues to run for a short while, it should not cause any problems as communication is cut off.

Dave, Dmitry, could you please comment?

As a separate question, why does QApplication ever need to be destroyed?
Comment 6 Andrew Wason 2011-11-21 15:08:41 PST
(In reply to comment #5)
>
> As a separate question, why does QApplication ever need to be destroyed?

In Qt applications it's typically destroyed when main() returns. I guess it could be allocated on the heap and leaked, but even then there is global static internal Qt state that is destroyed as part of application shutdown and this can cause a crash when lingering WorkerThreads threads indirectly access it.
Comment 7 Dmitry Titov 2011-11-21 15:37:57 PST
I agree with Alexey here. There is all kinds of confirmations going between ScriptExecutionContext of the page and WorkerContext about shutdowns and terminations (not of the actual OS thread but of a message queue-based 'worker thread'). It should be enough to deallocate any resources allocated by port's implementation. "workerContextDestroyed" callback is the one I would probably look at.

We should not assume there is always going to be 1:1 correspondence between workers and OS threads. At least this is going to be port-specific. For example, Chromium can run workers in separate process and might need to manage threads/resources/shutdown sequence in a specific way there.
Comment 8 Andrew Wason 2011-11-23 07:34:36 PST
(In reply to comment #7)
> It should be enough to deallocate any resources allocated by port's implementation.

The platform specific resource is the QThread itself, and the only time it is destroyed is when the thread exits. Bug 72155 helps somewhat with this by dropping QThread in favor of native threads, but native threads become QAdoptedThreads when WTF::scheduleDispatchFunctionsOnMainThread is called from them to postMessage from the WebWorker - so we have a similar problem.

Perhaps Qt's WTF::scheduleDispatchFunctionsOnMainThread could be changed to connect QCoreApplication::aboutToQuit to a slot that does QThread::wait, and connect QThread::finished to a slot that disconnects that. So this way when QApplication is finished, it will wait for all outstanding worker QThreads to finish. I'll look into this.

Currently my workaround for this bug in my application is to install a signal handler for SIGSEGV when QApplication::exec returns, and in the handler just _exit(0). This is nasty.
Comment 9 Andrew Wason 2011-11-23 18:12:43 PST
Created attachment 116462 [details]
wait for WorkerThread only for Qt platform

I've tried a number of approaches but can't see how to do this safely with Qt. Part of the problem is knowing when the application is shutting down in order to safely wait for the workers - connecting to QCoreApplication::aboutToQuit is too early because any QWebPages may not be destroyed yet, so they won't have told their workers to quit. Connecting to QCoreApplication::destroyed is too late because some workers may still be starting up and crash because QCoreApplication::instance() is now NULL.

Attached patch makes waiting for WorkerThreads in stop() conditional on PLATFORM(QT)
Comment 10 Alexey Proskuryakov 2011-11-23 21:19:07 PST
Comment on attachment 116462 [details]
wait for WorkerThread only for Qt platform

This puts a burden on whoever need to modify this code next. They would have to understand both regular code path and what QT does, which is not so easy.
Comment 11 Simon Hausmann 2011-11-24 05:52:29 PST
(In reply to comment #7)
> We should not assume there is always going to be 1:1 correspondence between workers and OS threads. At least this is going to be port-specific. For example, Chromium can run workers in separate process and might need to manage threads/resources/shutdown sequence in a specific way there.

That's a good point, but even if workers are implemented as processes, don't you want those to terminate in a "controlled" way?

How does chromium enforce the termination of shared workers if for example I close the last window?
Comment 12 Simon Hausmann 2011-11-24 05:56:01 PST
Nevermind my previous comment, I see now how this is kind of a Qt specific race condition. Hmmmm.
Comment 13 Andrew Wason 2011-12-01 11:33:57 PST
This bug was made irrelevant by bug 72155 - now that we don't use Qt objects in worker threads, it's safe to let them outlive QApplication.

For future reference, if we ever do need to wait for threads when QApplication is being destroyed - it looks like a Qt qAddPostRoutine() is the best place to do it.