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/WorkerReportingProxy.h"
     36 #include "core/workers/WorkerThreadStartupData.h"
     37 #include "platform/PlatformThreadData.h"
     38 #include "platform/heap/ThreadState.h"
     39 #include "platform/weborigin/KURL.h"
     40 #include "public/platform/Platform.h"
     41 #include "public/platform/WebWaitableEvent.h"
     42 #include "public/platform/WebWorkerRunLoop.h"
     43 #include "wtf/Noncopyable.h"
     44 #include "wtf/text/WTFString.h"
     45 
     46 #include <utility>
     47 
     48 namespace WebCore {
     49 
     50 static Mutex& threadSetMutex()
     51 {
     52     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
     53     return mutex;
     54 }
     55 
     56 static HashSet<WorkerThread*>& workerThreads()
     57 {
     58     DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ());
     59     return threads;
     60 }
     61 
     62 unsigned WorkerThread::workerThreadCount()
     63 {
     64     MutexLocker lock(threadSetMutex());
     65     return workerThreads().size();
     66 }
     67 
     68 WorkerThread::WorkerThread(WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData)
     69     : m_threadID(0)
     70     , m_workerLoaderProxy(workerLoaderProxy)
     71     , m_workerReportingProxy(workerReportingProxy)
     72     , m_startupData(startupData)
     73     , m_notificationClient(0)
     74     , m_shutdownEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
     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         ThreadState::attach();
    114         m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release());
    115         m_runLoop.setWorkerGlobalScope(workerGlobalScope());
    116 
    117         if (m_runLoop.terminated()) {
    118             // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet,
    119             // forbidExecution() couldn't be called from stop().
    120             m_workerGlobalScope->script()->forbidExecution();
    121         }
    122     }
    123     // The corresponding call to didStopWorkerRunLoop is in
    124     // ~WorkerScriptController.
    125     blink::Platform::current()->didStartWorkerRunLoop(blink::WebWorkerRunLoop(&m_runLoop));
    126 
    127     // Notify proxy that a new WorkerGlobalScope has been created and started.
    128     m_workerReportingProxy.workerGlobalScopeStarted(m_workerGlobalScope.get());
    129 
    130     WorkerScriptController* script = m_workerGlobalScope->script();
    131     if (!script->isExecutionForbidden())
    132         script->initializeContextIfNeeded();
    133     InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), startMode);
    134     script->evaluate(ScriptSourceCode(sourceCode, scriptURL));
    135 
    136     runEventLoop();
    137 
    138     // This should be called before we start the shutdown procedure.
    139     workerReportingProxy().willDestroyWorkerGlobalScope();
    140 
    141     ThreadIdentifier threadID = m_threadID;
    142 
    143     // The below assignment will destroy the context, which will in turn notify messaging proxy.
    144     // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
    145     // If Oilpan is enabled, we detach of the context/global scope, with the final heap cleanup below sweeping it out.
    146 #if !ENABLE(OILPAN)
    147     ASSERT(m_workerGlobalScope->hasOneRef());
    148 #endif
    149     m_workerGlobalScope->dispose();
    150     m_workerGlobalScope = nullptr;
    151 
    152     // Detach the ThreadState, cleaning out the thread's heap by
    153     // performing a final GC. The cleanup operation will at the end
    154     // assert that the heap is empty. If the heap does not become
    155     // empty, there are still pointers into the heap and those
    156     // pointers will be dangling after thread termination because we
    157     // are destroying the heap. It is important to detach while the
    158     // thread is still valid. In particular, finalizers for objects in
    159     // the heap for this thread will need to access thread local data.
    160     ThreadState::detach();
    161 
    162     // Notify the proxy that the WorkerGlobalScope has been disposed of.
    163     // This can free this thread object, hence it must not be touched afterwards.
    164     workerReportingProxy().workerGlobalScopeDestroyed();
    165 
    166     // Clean up PlatformThreadData before WTF::WTFThreadData goes away!
    167     PlatformThreadData::current().destroy();
    168 
    169     // The thread object may be already destroyed from notification now, don't try to access "this".
    170     detachThread(threadID);
    171 }
    172 
    173 void WorkerThread::runEventLoop()
    174 {
    175     // Does not return until terminated.
    176     m_runLoop.run();
    177 }
    178 
    179 class WorkerThreadShutdownFinishTask : public ExecutionContextTask {
    180 public:
    181     static PassOwnPtr<WorkerThreadShutdownFinishTask> create()
    182     {
    183         return adoptPtr(new WorkerThreadShutdownFinishTask());
    184     }
    185 
    186     virtual void performTask(ExecutionContext *context)
    187     {
    188         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    189         workerGlobalScope->clearInspector();
    190         // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
    191         workerGlobalScope->clearScript();
    192     }
    193 
    194     virtual bool isCleanupTask() const { return true; }
    195 };
    196 
    197 class WorkerThreadShutdownStartTask : public ExecutionContextTask {
    198 public:
    199     static PassOwnPtr<WorkerThreadShutdownStartTask> create()
    200     {
    201         return adoptPtr(new WorkerThreadShutdownStartTask());
    202     }
    203 
    204     virtual void performTask(ExecutionContext *context)
    205     {
    206         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    207         workerGlobalScope->stopFetch();
    208         workerGlobalScope->stopActiveDOMObjects();
    209 
    210         // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
    211         // which become dangling once Heap is destroyed.
    212         workerGlobalScope->removeAllEventListeners();
    213 
    214         // Stick a shutdown command at the end of the queue, so that we deal
    215         // with all the cleanup tasks the databases post first.
    216         workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create());
    217     }
    218 
    219     virtual bool isCleanupTask() const { return true; }
    220 };
    221 
    222 void WorkerThread::stop()
    223 {
    224     // Prevent the deadlock between GC and an attempt to stop a thread.
    225     ThreadState::SafePointScope safePointScope(ThreadState::HeapPointersOnStack);
    226 
    227     // Mutex protection is necessary because stop() can be called before the context is fully created.
    228     MutexLocker lock(m_threadCreationMutex);
    229 
    230     // Signal the thread to notify that the thread's stopping.
    231     if (m_shutdownEvent)
    232         m_shutdownEvent->signal();
    233 
    234     // 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.
    235     if (m_workerGlobalScope) {
    236         m_workerGlobalScope->script()->scheduleExecutionTermination();
    237         m_workerGlobalScope->wasRequestedToTerminate();
    238         m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create());
    239         return;
    240     }
    241     m_runLoop.terminate();
    242 }
    243 
    244 bool WorkerThread::isCurrentThread() const
    245 {
    246     return m_threadID == currentThread();
    247 }
    248 
    249 } // namespace WebCore
    250