Home | History | Annotate | Download | only in workers
      1 /*
      2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  *
     25  */
     26 
     27 #include "config.h"
     28 
     29 #include "core/workers/WorkerThread.h"
     30 
     31 #include "bindings/v8/ScriptSourceCode.h"
     32 #include "core/inspector/InspectorInstrumentation.h"
     33 #include "core/platform/ThreadGlobalData.h"
     34 #include "core/workers/DedicatedWorkerGlobalScope.h"
     35 #include "core/workers/WorkerClients.h"
     36 #include "core/workers/WorkerThreadStartupData.h"
     37 #include "modules/webdatabase/DatabaseManager.h"
     38 #include "modules/webdatabase/DatabaseTask.h"
     39 #include "public/platform/Platform.h"
     40 #include "public/platform/WebWorkerRunLoop.h"
     41 #include "weborigin/KURL.h"
     42 #include "wtf/Noncopyable.h"
     43 #include "wtf/text/WTFString.h"
     44 
     45 #include <utility>
     46 
     47 namespace WebCore {
     48 
     49 static Mutex& threadSetMutex()
     50 {
     51     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
     52     return mutex;
     53 }
     54 
     55 static HashSet<WorkerThread*>& workerThreads()
     56 {
     57     DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ());
     58     return threads;
     59 }
     60 
     61 unsigned WorkerThread::workerThreadCount()
     62 {
     63     MutexLocker lock(threadSetMutex());
     64     return workerThreads().size();
     65 }
     66 
     67 WorkerThread::WorkerThread(WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, PassOwnPtr<WorkerThreadStartupData> startupData)
     68     : m_threadID(0)
     69     , m_workerLoaderProxy(workerLoaderProxy)
     70     , m_workerReportingProxy(workerReportingProxy)
     71     , m_startupData(startupData)
     72 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
     73     , m_notificationClient(0)
     74 #endif
     75 {
     76     MutexLocker lock(threadSetMutex());
     77     workerThreads().add(this);
     78 }
     79 
     80 WorkerThread::~WorkerThread()
     81 {
     82     MutexLocker lock(threadSetMutex());
     83     ASSERT(workerThreads().contains(this));
     84     workerThreads().remove(this);
     85 }
     86 
     87 bool WorkerThread::start()
     88 {
     89     // Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts.
     90     MutexLocker lock(m_threadCreationMutex);
     91 
     92     if (m_threadID)
     93         return true;
     94 
     95     m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore: Worker");
     96 
     97     return m_threadID;
     98 }
     99 
    100 void WorkerThread::workerThreadStart(void* thread)
    101 {
    102     static_cast<WorkerThread*>(thread)->workerThread();
    103 }
    104 
    105 void WorkerThread::workerThread()
    106 {
    107     KURL scriptURL = m_startupData->m_scriptURL;
    108     String sourceCode = m_startupData->m_sourceCode;
    109     WorkerThreadStartMode startMode = m_startupData->m_startMode;
    110 
    111     {
    112         MutexLocker lock(m_threadCreationMutex);
    113 
    114         m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release());
    115 
    116         if (m_runLoop.terminated()) {
    117             // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet,
    118             // forbidExecution() couldn't be called from stop().
    119             m_workerGlobalScope->script()->forbidExecution();
    120         }
    121     }
    122     // The corresponding call to didStopWorkerRunLoop is in
    123     // ~WorkerScriptController.
    124     WebKit::Platform::current()->didStartWorkerRunLoop(WebKit::WebWorkerRunLoop(&m_runLoop));
    125 
    126     WorkerScriptController* script = m_workerGlobalScope->script();
    127     InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), startMode);
    128     script->evaluate(ScriptSourceCode(sourceCode, scriptURL));
    129 
    130     runEventLoop();
    131 
    132     ThreadIdentifier threadID = m_threadID;
    133 
    134     ASSERT(m_workerGlobalScope->hasOneRef());
    135 
    136     // The below assignment will destroy the context, which will in turn notify messaging proxy.
    137     // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
    138     m_workerGlobalScope = 0;
    139 
    140     // Clean up WebCore::ThreadGlobalData before WTF::WTFThreadData goes away!
    141     threadGlobalData().destroy();
    142 
    143     // The thread object may be already destroyed from notification now, don't try to access "this".
    144     detachThread(threadID);
    145 }
    146 
    147 void WorkerThread::runEventLoop()
    148 {
    149     // Does not return until terminated.
    150     m_runLoop.run(m_workerGlobalScope.get());
    151 }
    152 
    153 class WorkerThreadShutdownFinishTask : public ScriptExecutionContext::Task {
    154 public:
    155     static PassOwnPtr<WorkerThreadShutdownFinishTask> create()
    156     {
    157         return adoptPtr(new WorkerThreadShutdownFinishTask());
    158     }
    159 
    160     virtual void performTask(ScriptExecutionContext *context)
    161     {
    162         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    163         workerGlobalScope->clearInspector();
    164         // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
    165         workerGlobalScope->clearScript();
    166     }
    167 
    168     virtual bool isCleanupTask() const { return true; }
    169 };
    170 
    171 class WorkerThreadShutdownStartTask : public ScriptExecutionContext::Task {
    172 public:
    173     static PassOwnPtr<WorkerThreadShutdownStartTask> create()
    174     {
    175         return adoptPtr(new WorkerThreadShutdownStartTask());
    176     }
    177 
    178     virtual void performTask(ScriptExecutionContext *context)
    179     {
    180         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    181 
    182         // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below?
    183         DatabaseTaskSynchronizer cleanupSync;
    184         DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync);
    185 
    186         workerGlobalScope->stopActiveDOMObjects();
    187 
    188         workerGlobalScope->notifyObserversOfStop();
    189 
    190         // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
    191         // which become dangling once Heap is destroyed.
    192         workerGlobalScope->removeAllEventListeners();
    193 
    194         // We wait for the database thread to clean up all its stuff so that we
    195         // can do more stringent leak checks as we exit.
    196         cleanupSync.waitForTaskCompletion();
    197 
    198         // Stick a shutdown command at the end of the queue, so that we deal
    199         // with all the cleanup tasks the databases post first.
    200         workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create());
    201     }
    202 
    203     virtual bool isCleanupTask() const { return true; }
    204 };
    205 
    206 void WorkerThread::stop()
    207 {
    208     // Mutex protection is necessary because stop() can be called before the context is fully created.
    209     MutexLocker lock(m_threadCreationMutex);
    210 
    211     // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever.
    212     if (m_workerGlobalScope) {
    213         m_workerGlobalScope->script()->scheduleExecutionTermination();
    214 
    215         DatabaseManager::manager().interruptAllDatabasesForContext(m_workerGlobalScope.get());
    216         m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create());
    217         return;
    218     }
    219     m_runLoop.terminate();
    220 }
    221 
    222 bool WorkerThread::isCurrentThread() const
    223 {
    224     return m_threadID == currentThread();
    225 }
    226 
    227 class ReleaseFastMallocFreeMemoryTask : public ScriptExecutionContext::Task {
    228     virtual void performTask(ScriptExecutionContext*) OVERRIDE { WTF::releaseFastMallocFreeMemory(); }
    229 };
    230 
    231 void WorkerThread::releaseFastMallocFreeMemoryInAllThreads()
    232 {
    233     MutexLocker lock(threadSetMutex());
    234     HashSet<WorkerThread*>& threads = workerThreads();
    235     HashSet<WorkerThread*>::iterator end = threads.end();
    236     for (HashSet<WorkerThread*>::iterator it = threads.begin(); it != end; ++it)
    237         (*it)->runLoop().postTask(adoptPtr(new ReleaseFastMallocFreeMemoryTask));
    238 }
    239 
    240 } // namespace WebCore
    241