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 #if ENABLE(WORKERS) 30 31 #include "WorkerThread.h" 32 33 #include "DedicatedWorkerContext.h" 34 #include "KURL.h" 35 #include "PlatformString.h" 36 #include "ScriptSourceCode.h" 37 #include "ScriptValue.h" 38 #include "ThreadGlobalData.h" 39 40 #include <utility> 41 #include <wtf/Noncopyable.h> 42 43 #if ENABLE(DATABASE) 44 #include "DatabaseTask.h" 45 #include "DatabaseTracker.h" 46 #endif 47 48 namespace WebCore { 49 50 static Mutex& threadCountMutex() 51 { 52 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); 53 return mutex; 54 } 55 56 unsigned WorkerThread::m_threadCount = 0; 57 58 unsigned WorkerThread::workerThreadCount() 59 { 60 MutexLocker lock(threadCountMutex()); 61 return m_threadCount; 62 } 63 64 struct WorkerThreadStartupData { 65 WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); WTF_MAKE_FAST_ALLOCATED; 66 public: 67 static PassOwnPtr<WorkerThreadStartupData> create(const KURL& scriptURL, const String& userAgent, const String& sourceCode) 68 { 69 return new WorkerThreadStartupData(scriptURL, userAgent, sourceCode); 70 } 71 72 KURL m_scriptURL; 73 String m_userAgent; 74 String m_sourceCode; 75 private: 76 WorkerThreadStartupData(const KURL& scriptURL, const String& userAgent, const String& sourceCode); 77 }; 78 79 WorkerThreadStartupData::WorkerThreadStartupData(const KURL& scriptURL, const String& userAgent, const String& sourceCode) 80 : m_scriptURL(scriptURL.copy()) 81 , m_userAgent(userAgent.crossThreadString()) 82 , m_sourceCode(sourceCode.crossThreadString()) 83 { 84 } 85 86 WorkerThread::WorkerThread(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy) 87 : m_threadID(0) 88 , m_workerLoaderProxy(workerLoaderProxy) 89 , m_workerReportingProxy(workerReportingProxy) 90 , m_startupData(WorkerThreadStartupData::create(scriptURL, userAgent, sourceCode)) 91 { 92 MutexLocker lock(threadCountMutex()); 93 m_threadCount++; 94 } 95 96 WorkerThread::~WorkerThread() 97 { 98 MutexLocker lock(threadCountMutex()); 99 ASSERT(m_threadCount > 0); 100 m_threadCount--; 101 } 102 103 bool WorkerThread::start() 104 { 105 // Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts. 106 MutexLocker lock(m_threadCreationMutex); 107 108 if (m_threadID) 109 return true; 110 111 m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore: Worker"); 112 113 return m_threadID; 114 } 115 116 void* WorkerThread::workerThreadStart(void* thread) 117 { 118 return static_cast<WorkerThread*>(thread)->workerThread(); 119 } 120 121 void* WorkerThread::workerThread() 122 { 123 { 124 MutexLocker lock(m_threadCreationMutex); 125 m_workerContext = createWorkerContext(m_startupData->m_scriptURL, m_startupData->m_userAgent); 126 127 if (m_runLoop.terminated()) { 128 // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet, 129 // forbidExecution() couldn't be called from stop(). 130 m_workerContext->script()->forbidExecution(); 131 } 132 } 133 134 WorkerScriptController* script = m_workerContext->script(); 135 script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL)); 136 // Free the startup data to cause its member variable deref's happen on the worker's thread (since 137 // all ref/derefs of these objects are happening on the thread at this point). Note that 138 // WorkerThread::~WorkerThread happens on a different thread where it was created. 139 m_startupData.clear(); 140 141 runEventLoop(); 142 143 ThreadIdentifier threadID = m_threadID; 144 145 ASSERT(m_workerContext->hasOneRef()); 146 147 // The below assignment will destroy the context, which will in turn notify messaging proxy. 148 // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. 149 m_workerContext = 0; 150 151 // Clean up WebCore::ThreadGlobalData before WTF::WTFThreadData goes away! 152 threadGlobalData().destroy(); 153 154 // The thread object may be already destroyed from notification now, don't try to access "this". 155 detachThread(threadID); 156 157 return 0; 158 } 159 160 void WorkerThread::runEventLoop() 161 { 162 // Does not return until terminated. 163 m_runLoop.run(m_workerContext.get()); 164 } 165 166 class WorkerThreadShutdownFinishTask : public ScriptExecutionContext::Task { 167 public: 168 static PassOwnPtr<WorkerThreadShutdownFinishTask> create() 169 { 170 return new WorkerThreadShutdownFinishTask(); 171 } 172 173 virtual void performTask(ScriptExecutionContext *context) 174 { 175 ASSERT(context->isWorkerContext()); 176 WorkerContext* workerContext = static_cast<WorkerContext*>(context); 177 // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. 178 workerContext->clearScript(); 179 workerContext->thread()->runLoop().terminate(); 180 } 181 182 virtual bool isCleanupTask() const { return true; } 183 }; 184 185 class WorkerThreadShutdownStartTask : public ScriptExecutionContext::Task { 186 public: 187 static PassOwnPtr<WorkerThreadShutdownStartTask> create() 188 { 189 return new WorkerThreadShutdownStartTask(); 190 } 191 192 virtual void performTask(ScriptExecutionContext *context) 193 { 194 ASSERT(context->isWorkerContext()); 195 WorkerContext* workerContext = static_cast<WorkerContext*>(context); 196 197 #if ENABLE(DATABASE) 198 DatabaseTaskSynchronizer cleanupSync; 199 workerContext->stopDatabases(&cleanupSync); 200 #endif 201 202 workerContext->stopActiveDOMObjects(); 203 204 workerContext->notifyObserversOfStop(); 205 206 // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, 207 // which become dangling once Heap is destroyed. 208 workerContext->removeAllEventListeners(); 209 210 #if ENABLE(DATABASE) 211 // We wait for the database thread to clean up all its stuff so that we 212 // can do more stringent leak checks as we exit. 213 cleanupSync.waitForTaskCompletion(); 214 #endif 215 216 // Stick a shutdown command at the end of the queue, so that we deal 217 // with all the cleanup tasks the databases post first. 218 workerContext->postTask(WorkerThreadShutdownFinishTask::create()); 219 } 220 221 virtual bool isCleanupTask() const { return true; } 222 }; 223 224 void WorkerThread::stop() 225 { 226 // Mutex protection is necessary because stop() can be called before the context is fully created. 227 MutexLocker lock(m_threadCreationMutex); 228 229 // 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. 230 if (m_workerContext) { 231 m_workerContext->script()->scheduleExecutionTermination(); 232 233 #if ENABLE(DATABASE) 234 DatabaseTracker::tracker().interruptAllDatabasesForContext(m_workerContext.get()); 235 #endif 236 237 // FIXME: Rudely killing the thread won't work when we allow nested workers, because they will try to post notifications of their destruction. 238 // This can likely use the same mechanism as used for databases above. 239 240 m_runLoop.postTask(WorkerThreadShutdownStartTask::create()); 241 } else 242 m_runLoop.terminate(); 243 } 244 245 } // namespace WebCore 246 247 #endif // ENABLE(WORKERS) 248