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/core/v8/ScriptSourceCode.h"
     32 #include "core/dom/Microtask.h"
     33 #include "core/inspector/InspectorInstrumentation.h"
     34 #include "core/inspector/WorkerInspectorController.h"
     35 #include "core/workers/DedicatedWorkerGlobalScope.h"
     36 #include "core/workers/WorkerClients.h"
     37 #include "core/workers/WorkerReportingProxy.h"
     38 #include "core/workers/WorkerThreadStartupData.h"
     39 #include "platform/PlatformThreadData.h"
     40 #include "platform/Task.h"
     41 #include "platform/ThreadTimers.h"
     42 #include "platform/heap/ThreadState.h"
     43 #include "platform/weborigin/KURL.h"
     44 #include "public/platform/Platform.h"
     45 #include "public/platform/WebThread.h"
     46 #include "public/platform/WebWaitableEvent.h"
     47 #include "public/platform/WebWorkerRunLoop.h"
     48 #include "wtf/Noncopyable.h"
     49 #include "wtf/text/WTFString.h"
     50 
     51 #include <utility>
     52 
     53 namespace blink {
     54 
     55 namespace {
     56 const int64 kShortIdleHandlerDelayMs = 1000;
     57 const int64 kLongIdleHandlerDelayMs = 10*1000;
     58 
     59 class MicrotaskRunner : public WebThread::TaskObserver {
     60 public:
     61     virtual void willProcessTask() OVERRIDE { }
     62     virtual void didProcessTask() OVERRIDE
     63     {
     64         Microtask::performCheckpoint();
     65     }
     66 };
     67 
     68 } // namespace
     69 
     70 static Mutex& threadSetMutex()
     71 {
     72     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
     73     return mutex;
     74 }
     75 
     76 static HashSet<WorkerThread*>& workerThreads()
     77 {
     78     DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ());
     79     return threads;
     80 }
     81 
     82 unsigned WorkerThread::workerThreadCount()
     83 {
     84     MutexLocker lock(threadSetMutex());
     85     return workerThreads().size();
     86 }
     87 
     88 class WorkerSharedTimer : public SharedTimer {
     89 public:
     90     explicit WorkerSharedTimer(WorkerThread* workerThread)
     91         : m_workerThread(workerThread)
     92         , m_nextFireTime(0.0)
     93         , m_running(false)
     94     { }
     95 
     96     typedef void (*SharedTimerFunction)();
     97     virtual void setFiredFunction(SharedTimerFunction func)
     98     {
     99         m_sharedTimerFunction = func;
    100         if (!m_sharedTimerFunction)
    101             m_nextFireTime = 0.0;
    102     }
    103 
    104     virtual void setFireInterval(double interval)
    105     {
    106         ASSERT(m_sharedTimerFunction);
    107 
    108         // See BlinkPlatformImpl::setSharedTimerFireInterval for explanation of
    109         // why ceil is used in the interval calculation.
    110         int64 delay = static_cast<int64>(ceil(interval * 1000));
    111 
    112         if (delay < 0) {
    113             delay = 0;
    114             m_nextFireTime = 0.0;
    115         }
    116 
    117         m_running = true;
    118         m_nextFireTime = currentTime() + interval;
    119         m_workerThread->postDelayedTask(createSameThreadTask(&WorkerSharedTimer::OnTimeout, this), delay);
    120     }
    121 
    122     virtual void stop()
    123     {
    124         m_running = false;
    125     }
    126 
    127     double nextFireTime() { return m_nextFireTime; }
    128 
    129 private:
    130     void OnTimeout()
    131     {
    132         ASSERT(m_workerThread->workerGlobalScope());
    133         if (m_sharedTimerFunction && m_running && !m_workerThread->workerGlobalScope()->isClosing())
    134             m_sharedTimerFunction();
    135     }
    136 
    137     WorkerThread* m_workerThread;
    138     SharedTimerFunction m_sharedTimerFunction;
    139     double m_nextFireTime;
    140     bool m_running;
    141 };
    142 
    143 class WorkerThreadTask : public blink::WebThread::Task {
    144     WTF_MAKE_NONCOPYABLE(WorkerThreadTask); WTF_MAKE_FAST_ALLOCATED;
    145 public:
    146     static PassOwnPtr<WorkerThreadTask> create(const WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented)
    147     {
    148         return adoptPtr(new WorkerThreadTask(workerThread, task, isInstrumented));
    149     }
    150 
    151     virtual ~WorkerThreadTask() { }
    152 
    153     virtual void run() OVERRIDE
    154     {
    155         WorkerGlobalScope* workerGlobalScope = m_workerThread.workerGlobalScope();
    156         // Tasks could be put on the message loop after the cleanup task,
    157         // ensure none of those are ran.
    158         if (!workerGlobalScope)
    159             return;
    160 
    161         if (m_isInstrumented)
    162             InspectorInstrumentation::willPerformExecutionContextTask(workerGlobalScope, m_task.get());
    163         if ((!workerGlobalScope->isClosing() && !m_workerThread.terminated()) || m_task->isCleanupTask())
    164             m_task->performTask(workerGlobalScope);
    165         if (m_isInstrumented)
    166             InspectorInstrumentation::didPerformExecutionContextTask(workerGlobalScope);
    167     }
    168 
    169 private:
    170     WorkerThreadTask(const WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented)
    171         : m_workerThread(workerThread)
    172         , m_task(task)
    173         , m_isInstrumented(isInstrumented)
    174     {
    175         if (m_isInstrumented)
    176             m_isInstrumented = !m_task->taskNameForInstrumentation().isEmpty();
    177         if (m_isInstrumented)
    178             InspectorInstrumentation::didPostExecutionContextTask(m_workerThread.workerGlobalScope(), m_task.get());
    179     }
    180 
    181     const WorkerThread& m_workerThread;
    182     OwnPtr<ExecutionContextTask> m_task;
    183     bool m_isInstrumented;
    184 };
    185 
    186 class RunDebuggerQueueTask FINAL : public ExecutionContextTask {
    187 public:
    188     static PassOwnPtr<RunDebuggerQueueTask> create(WorkerThread* thread)
    189     {
    190         return adoptPtr(new RunDebuggerQueueTask(thread));
    191     }
    192     virtual void performTask(ExecutionContext* context) OVERRIDE
    193     {
    194         ASSERT(context->isWorkerGlobalScope());
    195         m_thread->runDebuggerTask(WorkerThread::DontWaitForMessage);
    196     }
    197 
    198 private:
    199     explicit RunDebuggerQueueTask(WorkerThread* thread) : m_thread(thread) { }
    200 
    201     WorkerThread* m_thread;
    202 };
    203 
    204 WorkerThread::WorkerThread(WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData)
    205     : m_terminated(false)
    206     , m_workerLoaderProxy(workerLoaderProxy)
    207     , m_workerReportingProxy(workerReportingProxy)
    208     , m_startupData(startupData)
    209     , m_shutdownEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
    210     , m_terminationEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
    211 {
    212     MutexLocker lock(threadSetMutex());
    213     workerThreads().add(this);
    214 }
    215 
    216 WorkerThread::~WorkerThread()
    217 {
    218     MutexLocker lock(threadSetMutex());
    219     ASSERT(workerThreads().contains(this));
    220     workerThreads().remove(this);
    221 }
    222 
    223 void WorkerThread::start()
    224 {
    225     if (m_thread)
    226         return;
    227 
    228     m_thread = WebThreadSupportingGC::create("WebCore: Worker");
    229     m_thread->postTask(new Task(WTF::bind(&WorkerThread::initialize, this)));
    230 }
    231 
    232 void WorkerThread::interruptAndDispatchInspectorCommands()
    233 {
    234     MutexLocker locker(m_workerInspectorControllerMutex);
    235     if (m_workerInspectorController)
    236         m_workerInspectorController->interruptAndDispatchInspectorCommands();
    237 }
    238 
    239 PlatformThreadId WorkerThread::platformThreadId() const
    240 {
    241     if (!m_thread)
    242         return 0;
    243     return m_thread->platformThread().threadId();
    244 }
    245 
    246 void WorkerThread::initialize()
    247 {
    248     KURL scriptURL = m_startupData->m_scriptURL;
    249     String sourceCode = m_startupData->m_sourceCode;
    250     WorkerThreadStartMode startMode = m_startupData->m_startMode;
    251 
    252     {
    253         MutexLocker lock(m_threadCreationMutex);
    254 
    255         // The worker was terminated before the thread had a chance to run.
    256         if (m_terminated) {
    257             // Notify the proxy that the WorkerGlobalScope has been disposed of.
    258             // This can free this thread object, hence it must not be touched afterwards.
    259             m_workerReportingProxy.workerThreadTerminated();
    260             return;
    261         }
    262 
    263         m_microtaskRunner = adoptPtr(new MicrotaskRunner);
    264         m_thread->addTaskObserver(m_microtaskRunner.get());
    265         m_thread->attachGC();
    266         m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release());
    267 
    268         m_sharedTimer = adoptPtr(new WorkerSharedTimer(this));
    269         PlatformThreadData::current().threadTimers().setSharedTimer(m_sharedTimer.get());
    270     }
    271 
    272     // The corresponding call to didStopWorkerRunLoop is in
    273     // ~WorkerScriptController.
    274     blink::Platform::current()->didStartWorkerRunLoop(blink::WebWorkerRunLoop(this));
    275 
    276     // Notify proxy that a new WorkerGlobalScope has been created and started.
    277     m_workerReportingProxy.workerGlobalScopeStarted(m_workerGlobalScope.get());
    278 
    279     WorkerScriptController* script = m_workerGlobalScope->script();
    280     if (!script->isExecutionForbidden())
    281         script->initializeContextIfNeeded();
    282     InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), startMode);
    283     script->evaluate(ScriptSourceCode(sourceCode, scriptURL));
    284 
    285     postInitialize();
    286 
    287     postDelayedTask(createSameThreadTask(&WorkerThread::idleHandler, this), kShortIdleHandlerDelayMs);
    288 }
    289 
    290 void WorkerThread::cleanup()
    291 {
    292 
    293     // This should be called before we start the shutdown procedure.
    294     workerReportingProxy().willDestroyWorkerGlobalScope();
    295 
    296     // The below assignment will destroy the context, which will in turn notify messaging proxy.
    297     // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them.
    298     // If Oilpan is enabled, we detach of the context/global scope, with the final heap cleanup below sweeping it out.
    299 #if !ENABLE(OILPAN)
    300     ASSERT(m_workerGlobalScope->hasOneRef());
    301 #endif
    302     m_workerGlobalScope->dispose();
    303     m_workerGlobalScope = nullptr;
    304 
    305     m_thread->detachGC();
    306 
    307     m_thread->removeTaskObserver(m_microtaskRunner.get());
    308     m_microtaskRunner = nullptr;
    309 
    310     // Notify the proxy that the WorkerGlobalScope has been disposed of.
    311     // This can free this thread object, hence it must not be touched afterwards.
    312     workerReportingProxy().workerThreadTerminated();
    313 
    314     m_terminationEvent->signal();
    315 
    316     // Clean up PlatformThreadData before WTF::WTFThreadData goes away!
    317     PlatformThreadData::current().destroy();
    318 }
    319 
    320 class WorkerThreadShutdownFinishTask : public ExecutionContextTask {
    321 public:
    322     static PassOwnPtr<WorkerThreadShutdownFinishTask> create()
    323     {
    324         return adoptPtr(new WorkerThreadShutdownFinishTask());
    325     }
    326 
    327     virtual void performTask(ExecutionContext *context)
    328     {
    329         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    330         workerGlobalScope->clearInspector();
    331         // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed.
    332         workerGlobalScope->clearScript();
    333         workerGlobalScope->thread()->m_thread->postTask(new Task(WTF::bind(&WorkerThread::cleanup, workerGlobalScope->thread())));
    334     }
    335 
    336     virtual bool isCleanupTask() const { return true; }
    337 };
    338 
    339 class WorkerThreadShutdownStartTask : public ExecutionContextTask {
    340 public:
    341     static PassOwnPtr<WorkerThreadShutdownStartTask> create()
    342     {
    343         return adoptPtr(new WorkerThreadShutdownStartTask());
    344     }
    345 
    346     virtual void performTask(ExecutionContext *context)
    347     {
    348         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    349         workerGlobalScope->stopFetch();
    350         workerGlobalScope->stopActiveDOMObjects();
    351         PlatformThreadData::current().threadTimers().setSharedTimer(nullptr);
    352 
    353         // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
    354         // which become dangling once Heap is destroyed.
    355         workerGlobalScope->removeAllEventListeners();
    356 
    357         // Stick a shutdown command at the end of the queue, so that we deal
    358         // with all the cleanup tasks the databases post first.
    359         workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create());
    360     }
    361 
    362     virtual bool isCleanupTask() const { return true; }
    363 };
    364 
    365 void WorkerThread::stop()
    366 {
    367     // Prevent the deadlock between GC and an attempt to stop a thread.
    368     ThreadState::SafePointScope safePointScope(ThreadState::HeapPointersOnStack);
    369     stopInternal();
    370 }
    371 
    372 void WorkerThread::stopInShutdownSequence()
    373 {
    374     stopInternal();
    375 }
    376 
    377 void WorkerThread::stopInternal()
    378 {
    379     // Protect against this method and initialize() racing each other.
    380     MutexLocker lock(m_threadCreationMutex);
    381 
    382     // If stop has already been called, just return.
    383     if (m_terminated)
    384         return;
    385     m_terminated = true;
    386 
    387     // Signal the thread to notify that the thread's stopping.
    388     if (m_shutdownEvent)
    389         m_shutdownEvent->signal();
    390 
    391     if (!m_workerGlobalScope)
    392         return;
    393 
    394     // 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.
    395     m_workerGlobalScope->script()->scheduleExecutionTermination();
    396     InspectorInstrumentation::didKillAllExecutionContextTasks(m_workerGlobalScope.get());
    397     m_debuggerMessageQueue.kill();
    398     postTask(WorkerThreadShutdownStartTask::create());
    399 }
    400 
    401 void WorkerThread::terminateAndWaitForAllWorkers()
    402 {
    403     // Keep this lock to prevent WorkerThread instances from being destroyed.
    404     MutexLocker lock(threadSetMutex());
    405     HashSet<WorkerThread*> threads = workerThreads();
    406     for (HashSet<WorkerThread*>::iterator itr = threads.begin(); itr != threads.end(); ++itr)
    407         (*itr)->stopInShutdownSequence();
    408 
    409     for (HashSet<WorkerThread*>::iterator itr = threads.begin(); itr != threads.end(); ++itr)
    410         (*itr)->terminationEvent()->wait();
    411 }
    412 
    413 bool WorkerThread::isCurrentThread() const
    414 {
    415     return m_thread && m_thread->isCurrentThread();
    416 }
    417 
    418 void WorkerThread::idleHandler()
    419 {
    420     ASSERT(m_workerGlobalScope.get());
    421     int64 delay = kLongIdleHandlerDelayMs;
    422 
    423     // Do a script engine idle notification if the next event is distant enough.
    424     const double kMinIdleTimespan = 0.3;
    425     if (m_sharedTimer->nextFireTime() == 0.0 || m_sharedTimer->nextFireTime() > currentTime() + kMinIdleTimespan) {
    426         bool hasMoreWork = !m_workerGlobalScope->idleNotification();
    427         if (hasMoreWork)
    428             delay = kShortIdleHandlerDelayMs;
    429     }
    430 
    431     postDelayedTask(createSameThreadTask(&WorkerThread::idleHandler, this), delay);
    432 }
    433 
    434 void WorkerThread::postTask(PassOwnPtr<ExecutionContextTask> task)
    435 {
    436     m_thread->postTask(WorkerThreadTask::create(*this, task, true).leakPtr());
    437 }
    438 
    439 void WorkerThread::postDelayedTask(PassOwnPtr<ExecutionContextTask> task, long long delayMs)
    440 {
    441     m_thread->postDelayedTask(WorkerThreadTask::create(*this, task, true).leakPtr(), delayMs);
    442 }
    443 
    444 void WorkerThread::postDebuggerTask(PassOwnPtr<ExecutionContextTask> task)
    445 {
    446     m_debuggerMessageQueue.append(WorkerThreadTask::create(*this, task, false));
    447     postTask(RunDebuggerQueueTask::create(this));
    448 }
    449 
    450 MessageQueueWaitResult WorkerThread::runDebuggerTask(WaitMode waitMode)
    451 {
    452     ASSERT(isCurrentThread());
    453     MessageQueueWaitResult result;
    454     double absoluteTime = MessageQueue<blink::WebThread::Task>::infiniteTime();
    455     OwnPtr<blink::WebThread::Task> task;
    456     {
    457         if (waitMode == DontWaitForMessage)
    458             absoluteTime = 0.0;
    459         ThreadState::SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack);
    460         task = m_debuggerMessageQueue.waitForMessageWithTimeout(result, absoluteTime);
    461     }
    462 
    463     if (result == MessageQueueMessageReceived) {
    464         InspectorInstrumentation::willProcessTask(workerGlobalScope());
    465         task->run();
    466         InspectorInstrumentation::didProcessTask(workerGlobalScope());
    467     }
    468 
    469     return result;
    470 }
    471 
    472 void WorkerThread::willEnterNestedLoop()
    473 {
    474     InspectorInstrumentation::willEnterNestedRunLoop(m_workerGlobalScope.get());
    475 }
    476 
    477 void WorkerThread::didLeaveNestedLoop()
    478 {
    479     InspectorInstrumentation::didLeaveNestedRunLoop(m_workerGlobalScope.get());
    480 }
    481 
    482 void WorkerThread::setWorkerInspectorController(WorkerInspectorController* workerInspectorController)
    483 {
    484     MutexLocker locker(m_workerInspectorControllerMutex);
    485     m_workerInspectorController = workerInspectorController;
    486 }
    487 
    488 } // namespace blink
    489