#include "config.h"
#if ENABLE(WORKERS)
#include "WorkerThread.h"
#include "DedicatedWorkerContext.h"
#include "KURL.h"
#include "PlatformString.h"
#include "ScriptSourceCode.h"
#include "ScriptValue.h"
#include "ThreadGlobalData.h"
#include <utility>
#include <wtf/Noncopyable.h>
#if ENABLE(DATABASE)
#include "DatabaseTask.h"
#include "DatabaseTracker.h"
#endif
namespace WebCore {
static Mutex& threadCountMutex()
{
AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
return mutex;
}
unsigned WorkerThread::m_threadCount = 0;
unsigned WorkerThread::workerThreadCount()
{
MutexLocker lock(threadCountMutex());
return m_threadCount;
}
struct WorkerThreadStartupData {
WTF_MAKE_NONCOPYABLE(WorkerThreadStartupData); WTF_MAKE_FAST_ALLOCATED;
public:
static PassOwnPtr<WorkerThreadStartupData> create(const KURL& scriptURL, const String& userAgent, const String& sourceCode)
{
return adoptPtr(new WorkerThreadStartupData(scriptURL, userAgent, sourceCode));
}
KURL m_scriptURL;
String m_userAgent;
String m_sourceCode;
private:
WorkerThreadStartupData(const KURL& scriptURL, const String& userAgent, const String& sourceCode);
};
WorkerThreadStartupData::WorkerThreadStartupData(const KURL& scriptURL, const String& userAgent, const String& sourceCode)
: m_scriptURL(scriptURL.copy())
, m_userAgent(userAgent.isolatedCopy())
, m_sourceCode(sourceCode.isolatedCopy())
{
}
WorkerThread::WorkerThread(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy)
: m_threadID(0)
, m_workerLoaderProxy(workerLoaderProxy)
, m_workerReportingProxy(workerReportingProxy)
, m_startupData(WorkerThreadStartupData::create(scriptURL, userAgent, sourceCode))
{
MutexLocker lock(threadCountMutex());
m_threadCount++;
}
WorkerThread::~WorkerThread()
{
MutexLocker lock(threadCountMutex());
ASSERT(m_threadCount > 0);
m_threadCount--;
}
bool WorkerThread::start()
{
MutexLocker lock(m_threadCreationMutex);
if (m_threadID)
return true;
m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore: Worker");
return m_threadID;
}
void* WorkerThread::workerThreadStart(void* thread)
{
return static_cast<WorkerThread*>(thread)->workerThread();
}
void* WorkerThread::workerThread()
{
{
MutexLocker lock(m_threadCreationMutex);
m_workerContext = createWorkerContext(m_startupData->m_scriptURL, m_startupData->m_userAgent);
if (m_runLoop.terminated()) {
m_workerContext->script()->forbidExecution();
}
}
WorkerScriptController* script = m_workerContext->script();
script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL));
m_startupData.clear();
runEventLoop();
ThreadIdentifier threadID = m_threadID;
ASSERT(m_workerContext->hasOneRef());
m_workerContext = 0;
threadGlobalData().destroy();
detachThread(threadID);
return 0;
}
void WorkerThread::runEventLoop()
{
m_runLoop.run(m_workerContext.get());
}
class WorkerThreadShutdownFinishTask : public ScriptExecutionContext::Task {
public:
static PassOwnPtr<WorkerThreadShutdownFinishTask> create()
{
return adoptPtr(new WorkerThreadShutdownFinishTask());
}
virtual void performTask(ScriptExecutionContext *context)
{
ASSERT(context->isWorkerContext());
WorkerContext* workerContext = static_cast<WorkerContext*>(context);
workerContext->clearScript();
}
virtual bool isCleanupTask() const { return true; }
};
class WorkerThreadShutdownStartTask : public ScriptExecutionContext::Task {
public:
static PassOwnPtr<WorkerThreadShutdownStartTask> create()
{
return adoptPtr(new WorkerThreadShutdownStartTask());
}
virtual void performTask(ScriptExecutionContext *context)
{
ASSERT(context->isWorkerContext());
WorkerContext* workerContext = static_cast<WorkerContext*>(context);
#if ENABLE(DATABASE)
DatabaseTaskSynchronizer cleanupSync;
workerContext->stopDatabases(&cleanupSync);
#endif
workerContext->stopActiveDOMObjects();
workerContext->notifyObserversOfStop();
workerContext->removeAllEventListeners();
#if ENABLE(DATABASE)
cleanupSync.waitForTaskCompletion();
#endif
workerContext->postTask(WorkerThreadShutdownFinishTask::create());
}
virtual bool isCleanupTask() const { return true; }
};
void WorkerThread::stop()
{
MutexLocker lock(m_threadCreationMutex);
if (m_workerContext) {
m_workerContext->script()->scheduleExecutionTermination();
#if ENABLE(DATABASE)
DatabaseTracker::tracker().interruptAllDatabasesForContext(m_workerContext.get());
#endif
m_runLoop.postTask(WorkerThreadShutdownStartTask::create());
}
m_runLoop.terminate();
}
}
#endif // ENABLE(WORKERS)