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/workers/DedicatedWorkerGlobalScope.h"
     34 #include "core/workers/WorkerClients.h"
     35 #include "core/workers/WorkerThreadStartupData.h"
     36 #include "modules/webdatabase/DatabaseManager.h"
     37 #include "modules/webdatabase/DatabaseTask.h"
     38 #include "platform/PlatformThreadData.h"
     39 #include "platform/weborigin/KURL.h"
     40 #include "public/platform/Platform.h"
     41 #include "public/platform/WebWorkerRunLoop.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     , m_notificationClient(0)
     73 {
     74     MutexLocker lock(threadSetMutex());
     75     workerThreads().add(this);
     76 }
     77 
     78 WorkerThread::~WorkerThread()
     79 {
     80     MutexLocker lock(threadSetMutex());
     81     ASSERT(workerThreads().contains(this));
     82     workerThreads().remove(this);
     83 }
     84 
     85 bool WorkerThread::start()
     86 {
     87     // Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts.
     88     MutexLocker lock(m_threadCreationMutex);
     89 
     90     if (m_threadID)
     91         return true;
     92 
     93     m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore: Worker");
     94 
     95     return m_threadID;
     96 }
     97 
     98 void WorkerThread::workerThreadStart(void* thread)
     99 {
    100     static_cast<WorkerThread*>(thread)->workerThread();
    101 }
    102 
    103 void WorkerThread::workerThread()
    104 {
    105     KURL scriptURL = m_startupData->m_scriptURL;
    106     String sourceCode = m_startupData->m_sourceCode;
    107     WorkerThreadStartMode startMode = m_startupData->m_startMode;
    108 
    109     {
    110         MutexLocker lock(m_threadCreationMutex);
    111 
    112         m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release());
    113 
    114         if (m_runLoop.terminated()) {
    115             // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet,
    116             // forbidExecution() couldn't be called from stop().
    117             m_workerGlobalScope->script()->forbidExecution();
    118         }
    119     }
    120     // The corresponding call to didStopWorkerRunLoop is in
    121     // ~WorkerScriptController.
    122     blink::Platform::current()->didStartWorkerRunLoop(blink::WebWorkerRunLoop(&m_runLoop));
    123 
    124     WorkerScriptController* script = m_workerGlobalScope->script();
    125     InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), startMode);
    126     script->evaluate(ScriptSourceCode(sourceCode, scriptURL));
    127 
    128     runEventLoop();
    129 
    130     ThreadIdentifier threadID = m_threadID;
    131 
    132     ASSERT(m_workerGlobalScope->hasOneRef());
    133 
    134     // The below assignment will destroy the context, which will in turn notify messaging proxy.
    135     // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
    136     m_workerGlobalScope = 0;
    137 
    138     // Clean up PlatformThreadData before WTF::WTFThreadData goes away!
    139     PlatformThreadData::current().destroy();
    140 
    141     // The thread object may be already destroyed from notification now, don't try to access "this".
    142     detachThread(threadID);
    143 }
    144 
    145 void WorkerThread::runEventLoop()
    146 {
    147     // Does not return until terminated.
    148     m_runLoop.run(m_workerGlobalScope.get());
    149 }
    150 
    151 class WorkerThreadShutdownFinishTask : public ExecutionContextTask {
    152 public:
    153     static PassOwnPtr<WorkerThreadShutdownFinishTask> create()
    154     {
    155         return adoptPtr(new WorkerThreadShutdownFinishTask());
    156     }
    157 
    158     virtual void performTask(ExecutionContext *context)
    159     {
    160         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    161         workerGlobalScope->clearInspector();
    162         // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
    163         workerGlobalScope->clearScript();
    164     }
    165 
    166     virtual bool isCleanupTask() const { return true; }
    167 };
    168 
    169 class WorkerThreadShutdownStartTask : public ExecutionContextTask {
    170 public:
    171     static PassOwnPtr<WorkerThreadShutdownStartTask> create()
    172     {
    173         return adoptPtr(new WorkerThreadShutdownStartTask());
    174     }
    175 
    176     virtual void performTask(ExecutionContext *context)
    177     {
    178         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    179 
    180         // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below?
    181         DatabaseTaskSynchronizer cleanupSync;
    182         DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync);
    183 
    184         workerGlobalScope->stopActiveDOMObjects();
    185 
    186         workerGlobalScope->notifyObserversOfStop();
    187 
    188         // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
    189         // which become dangling once Heap is destroyed.
    190         workerGlobalScope->removeAllEventListeners();
    191 
    192         // We wait for the database thread to clean up all its stuff so that we
    193         // can do more stringent leak checks as we exit.
    194         cleanupSync.waitForTaskCompletion();
    195 
    196         // Stick a shutdown command at the end of the queue, so that we deal
    197         // with all the cleanup tasks the databases post first.
    198         workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create());
    199     }
    200 
    201     virtual bool isCleanupTask() const { return true; }
    202 };
    203 
    204 void WorkerThread::stop()
    205 {
    206     // Mutex protection is necessary because stop() can be called before the context is fully created.
    207     MutexLocker lock(m_threadCreationMutex);
    208 
    209     // 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.
    210     if (m_workerGlobalScope) {
    211         m_workerGlobalScope->script()->scheduleExecutionTermination();
    212 
    213         DatabaseManager::manager().interruptAllDatabasesForContext(m_workerGlobalScope.get());
    214         m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create());
    215         return;
    216     }
    217     m_runLoop.terminate();
    218 }
    219 
    220 bool WorkerThread::isCurrentThread() const
    221 {
    222     return m_threadID == currentThread();
    223 }
    224 
    225 class ReleaseFastMallocFreeMemoryTask : public ExecutionContextTask {
    226     virtual void performTask(ExecutionContext*) OVERRIDE { WTF::releaseFastMallocFreeMemory(); }
    227 };
    228 
    229 void WorkerThread::releaseFastMallocFreeMemoryInAllThreads()
    230 {
    231     MutexLocker lock(threadSetMutex());
    232     HashSet<WorkerThread*>& threads = workerThreads();
    233     HashSet<WorkerThread*>::iterator end = threads.end();
    234     for (HashSet<WorkerThread*>::iterator it = threads.begin(); it != end; ++it)
    235         (*it)->runLoop().postTask(adoptPtr(new ReleaseFastMallocFreeMemoryTask));
    236 }
    237 
    238 } // namespace WebCore
    239