Bug 59801

Summary: fast/workers/storage/use-same-database-in-page-and-workers.html sometimes asserts in WorkerThread::workerThread
Product: WebKit Reporter: Adam Roben (:aroben) <aroben>
Component: WebCore JavaScriptAssignee: Nobody <webkit-unassigned>
Status: NEW ---    
Severity: Normal CC: ap, ddkilzer, dimich, dumi, ericu, levin, michaeln, mrobinson, yurys
Priority: P2 Keywords: InRadar, LayoutTestFailure, MakingBotsRed
Version: 528+ (Nightly build)   
Hardware: PC   
OS: OS X 10.5   
URL: http://build.webkit.org/results/Leopard%20Intel%20Debug%20(Tests)/r85325%20(29670)/fast/workers/storage/use-same-database-in-page-and-workers-crash-log.txt

Description Adam Roben (:aroben) 2011-04-29 09:49:00 PDT
fast/workers/storage/use-same-database-in-page-and-workers.html sometimes asserts in WorkerThread::workerThread. See <http://build.webkit.org/results/Leopard%20Intel%20Debug%20(Tests)/r85325%20(29670)/fast/workers/storage/use-same-database-in-page-and-workers-crash-log.txt> for an example.
Comment 1 Adam Roben (:aroben) 2011-04-29 09:49:15 PDT
<rdar://problem/9358641>
Comment 2 Michael Nordman 2011-04-29 13:15:33 PDT
void* WorkerThread::workerThread()
{
    {
        MutexLocker lock(m_threadCreationMutex);
        m_workerContext = createWorkerContext(m_startupData->m_scriptURL, m_startupData->m_userAgent);

        if (m_runLoop.terminated()) {
            // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet,
            // forbidExecution() couldn't be called from stop().
           m_workerContext->script()->forbidExecution();
        }
    }

    WorkerScriptController* script = m_workerContext->script();
    script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL));
    // Free the startup data to cause its member variable deref's happen on the worker's thread (since
    // all ref/derefs of these objects are happening on the thread at this point). Note that
    // WorkerThread::~WorkerThread happens on a different thread where it was created.
    m_startupData.clear();

    runEventLoop();

    ThreadIdentifier threadID = m_threadID;

    ASSERT(m_workerContext->hasOneRef());  ******* TRIPPING ON THIS (?) *******

    // The below assignment will destroy the context, which will in turn notify messaging proxy.
    // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
    m_workerContext = 0;

    // Clean up WebCore::ThreadGlobalData before WTF::WTFThreadData goes away!
    threadGlobalData().destroy();

    // The thread object may be already destroyed from notification now, don't try to access "this".
    detachThread(threadID);

    return 0;
}
Comment 3 Adam Roben (:aroben) 2011-04-29 13:25:33 PDT
From <http://build.webkit.org/results/Leopard%20Intel%20Debug%20(Tests)/r85325%20(29670)/fast/workers/storage/use-same-database-in-page-and-workers-stderr.txt>:

ASSERTION FAILED: m_workerContext->hasOneRef()
/Volumes/Big/WebKit-BuildSlave/leopard-intel-debug/build/Source/WebCore/workers/WorkerThread.cpp(145) : void* WebCore::WorkerThread::workerThread()
1   WebCore::WorkerThread::workerThreadCount()
2   WebCore::WorkerThread::workerThreadCount()
3   WTF::createThread(void* (*)(void*), void*)
4   _pthread_start
5   thread_start
Comment 4 Alexey Proskuryakov 2011-04-29 13:26:17 PDT
That's a fairly important assertion - if that fails, the worker context may leak (since no GC will happen after the thread has exited).
Comment 5 Michael Nordman 2011-04-29 17:05:08 PDT
This may be contributing to the problem. Databases hold a reference to their ScriptExecutionContext and there is no guarantee that a Database is finally released and deleted on the context thread.

Database::~Database()
{
    // The reference to the ScriptExecutionContext needs to be cleared on the JavaScript thread.
    // If we're on that thread already, we can just let the RefPtr's destruction do the dereffing.
    if (!m_scriptExecutionContext->isContextThread()) {
        // Grab a pointer to the script execution here because we're releasing it when we pass it to
        // DerefContextTask::create.
        ScriptExecutionContext* scriptExecutionContext = m_scriptExecutionContext.get();
        
        scriptExecutionContext->postTask(DerefContextTask::create(m_scriptExecutionContext.release()));
    }
}

A similar situation exists with SQLTransactions and SQLStatements and the callback objects they hold, there is no guarantee that SQLTransactions and SQLStatements are deleted on the context thread, but the callback must be released on the context thread... see class SQLCallbackWrapper.


Maybe something that could help would be to have the safeRelease task created by SQLCallbackWrapper use the isCleanupTask() feature of ScriptExecutionContext::Task as does DerefContextTask?

class SQLCallbackWrapper {
...
    void clear()
    {
        ScriptExecutionContext* context;
        T* callback;
        {
            MutexLocker locker(m_mutex);
            if (!m_callback) {
                ASSERT(!m_scriptExecutionContext);
                return;
            }
            if (m_scriptExecutionContext->isContextThread()) {
                m_callback = 0;
                m_scriptExecutionContext = 0;
                return;
            }
            context = m_scriptExecutionContext.release().leakRef();
            callback = m_callback.release().leakRef();
        }
        context->postTask(createCallbackTask(&safeRelease, callback));   ******* THIS TASK ******
    }


class ScriptExecutionContext {
...
        class Task {
            WTF_MAKE_NONCOPYABLE(Task); WTF_MAKE_FAST_ALLOCATED;
        public:
            Task() { }
            virtual ~Task();
            virtual void performTask(ScriptExecutionContext*) = 0;
            // Certain tasks get marked specially so that they aren't discarded, and are executed,
            // when the context is shutting down its message queue.
            virtual bool isCleanupTask() const { return false; }    ****** USE THIS FEATURE ******
        };
Comment 6 Ryosuke Niwa 2012-01-10 02:05:32 PST
*** Bug 49869 has been marked as a duplicate of this bug. ***
Comment 7 Gustavo Noronha (kov) 2012-05-02 14:32:04 PDT
*** Bug 56147 has been marked as a duplicate of this bug. ***