Bug 437152 - "implement worker threads". r+sr=jst.

This commit is contained in:
Ben Turner 2008-08-16 18:25:01 -07:00
Родитель 8e8f112b0a
Коммит 8f2cbea28f
27 изменённых файлов: 4416 добавлений и 2 удалений

Просмотреть файл

@ -60,7 +60,8 @@ DIRS = \
storage \
json \
offline \
geolocation
geolocation \
threads
ifdef MOZ_SVG
DIRS += svg

Просмотреть файл

@ -39,6 +39,8 @@
#include "domstubs.idl"
interface nsIDOMWorkerPool;
[scriptable, uuid(8e150a70-3e51-45df-bee3-77505fbe016c)]
interface nsIDOMNavigator : nsISupports
{
@ -63,6 +65,8 @@ interface nsIDOMNavigator : nsISupports
boolean javaEnabled();
boolean taintEnabled();
nsIDOMWorkerPool newWorkerPool();
// XXX This one's tough, would nsISupports preference(in DOMString
// pref /*, ... */); work?

Просмотреть файл

@ -0,0 +1,52 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
# Ben Turner <bent.mozilla@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
XPIDL_MODULE = dom_threads
GRE_MODULE = 1
XPIDLSRCS = nsIDOMThreads.idl
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,140 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIScriptError;
[scriptable, function, uuid(e50ca05d-1381-4abb-a021-02eb720cfc38)]
interface nsIDOMWorkerMessageListener : nsISupports
{
/**
* An nsIDOMWorkerThread receives the onMessage callback when another
* worker posts a message to it.
*
* @param aMessage (in DOMString)
* The message sent from another worker.
* @param aSource (in nsISupports)
* The worker that sent the message. Useful for a quick response.
*/
void onMessage(in DOMString aMessage,
in nsISupports aSource);
};
[scriptable, function, uuid(9df8422e-25dd-43f4-b9b9-709f9e074647)]
interface nsIDOMWorkerErrorListener : nsISupports
{
/**
* An nsIDOMWorkerPool receives the onError callback when one of its child
* workers has a parse error or an unhandled exception.
*
* @param aMessage (in nsIScriptError)
* Details about the error that occurred. See nsIScriptError.
* @param aSource (in nsISupports)
* The worker that sent the message. Depending on the specific error in
* question it may not be possible to use this object (in the case of a
* parse error, for instance, aSource will be unusable).
*/
void onError(in nsIScriptError aError,
in nsISupports aSource);
};
[scriptable, uuid(6f19f3ff-2aaa-4504-9b71-dca3c191efed)]
interface nsIDOMWorkerThread : nsISupports
{
/**
* Sends a message to the worker.
*
* @param aMessage (in DOMString)
* The message to send.
*/
void postMessage(in DOMString aMessage);
};
[scriptable, uuid(45312e93-8a3e-4493-9bd9-272a6c23a16c)]
interface nsIDOMWorkerPool : nsISupports
{
/**
* Sends a message to the pool.
*
* @param aMessage (in DOMString)
* The message to send..
*/
void postMessage(in DOMString aMessage);
/**
* The nsIDOMWorkerMessageListener which handles messages for this worker.
*
* Developers should set this attribute to a proper object before another
* worker begins sending messages to ensure that all messages are received.
*/
attribute nsIDOMWorkerMessageListener messageListener;
/**
* The nsIDOMWorkerErrorListener which handles errors in child threads.
*
* Developers should set this attribute to a proper object before calling
* createWorker in order to catch parse errors as well as runtime exceptions.
*/
attribute nsIDOMWorkerErrorListener errorListener;
/**
* Create a new worker object by evaluating the given script.
*
* @param aSourceScript (in DOMString)
* The script to compile. See below for details on the scope in which
* the script will run.
*/
nsIDOMWorkerThread createWorker(in DOMString aSourceScript);
};
[scriptable, uuid(0f2a52ea-afc9-49e6-86dd-2d0cb65b5dd5)]
interface nsIDOMThreadService : nsISupports
{
/**
* Creates a new DOM worker pool.
*/
nsIDOMWorkerPool createPool();
};
[scriptable, uuid(fcf387be-a7e3-4283-8bc5-06bfe13c5e8c)]
interface nsIDOMWorkerThreadContext : nsISupports
{
readonly attribute nsIDOMWorkerThread thisThread;
};

Просмотреть файл

@ -42,7 +42,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = base jsurl events storage offline json geolocation
DIRS = base jsurl events storage offline json geolocation threads
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -125,6 +125,7 @@ LOCAL_INCLUDES = \
-I$(srcdir)/../storage \
-I$(srcdir)/../offline \
-I$(srcdir)/../geolocation \
-I$(srcdir)/../threads \
-I$(topsrcdir)/content/xbl/src \
-I$(topsrcdir)/content/xul/document/src \
-I$(topsrcdir)/content/events/src \

Просмотреть файл

@ -82,6 +82,7 @@
#include "nsLayoutStatics.h"
#include "nsCycleCollector.h"
#include "nsCCUncollectableMarker.h"
#include "nsDOMThreadService.h"
// Interfaces Needed
#include "nsIWidget.h"
@ -113,6 +114,7 @@
#include "nsIDOMPkcs11.h"
#include "nsIDOMOfflineResourceList.h"
#include "nsIDOMGeoGeolocation.h"
#include "nsIDOMThreads.h"
#include "nsDOMString.h"
#include "nsIEmbeddingSiteWindow2.h"
#include "nsThreadUtils.h"
@ -862,6 +864,12 @@ nsGlobalWindow::FreeInnerObjects(PRBool aClearScope)
{
NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
// Kill all of the workers for this window.
nsDOMThreadService* dts = nsDOMThreadService::get();
if (dts) {
dts->CancelWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
}
ClearAllTimeouts();
mChromeEventHandler = nsnull;
@ -8566,6 +8574,11 @@ nsGlobalWindow::SuspendTimeouts()
{
FORWARD_TO_INNER_VOID(SuspendTimeouts, ());
nsDOMThreadService* dts = nsDOMThreadService::get();
if (dts) {
dts->SuspendWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
}
PRTime now = PR_Now();
for (nsTimeout *t = FirstTimeout(); IsTimeout(t); t = t->Next()) {
// Change mWhen to be the time remaining for this timer.
@ -8621,6 +8634,11 @@ nsGlobalWindow::ResumeTimeouts()
{
FORWARD_TO_INNER(ResumeTimeouts, (), NS_ERROR_NOT_INITIALIZED);
nsDOMThreadService* dts = nsDOMThreadService::get();
if (dts) {
dts->ResumeWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
}
// Restore all of the timeouts, using the stored time remaining
// (stored in timeout->mWhen).
@ -9725,3 +9743,18 @@ NS_IMETHODIMP nsNavigator::GetGeolocation(nsIDOMGeoGeolocation **_retval)
return NS_OK;
}
NS_IMETHODIMP
nsNavigator::NewWorkerPool(nsIDOMWorkerPool** _retval)
{
nsresult rv;
nsCOMPtr<nsIDOMThreadService> threadService =
nsDOMThreadService::GetOrInitService();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMWorkerPool> newPool;
rv = threadService->CreatePool(getter_AddRefs(newPool));
NS_ENSURE_SUCCESS(rv, rv);
newPool.forget(_retval);
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,80 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is worker threads.
#
# The Initial Developer of the Original Code is
# Mozilla Corporation
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
# Ben Turner <bent.mozilla@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
LIBRARY_NAME = domthreads_s
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
REQUIRES = \
caps \
content \
js \
layout \
pref \
string \
widget \
xpcom \
xpconnect \
$(NULL)
CPPSRCS = \
nsDOMThreadService.cpp \
nsDOMWorkerBase.cpp \
nsDOMWorkerPool.cpp \
nsDOMWorkerSecurityManager.cpp \
nsDOMWorkerThread.cpp \
nsDOMWorkerTimeout.cpp \
$(NULL)
LOCAL_INCLUDES = \
-I$(topsrcdir)/dom/src/base \
$(NULL)
ifdef MOZ_MOCHITEST
DIRS += test
endif
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,992 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsDOMThreadService.h"
// Interfaces
#include "nsIComponentManager.h"
#include "nsIConsoleService.h"
#include "nsIEventTarget.h"
#include "nsIGenericFactory.h"
#include "nsIJSContextStack.h"
#include "nsIJSRuntimeService.h"
#include "nsIObserverService.h"
#include "nsIScriptError.h"
#include "nsIScriptGlobalObject.h"
#include "nsIServiceManager.h"
#include "nsISupportsPriority.h"
#include "nsIThreadPool.h"
#include "nsIXPConnect.h"
// Other includes
#include "nsAutoLock.h"
#include "nsAutoPtr.h"
#include "nsContentUtils.h"
#include "nsDeque.h"
#include "nsIClassInfoImpl.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#include "nsXPCOMCID.h"
#include "nsXPCOMCIDInternal.h"
#include "pratom.h"
#include "prthread.h"
// DOMWorker includes
#include "nsDOMWorkerPool.h"
#include "nsDOMWorkerSecurityManager.h"
#include "nsDOMWorkerTimeout.h"
#ifdef PR_LOGGING
PRLogModuleInfo *gDOMThreadsLog = nsnull;
#endif
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
// The maximum number of threads in the internal thread pool
#define THREADPOOL_MAX_THREADS 3
PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= 1);
// The maximum number of idle threads in the internal thread pool
#define THREADPOOL_IDLE_THREADS 3
PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= THREADPOOL_IDLE_THREADS);
// The number of times our JS operation callback will be called before yielding
// the thread
#define CALLBACK_YIELD_THRESHOLD 100
// A "bad" value for the NSPR TLS functions.
#define BAD_TLS_INDEX (PRUintn)-1
// Don't know why nsISupports.idl defines this out...
#define NS_FORWARD_NSISUPPORTS(_to) \
NS_IMETHOD QueryInterface(const nsIID& uuid, void** result) { \
return _to QueryInterface(uuid, result); \
} \
NS_IMETHOD_(nsrefcnt) AddRef(void) { return _to AddRef(); } \
NS_IMETHOD_(nsrefcnt) Release(void) { return _to Release(); }
// Easy access for static functions. No reference here.
static nsDOMThreadService* gDOMThreadService = nsnull;
// These pointers actually carry references and must be released.
static nsIObserverService* gObserverService = nsnull;
static nsIJSRuntimeService* gJSRuntimeService = nsnull;
static nsIThreadJSContextStack* gThreadJSContextStack = nsnull;
static nsIXPCSecurityManager* gWorkerSecurityManager = nsnull;
PRUintn gJSContextIndex = BAD_TLS_INDEX;
/**
* Simple class to automatically destroy a JSContext to make error handling
* easier.
*/
class JSAutoContextDestroyer
{
public:
JSAutoContextDestroyer(JSContext* aCx)
: mCx(aCx) { }
~JSAutoContextDestroyer() {
if (mCx) {
nsContentUtils::XPConnect()->ReleaseJSContext(mCx, PR_TRUE);
}
}
operator JSContext*() {
return mCx;
}
JSContext* forget() {
JSContext* cx = mCx;
mCx = nsnull;
return cx;
}
private:
JSContext* mCx;
};
/**
* This class is used as to post an error to the main thread. It logs the error
* to the console and calls the pool's onError callback.
*/
class nsReportErrorRunnable : public nsRunnable
{
public:
nsReportErrorRunnable(nsIScriptError* aError, nsDOMWorkerThread* aWorker)
: mError(aError), mWorker(aWorker) { }
NS_IMETHOD Run() {
nsresult rv;
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
consoleService->LogMessage(mError);
}
if (!mWorker->IsCanceled()) {
#ifdef PR_LOGGING
nsAutoString message;
mError->GetErrorMessage(message);
#endif
nsRefPtr<nsDOMWorkerPool> pool = mWorker->Pool();
LOG(("Posting error '%s' to pool [0x%p]",
NS_LossyConvertUTF16toASCII(message).get(),
static_cast<void*>(pool.get())));
pool->HandleError(mError, mWorker);
}
return NS_OK;
}
private:
// XXX Maybe this should be an nsIException...
nsCOMPtr<nsIScriptError> mError;
// Have to carry a strong ref since this is used as a parameter to the
// onError callback.
nsRefPtr<nsDOMWorkerThread> mWorker;
};
/**
* Need this to expose an nsIScriptError to content JS (implement nsIClassInfo
* with DOM_OBJECT flag set.
*/
class nsDOMWorkerScriptError : public nsIClassInfo
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICLASSINFO
nsDOMWorkerScriptError(nsIScriptError* aError)
: mScriptError(this, aError) { }
protected:
// Lame, nsIScriptError and nsIClassInfo both have 'readonly attribute
// unsigned long flags' so we have to use an inner class to do this the
// right way...
class InnerScriptError : public nsIScriptError
{
public:
NS_FORWARD_NSISUPPORTS(mParent->)
NS_FORWARD_NSISCRIPTERROR(mError->)
NS_FORWARD_NSICONSOLEMESSAGE(mError->)
InnerScriptError(nsDOMWorkerScriptError* aParent, nsIScriptError* aError)
: mParent(aParent), mError(aError) { }
protected:
nsDOMWorkerScriptError* mParent;
nsCOMPtr<nsIScriptError> mError;
};
InnerScriptError mScriptError;
};
NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerScriptError)
NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerScriptError)
// More hoops to jump through for the identical IDL methods
NS_INTERFACE_MAP_BEGIN(nsDOMWorkerScriptError)
if (aIID.Equals(NS_GET_IID(nsIScriptError)) ||
aIID.Equals(NS_GET_IID(nsIConsoleMessage))) {
foundInterface = static_cast<nsIConsoleMessage*>(&mScriptError);
}
else
NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerScriptError, nsIScriptError,
nsIConsoleMessage)
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerScriptError)
/**
* Used to post an expired timeout to the correct worker.
*/
class nsDOMWorkerTimeoutRunnable : public nsRunnable
{
public:
nsDOMWorkerTimeoutRunnable(nsDOMWorkerTimeout* aTimeout)
: mTimeout(aTimeout) { }
NS_IMETHOD Run() {
return mTimeout->Run();
}
protected:
nsRefPtr<nsDOMWorkerTimeout> mTimeout;
};
/**
* This class exists to solve a particular problem: Calling Dispatch on a
* thread pool will always create a new thread to service the runnable as long
* as the thread limit has not been reached. Since our DOM workers can only be
* accessed by one thread at a time we could end up spawning a new thread that
* does nothing but wait initially. There is no way to control this behavior
* currently so we cheat by using a runnable that emulates a thread. The
* nsDOMThreadService's monitor protects the queue of events.
*/
class nsDOMWorkerRunnable : public nsRunnable
{
friend class nsDOMThreadService;
public:
nsDOMWorkerRunnable(nsDOMWorkerThread* aWorker)
: mWorker(aWorker) { }
virtual ~nsDOMWorkerRunnable() {
nsCOMPtr<nsIRunnable> runnable;
while ((runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront()))) {
// Loop until all the runnables are dead.
}
// Only release mWorker on the main thread!
nsDOMWorkerThread* worker = nsnull;
mWorker.swap(worker);
nsISupports* supports = NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, worker);
NS_ASSERTION(supports, "This should never be null!");
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
NS_ProxyRelease(mainThread, supports);
}
void PutRunnable(nsIRunnable* aRunnable) {
NS_ASSERTION(aRunnable, "Null pointer!");
NS_ADDREF(aRunnable);
// No need to enter the monitor because we should already be in it.
mRunnables.Push(aRunnable);
}
NS_IMETHOD Run() {
// This must have been set up by the thread service
NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
// Make sure we have a JSContext to run everything on.
JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
NS_ASSERTION(cx, "nsDOMThreadService didn't give us a context!");
JS_SetContextPrivate(cx, mWorker);
// Tell the worker which context it will be using
if (mWorker->SetGlobalForContext(cx)) {
RunQueue();
// Remove the global object from the context so that it might be garbage
// collected.
JS_SetGlobalObject(cx, NULL);
JS_SetContextPrivate(cx, NULL);
}
else {
// This is usually due to a parse error in the worker script...
JS_SetGlobalObject(cx, NULL);
nsAutoMonitor mon(gDOMThreadService->mMonitor);
gDOMThreadService->WorkerComplete(this);
mon.NotifyAll();
}
return NS_OK;
}
protected:
void RunQueue() {
while (1) {
nsCOMPtr<nsIRunnable> runnable;
{
nsAutoMonitor mon(gDOMThreadService->mMonitor);
runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront());
if (!runnable || mWorker->IsCanceled()) {
#ifdef PR_LOGGING
if (mWorker->IsCanceled()) {
LOG(("Bailing out of run loop for canceled worker[0x%p]",
static_cast<void*>(mWorker.get())));
}
#endif
gDOMThreadService->WorkerComplete(this);
mon.NotifyAll();
return;
}
}
#ifdef DEBUG
nsresult rv =
#endif
runnable->Run();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Runnable failed!");
}
}
// Set at construction
nsRefPtr<nsDOMWorkerThread> mWorker;
// Protected by mMonitor
nsDeque mRunnables;
};
/*******************************************************************************
* JS environment function and callbacks
*/
JSBool JS_DLL_CALLBACK
DOMWorkerOperationCallback(JSContext* aCx)
{
nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
// Want a strong ref here to make sure that the monitor we wait on won't go
// away.
nsRefPtr<nsDOMWorkerPool> pool;
PRBool wasSuspended = PR_FALSE;
jsrefcount suspendDepth = 0;
while (1) {
// Kill execution if we're canceled.
if (worker->IsCanceled()) {
LOG(("Forcefully killing JS for worker [0x%p]",
static_cast<void*>(worker)));
if (wasSuspended) {
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
JS_ResumeRequest(aCx, suspendDepth);
}
// Kill exectuion of the currently running JS.
return PR_FALSE;
}
// Break out if we're not suspended.
if (!worker->IsSuspended()) {
if (wasSuspended) {
gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
JS_ResumeRequest(aCx, suspendDepth);
}
break;
}
if (!wasSuspended) {
// Make sure we can get the monitor we need to wait on. It's possible that
// the worker was canceled since we checked above.
if (worker->IsCanceled()) {
NS_WARNING("Tried to suspend on a pool that has gone away");
return PR_FALSE;
}
pool = worker->Pool();
// Make sure to suspend our request while we block like this, otherwise we
// prevent GC for everyone.
suspendDepth = JS_SuspendRequest(aCx);
// Since we're going to block this thread we should open up a new thread
// in the thread pool for other workers.
gDOMThreadService->ChangeThreadPoolMaxThreads(1);
// Only do all this setup once.
wasSuspended = PR_TRUE;
}
nsAutoMonitor mon(pool->Monitor());
mon.Wait();
}
// Since only one thread can access a context at once we don't have to worry
// about atomically incrementing this counter
if (++worker->mCallbackCount >= CALLBACK_YIELD_THRESHOLD) {
// Must call this so that GC can happen on the main thread!
JS_YieldRequest(aCx);
// Start the counter over.
worker->mCallbackCount = 0;
}
// Continue execution.
return JS_TRUE;
}
void JS_DLL_CALLBACK
DOMWorkerErrorReporter(JSContext* aCx,
const char* aMessage,
JSErrorReport* aReport)
{
NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
nsDOMWorkerThread* worker = (nsDOMWorkerThread*)JS_GetContextPrivate(aCx);
nsresult rv;
nsCOMPtr<nsIScriptError> errorObject =
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,);
const PRUnichar* message =
reinterpret_cast<const PRUnichar*>(aReport->ucmessage);
nsAutoString filename;
filename.AssignWithConversion(aReport->filename);
const PRUnichar* line =
reinterpret_cast<const PRUnichar*>(aReport->uclinebuf);
PRUint32 column = aReport->uctokenptr - aReport->uclinebuf;
rv = errorObject->Init(message, filename.get(), line, aReport->lineno,
column, aReport->flags, "DOM Worker javascript");
NS_ENSURE_SUCCESS(rv,);
nsRefPtr<nsDOMWorkerScriptError> domError =
new nsDOMWorkerScriptError(errorObject);
NS_ENSURE_TRUE(domError,);
nsCOMPtr<nsIScriptError> scriptError(do_QueryInterface(domError));
NS_ENSURE_TRUE(scriptError,);
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
NS_ENSURE_TRUE(mainThread,);
nsCOMPtr<nsIRunnable> runnable =
new nsReportErrorRunnable(scriptError, worker);
NS_ENSURE_TRUE(runnable,);
rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv,);
}
/*******************************************************************************
* nsDOMThreadService
*/
nsDOMThreadService::nsDOMThreadService()
: mMonitor(nsnull)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
#ifdef PR_LOGGING
if (!gDOMThreadsLog) {
gDOMThreadsLog = PR_NewLogModule("nsDOMThreads");
}
#endif
LOG(("Initializing DOM Thread service"));
}
nsDOMThreadService::~nsDOMThreadService()
{
LOG(("DOM Thread service destroyed"));
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Cleanup();
if (mMonitor) {
nsAutoMonitor::DestroyMonitor(mMonitor);
}
}
NS_IMPL_THREADSAFE_ISUPPORTS4(nsDOMThreadService, nsIEventTarget,
nsIObserver,
nsIThreadPoolListener,
nsIDOMThreadService)
nsresult
nsDOMThreadService::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gDOMThreadService, "Only one instance should ever be created!");
nsresult rv;
nsCOMPtr<nsIObserverService> obs =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
obs.forget(&gObserverService);
mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mThreadPool->SetListener(this);
NS_ENSURE_SUCCESS(rv, rv);
rv = mThreadPool->SetThreadLimit(THREADPOOL_MAX_THREADS);
NS_ENSURE_SUCCESS(rv, rv);
rv = mThreadPool->SetIdleThreadLimit(THREADPOOL_IDLE_THREADS);
NS_ENSURE_SUCCESS(rv, rv);
mMonitor = nsAutoMonitor::NewMonitor("nsDOMThreadService::mMonitor");
NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
PRBool success = mWorkersInProgress.Init();
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIJSRuntimeService>
runtimeSvc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
NS_ENSURE_TRUE(runtimeSvc, NS_ERROR_FAILURE);
runtimeSvc.forget(&gJSRuntimeService);
nsCOMPtr<nsIThreadJSContextStack>
contextStack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
NS_ENSURE_TRUE(contextStack, NS_ERROR_FAILURE);
contextStack.forget(&gThreadJSContextStack);
nsCOMPtr<nsIXPCSecurityManager> secMan(new nsDOMWorkerSecurityManager());
NS_ENSURE_TRUE(secMan, NS_ERROR_OUT_OF_MEMORY);
secMan.forget(&gWorkerSecurityManager);
if (gJSContextIndex == BAD_TLS_INDEX &&
PR_NewThreadPrivateIndex(&gJSContextIndex, NULL) != PR_SUCCESS) {
NS_ERROR("PR_NewThreadPrivateIndex failed!");
gJSContextIndex = BAD_TLS_INDEX;
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* static */
already_AddRefed<nsIDOMThreadService>
nsDOMThreadService::GetOrInitService()
{
if (!gDOMThreadService) {
nsRefPtr<nsDOMThreadService> service = new nsDOMThreadService();
NS_ENSURE_TRUE(service, nsnull);
nsresult rv = service->Init();
NS_ENSURE_SUCCESS(rv, nsnull);
service.swap(gDOMThreadService);
}
nsCOMPtr<nsIDOMThreadService> service(gDOMThreadService);
return service.forget();
}
/* static */
nsDOMThreadService*
nsDOMThreadService::get()
{
return gDOMThreadService;
}
/* static */
void
nsDOMThreadService::Shutdown()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_IF_RELEASE(gDOMThreadService);
}
void
nsDOMThreadService::Cleanup()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// This will either be called at 'xpcom-shutdown' or earlier if the call to
// Init fails somehow. We can therefore assume that all services will still
// be available here.
if (gObserverService) {
gObserverService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
NS_RELEASE(gObserverService);
}
// The thread pool holds a circular reference to this service through its
// listener. We must shut down the thread pool manually to break this cycle.
if (mThreadPool) {
mThreadPool->Shutdown();
mThreadPool = nsnull;
}
// Need to force a GC so that all of our workers get cleaned up.
if (gThreadJSContextStack) {
JSContext* safeContext;
if (NS_SUCCEEDED(gThreadJSContextStack->GetSafeJSContext(&safeContext))) {
JS_GC(safeContext);
}
NS_RELEASE(gThreadJSContextStack);
}
// These must be released after the thread pool is shut down.
NS_IF_RELEASE(gJSRuntimeService);
NS_IF_RELEASE(gWorkerSecurityManager);
}
nsresult
nsDOMThreadService::Dispatch(nsDOMWorkerThread* aWorker,
nsIRunnable* aRunnable)
{
NS_ASSERTION(aWorker, "Null pointer!");
NS_ASSERTION(aRunnable, "Null pointer!");
NS_ASSERTION(mThreadPool, "Dispatch called after 'xpcom-shutdown'!");
if (aWorker->IsCanceled()) {
LOG(("Will not dispatch runnable [0x%p] for canceled worker [0x%p]",
static_cast<void*>(aRunnable), static_cast<void*>(aWorker)));
return NS_ERROR_NOT_AVAILABLE;
}
nsRefPtr<nsDOMWorkerRunnable> workerRunnable;
{
nsAutoMonitor mon(mMonitor);
if (mWorkersInProgress.Get(aWorker, getter_AddRefs(workerRunnable))) {
workerRunnable->PutRunnable(aRunnable);
return NS_OK;
}
workerRunnable = new nsDOMWorkerRunnable(aWorker);
NS_ENSURE_TRUE(workerRunnable, NS_ERROR_OUT_OF_MEMORY);
workerRunnable->PutRunnable(aRunnable);
PRBool success = mWorkersInProgress.Put(aWorker, workerRunnable);
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
}
nsresult rv = mThreadPool->Dispatch(workerRunnable, NS_DISPATCH_NORMAL);
// XXX This is a mess and it could probably be removed once we have an
// infallible malloc implementation.
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch runnable to thread pool!");
nsAutoMonitor mon(mMonitor);
// We exited the monitor after inserting the runnable into the table so make
// sure we're removing the right one!
nsRefPtr<nsDOMWorkerRunnable> tableRunnable;
if (mWorkersInProgress.Get(aWorker, getter_AddRefs(tableRunnable)) &&
workerRunnable == tableRunnable) {
mWorkersInProgress.Remove(aWorker);
// And don't forget to tell anyone who's waiting.
mon.NotifyAll();
}
return rv;
}
return NS_OK;
}
void
nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
{
// No need to be in the monitor here because we should already be in it.
#ifdef DEBUG
nsRefPtr<nsDOMWorkerThread>& debugWorker = aRunnable->mWorker;
nsRefPtr<nsDOMWorkerRunnable> runnable;
NS_ASSERTION(mWorkersInProgress.Get(debugWorker, getter_AddRefs(runnable)) &&
runnable == aRunnable,
"Removing a worker that isn't in our hashtable?!");
#endif
mWorkersInProgress.Remove(aRunnable->mWorker);
}
void
nsDOMThreadService::WaitForCanceledWorker(nsDOMWorkerThread* aWorker)
{
NS_ASSERTION(aWorker->IsCanceled(),
"Waiting on a worker that isn't canceled!");
nsAutoMonitor mon(mMonitor);
while (mWorkersInProgress.Get(aWorker, nsnull)) {
mon.Wait();
}
}
/* static */
JSContext*
nsDOMThreadService::CreateJSContext()
{
JSRuntime* rt;
gJSRuntimeService->GetRuntime(&rt);
NS_ENSURE_TRUE(rt, nsnull);
JSAutoContextDestroyer cx(JS_NewContext(rt, 8192));
NS_ENSURE_TRUE(cx, nsnull);
JS_SetErrorReporter(cx, DOMWorkerErrorReporter);
JS_SetOperationCallback(cx, DOMWorkerOperationCallback,
100 * JS_OPERATION_WEIGHT_BASE);
nsresult rv = nsContentUtils::XPConnect()->
SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
NS_ENSURE_SUCCESS(rv, nsnull);
return cx.forget();
}
#define LOOP_OVER_POOLS(_func, _args) \
PR_BEGIN_MACRO \
PRUint32 poolCount = mPools.Length(); \
for (PRUint32 i = 0; i < poolCount; i++) { \
mPools[i]-> _func _args ; \
} \
PR_END_MACRO
void
nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOOP_OVER_POOLS(CancelWorkersForGlobal, (aGlobalObject));
}
void
nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOOP_OVER_POOLS(SuspendWorkersForGlobal, (aGlobalObject));
}
void
nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOOP_OVER_POOLS(ResumeWorkersForGlobal, (aGlobalObject));
}
void
nsDOMThreadService::NoteDyingPool(nsDOMWorkerPool* aPool)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mPools.Contains(aPool), "aPool should be in the array!");
mPools.RemoveElement(aPool);
}
void
nsDOMThreadService::TimeoutReady(nsDOMWorkerTimeout* aTimeout)
{
nsRefPtr<nsDOMWorkerTimeoutRunnable> runnable =
new nsDOMWorkerTimeoutRunnable(aTimeout);
NS_ENSURE_TRUE(runnable,);
Dispatch(aTimeout->GetWorker(), runnable);
}
nsresult
nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
{
NS_ENSURE_ARG(aDelta == 1 || aDelta == -1);
PRUint32 currentThreadCount;
nsresult rv = mThreadPool->GetThreadLimit(&currentThreadCount);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 newThreadCount = (PRInt32)currentThreadCount + (PRInt32)aDelta;
NS_ASSERTION(newThreadCount >= THREADPOOL_MAX_THREADS,
"Can't go below initial thread count!");
rv = mThreadPool->SetThreadLimit((PRUint32)newThreadCount);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsIJSRuntimeService*
nsDOMThreadService::JSRuntimeService()
{
return gJSRuntimeService;
}
nsIThreadJSContextStack*
nsDOMThreadService::ThreadJSContextStack()
{
return gThreadJSContextStack;
}
nsIXPCSecurityManager*
nsDOMThreadService::WorkerSecurityManager()
{
return gWorkerSecurityManager;
}
/**
* See nsIEventTarget
*/
NS_IMETHODIMP
nsDOMThreadService::Dispatch(nsIRunnable* aEvent,
PRUint32 aFlags)
{
NS_ENSURE_ARG_POINTER(aEvent);
NS_ENSURE_FALSE(aFlags & NS_DISPATCH_SYNC, NS_ERROR_NOT_IMPLEMENTED);
// This should only ever be called by the timer code! We run the event right
// now, but all that does is queue the real event for the proper worker.
aEvent->Run();
return NS_OK;
}
/**
* See nsIEventTarget
*/
NS_IMETHODIMP
nsDOMThreadService::IsOnCurrentThread(PRBool* _retval)
{
NS_NOTREACHED("No one should call this!");
return NS_ERROR_NOT_IMPLEMENTED;
}
/**
* See nsIObserver
*/
NS_IMETHODIMP
nsDOMThreadService::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
Cleanup();
return NS_OK;
}
NS_NOTREACHED("Unknown observer topic!");
return NS_OK;
}
/**
* See nsIThreadPoolListener
*/
NS_IMETHODIMP
nsDOMThreadService::OnThreadCreated()
{
LOG(("Thread created"));
nsIThread* current = NS_GetCurrentThread();
// We want our worker threads to always have a lower priority than the main
// thread. NSPR docs say that this isn't incredibly reliable across all
// platforms but we hope for the best.
nsCOMPtr<nsISupportsPriority> priority(do_QueryInterface(current));
NS_ENSURE_TRUE(priority, NS_ERROR_FAILURE);
nsresult rv = priority->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
// Set the context up for the worker.
JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
if (!cx) {
cx = nsDOMThreadService::CreateJSContext();
NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
PRStatus status = PR_SetThreadPrivate(gJSContextIndex, cx);
if (status != PR_SUCCESS) {
NS_WARNING("Failed to set context on thread!");
nsContentUtils::XPConnect()->ReleaseJSContext(cx, PR_TRUE);
return NS_ERROR_FAILURE;
}
}
// Make sure that XPConnect knows about this context.
gThreadJSContextStack->Push(cx);
gThreadJSContextStack->SetSafeJSContext(cx);
return NS_OK;
}
NS_IMETHODIMP
nsDOMThreadService::OnThreadShuttingDown()
{
LOG(("Thread shutting down"));
NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
NS_WARN_IF_FALSE(cx, "Thread died with no context?");
if (cx) {
JSContext* pushedCx;
gThreadJSContextStack->Pop(&pushedCx);
NS_ASSERTION(pushedCx == cx, "Popped the wrong context!");
gThreadJSContextStack->SetSafeJSContext(nsnull);
nsContentUtils::XPConnect()->ReleaseJSContext(cx, PR_TRUE);
}
return NS_OK;
}
/**
* See nsIDOMThreadService
*/
NS_IMETHODIMP
nsDOMThreadService::CreatePool(nsIDOMWorkerPool** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_TRUE(mThreadPool, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
nsRefPtr<nsDOMWorkerPool> pool(new nsDOMWorkerPool());
NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = pool->Init();
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!mPools.Contains(pool), "Um?!");
mPools.AppendElement(pool);
NS_ADDREF(*_retval = pool);
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,144 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSDOMTHREADSERVICE_H__
#define __NSDOMTHREADSERVICE_H__
// Interfaces
#include "nsIEventTarget.h"
#include "nsIObserver.h"
#include "nsIThreadPool.h"
#include "nsIDOMThreads.h"
// Other includes
#include "jsapi.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsRefPtrHashtable.h"
#include "nsTPtrArray.h"
#include "prmon.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gDOMThreadsLog;
#endif
class nsDOMWorkerPool;
class nsDOMWorkerRunnable;
class nsDOMWorkerThread;
class nsDOMWorkerTimeout;
class nsIJSRuntimeService;
class nsIScriptGlobalObject;
class nsIThreadJSContextStack;
class nsIXPConnect;
class nsIXPCSecurityManager;
class nsDOMThreadService : public nsIEventTarget,
public nsIObserver,
public nsIThreadPoolListener,
public nsIDOMThreadService
{
friend class nsDOMWorkerPool;
friend class nsDOMWorkerRunnable;
friend class nsDOMWorkerThread;
friend class nsDOMWorkerTimeout;
friend class nsLayoutStatics;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIEVENTTARGET
NS_DECL_NSIOBSERVER
NS_DECL_NSITHREADPOOLLISTENER
NS_DECL_NSIDOMTHREADSERVICE
// Any DOM consumers that need access to this service should use this method.
static already_AddRefed<nsIDOMThreadService> GetOrInitService();
// Simple getter for this service. This does not create the service if it
// hasn't been created already, and it never AddRef's!
static nsDOMThreadService* get();
// Easy access to the services we care about.
static nsIJSRuntimeService* JSRuntimeService();
static nsIThreadJSContextStack* ThreadJSContextStack();
static nsIXPCSecurityManager* WorkerSecurityManager();
void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
nsresult ChangeThreadPoolMaxThreads(PRInt16 aDelta);
private:
nsDOMThreadService();
~nsDOMThreadService();
nsresult Init();
void Cleanup();
static void Shutdown();
nsresult Dispatch(nsDOMWorkerThread* aWorker,
nsIRunnable* aRunnable);
void WorkerComplete(nsDOMWorkerRunnable* aRunnable);
void WaitForCanceledWorker(nsDOMWorkerThread* aWorker);
static JSContext* CreateJSContext();
void NoteDyingPool(nsDOMWorkerPool* aPool);
void TimeoutReady(nsDOMWorkerTimeout* aTimeout);
// Our internal thread pool.
nsCOMPtr<nsIThreadPool> mThreadPool;
// Weak references, only ever touched on the main thread!
nsTPtrArray<nsDOMWorkerPool> mPools;
// mMonitor protects all access to mWorkersInProgress and
// mCreationsInProgress.
PRMonitor* mMonitor;
// A map from nsDOMWorkerThread to nsDOMWorkerRunnable.
nsRefPtrHashtable<nsVoidPtrHashKey, nsDOMWorkerRunnable> mWorkersInProgress;
};
#endif /* __NSDOMTHREADSERVICE_H__ */

Просмотреть файл

@ -0,0 +1,209 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsDOMWorkerBase.h"
struct JSContext;
// Interfaces
#include "nsIDOMThreads.h"
#include "nsIJSContextStack.h"
#include "nsIXPConnect.h"
// Other includes
#include "jsapi.h"
#include "nsAutoPtr.h"
#include "nsContentUtils.h"
#include "nsDOMJSUtils.h"
#include "nsThreadUtils.h"
#include "prlog.h"
#include "nsDOMWorkerThread.h"
#include "nsDOMWorkerPool.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gDOMThreadsLog;
#endif
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
/**
* Calls the message handler function of the target worker on the correct
* thread.
*/
class nsDOMPostMessageRunnable : public nsRunnable
{
public:
nsDOMPostMessageRunnable(const nsAString& aMessage,
nsDOMWorkerBase* aSource,
nsDOMWorkerBase* aTarget)
: mMessage(aMessage), mSource(aSource), mTarget(aTarget) {
NS_ASSERTION(aSource && aTarget, "Must specify both!");
}
NS_IMETHOD Run() {
#ifdef PR_LOGGING
nsCAutoString utf8Message;
utf8Message.AssignWithConversion(mMessage);
static const char* poolStr = "pool";
static const char* workerStr = "worker";
nsCOMPtr<nsIDOMWorkerPool> sourceIsPool;
mSource->QueryInterface(NS_GET_IID(nsIDOMWorkerPool),
getter_AddRefs(sourceIsPool));
nsCOMPtr<nsIDOMWorkerPool> targetIsPool;
mTarget->QueryInterface(NS_GET_IID(nsIDOMWorkerPool),
getter_AddRefs(targetIsPool));
#endif
LOG(("Posting message '%s' from %s [0x%p] to %s [0x%p]",
utf8Message.get(), sourceIsPool ? poolStr : workerStr,
static_cast<void*>(mSource.get()), targetIsPool ? poolStr : workerStr,
static_cast<void*>(mTarget.get())));
mTarget->HandleMessage(mMessage, mSource);
return NS_OK;
}
protected:
nsString mMessage;
nsRefPtr<nsDOMWorkerBase> mSource;
nsRefPtr<nsDOMWorkerBase> mTarget;
};
nsresult
nsDOMWorkerBase::PostMessageInternal(const nsAString& aMessage,
nsDOMWorkerBase* aSource)
{
if (IsCanceled() || aSource->IsCanceled()) {
return NS_OK;
}
nsRefPtr<nsDOMPostMessageRunnable> runnable =
new nsDOMPostMessageRunnable(aMessage, aSource, this);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = DispatchMessage(runnable);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsDOMWorkerBase::PostMessageInternal(const nsAString& aMessage)
{
nsAXPCNativeCallContext* ncc;
nsresult rv = nsContentUtils::XPConnect()->
GetCurrentNativeCallContext(&ncc);
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsDOMWorkerBase> source;
if (ncc) {
if (NS_IsMainThread()) {
// Must be a normal DOM context, use the pool as the source.
source = Pool();
}
else {
// Must be a worker context, get the worker from the context private.
JSContext* cx;
rv = ncc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsDOMWorkerThread> worker =
(nsDOMWorkerThread*)JS_GetContextPrivate(cx);
// Only allowed to communicate to other threads in the same pool.
nsRefPtr<nsDOMWorkerPool> sourcePool = worker->Pool();
NS_ENSURE_TRUE(sourcePool == Pool(), NS_ERROR_NOT_AVAILABLE);
source = worker;
}
}
else {
source = this;
}
rv = PostMessageInternal(aMessage, source);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
nsDOMWorkerBase::Cancel()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOG(("Canceling %s [0x%p]", Pool() == this ? "pool" : "worker",
static_cast<void*>(this)));
#ifdef DEBUG
PRInt32 cancel =
#endif
PR_AtomicSet(&mCanceled, 1);
NS_ASSERTION(!cancel, "Canceled more than once?!");
}
void
nsDOMWorkerBase::Suspend()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOG(("Suspending %s [0x%p]", Pool() == this ? "pool" : "worker",
static_cast<void*>(this)));
#ifdef DEBUG
PRInt32 suspended =
#endif
PR_AtomicSet(&mSuspended, 1);
NS_ASSERTION(!suspended, "Suspended more than once?!");
}
void
nsDOMWorkerBase::Resume()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOG(("Resuming %s [0x%p]", Pool() == this ? "pool" : "worker",
static_cast<void*>(this)));
#ifdef DEBUG
PRInt32 suspended =
#endif
PR_AtomicSet(&mSuspended, 0);
NS_ASSERTION(suspended, "Not suspended!");
}

Просмотреть файл

@ -0,0 +1,101 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSDOMWORKERBASE_H__
#define __NSDOMWORKERBASE_H__
#include "nsCOMPtr.h"
#include "nsIDOMThreads.h"
#include "nsStringGlue.h"
class nsIRunnable;
class nsDOMWorkerPool;
class nsDOMWorkerBase
{
friend class nsDOMPostMessageRunnable;
public:
// Meant to be inherited by an nsISupports-derived class.
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) = 0;
NS_IMETHOD_(nsrefcnt) AddRef(void) = 0;
NS_IMETHOD_(nsrefcnt) Release(void) = 0;
PRBool IsCanceled() {
return !!mCanceled;
}
PRBool IsSuspended() {
return !!mSuspended;
}
protected:
nsDOMWorkerBase() : mCanceled(0), mSuspended(0) { }
virtual ~nsDOMWorkerBase() { }
void SetMessageListener(nsIDOMWorkerMessageListener* aListener) {
mMessageListener = aListener;
}
nsIDOMWorkerMessageListener* GetMessageListener() {
return mMessageListener;
}
nsresult PostMessageInternal(const nsAString& aMessage,
nsDOMWorkerBase* aSource);
nsresult PostMessageInternal(const nsAString& aMessage);
virtual void Cancel();
virtual void Suspend();
virtual void Resume();
// Methods that must be implemented by a derived class.
virtual nsresult HandleMessage(const nsAString& aMessage,
nsDOMWorkerBase* aSource) = 0;
virtual nsresult DispatchMessage(nsIRunnable* aMessage) = 0;
virtual nsDOMWorkerPool* Pool() = 0;
private:
nsCOMPtr<nsIDOMWorkerMessageListener> mMessageListener;
PRInt32 mCanceled;
PRInt32 mSuspended;
};
#endif /* __NSDOMWORKERBASE_H__ */

Просмотреть файл

@ -0,0 +1,305 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsDOMWorkerPool.h"
// Interfaces
#include "nsIDOMClassInfo.h"
#include "nsIJSContextStack.h"
#include "nsIScriptContext.h"
#include "nsIScriptGlobalObject.h"
#include "nsIServiceManager.h"
#include "nsIThreadManager.h"
#include "nsIXPConnect.h"
#include "nsPIDOMWindow.h"
// Other includes
#include "nsAutoLock.h"
#include "nsContentUtils.h"
#include "nsDOMJSUtils.h"
#include "nsThreadUtils.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
#include "nsDOMWorkerThread.h"
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
#define LOOP_OVER_WORKERS(_func, _args) \
PR_BEGIN_MACRO \
PRUint32 workerCount = mWorkers.Length(); \
for (PRUint32 i = 0; i < workerCount; i++) { \
mWorkers[i]-> _func _args ; \
} \
PR_END_MACRO
nsDOMWorkerPool::nsDOMWorkerPool()
: mParentGlobal(nsnull)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
nsDOMWorkerPool::~nsDOMWorkerPool()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
LOOP_OVER_WORKERS(Cancel, ());
nsDOMThreadService::get()->NoteDyingPool(this);
if (mMonitor) {
nsAutoMonitor::DestroyMonitor(mMonitor);
}
}
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerPool, nsIDOMWorkerPool,
nsIClassInfo)
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPool, nsIDOMWorkerPool)
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerPool)
nsresult
nsDOMWorkerPool::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsAXPCNativeCallContext* ncc;
nsresult rv = nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc);
NS_ENSURE_SUCCESS(rv, rv);
// GetCurrentNativeCallContext can return NS_OK and still hand out a null
// context... We shouldn't ever see that here.
NS_ENSURE_TRUE(ncc, NS_ERROR_UNEXPECTED);
JSContext* cx;
rv = ncc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
NS_ENSURE_STATE(scriptContext);
nsIScriptGlobalObject* globalObject = scriptContext->GetGlobalObject();
NS_ENSURE_STATE(globalObject);
nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(globalObject));
NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
domWindow->GetCurrentInnerWindow() :
domWindow.get();
NS_ENSURE_STATE(innerWindow);
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(innerWindow));
NS_ENSURE_TRUE(globalSupports, NS_ERROR_NO_INTERFACE);
// We don't want a strong ref, this guy owns us.
mParentGlobal = globalSupports.get();
mMonitor = nsAutoMonitor::NewMonitor("nsDOMWorkerPool::mMonitor");
NS_ENSURE_TRUE(mMonitor, NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
nsresult
nsDOMWorkerPool::HandleMessage(const nsAString& aMessage,
nsDOMWorkerBase* aSource)
{
nsCOMPtr<nsIDOMWorkerMessageListener> messageListener =
nsDOMWorkerBase::GetMessageListener();
if (!messageListener) {
LOG(("Message received on a worker with no listener!"));
return NS_OK;
}
nsCOMPtr<nsISupports> source;
aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source));
NS_ASSERTION(source, "Impossible!");
messageListener->OnMessage(aMessage, source);
return NS_OK;
}
nsresult
nsDOMWorkerPool::DispatchMessage(nsIRunnable* aRunnable)
{
// Can be called from many different threads!
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
NS_ENSURE_TRUE(mainThread, NS_ERROR_FAILURE);
nsresult rv = mainThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
nsDOMWorkerPool::HandleError(nsIScriptError* aError,
nsDOMWorkerThread* aSource)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mErrorListener) {
mErrorListener->OnError(aError,
NS_ISUPPORTS_CAST(nsIDOMWorkerThread*, aSource));
}
}
void
nsDOMWorkerPool::NoteDyingWorker(nsDOMWorkerThread* aWorker)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mWorkers.Contains(aWorker), "Worker from a different pool?!");
mWorkers.RemoveElement(aWorker);
}
void
nsDOMWorkerPool::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
NS_ASSERTION(globalSupports, "Null pointer?!");
if (globalSupports == mParentGlobal) {
LOOP_OVER_WORKERS(Cancel, ());
mWorkers.Clear();
if (IsSuspended()) {
nsAutoMonitor mon(mMonitor);
mon.NotifyAll();
}
}
}
void
nsDOMWorkerPool::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
NS_ASSERTION(globalSupports, "Null pointer?!");
if (globalSupports == mParentGlobal) {
LOOP_OVER_WORKERS(Suspend, ());
Suspend();
}
}
void
nsDOMWorkerPool::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsISupports> globalSupports(do_QueryInterface(aGlobalObject));
NS_ASSERTION(globalSupports, "Null pointer?!");
if (globalSupports == mParentGlobal) {
LOOP_OVER_WORKERS(Resume, ());
Resume();
nsAutoMonitor mon(mMonitor);
mon.NotifyAll();
}
}
NS_IMETHODIMP
nsDOMWorkerPool::PostMessage(const nsAString& aMessage)
{
nsresult rv = PostMessageInternal(aMessage);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::SetMessageListener(nsIDOMWorkerMessageListener* aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsDOMWorkerBase::SetMessageListener(aListener);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::GetMessageListener(nsIDOMWorkerMessageListener** aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIDOMWorkerMessageListener> listener = nsDOMWorkerBase::GetMessageListener();
listener.forget(aListener);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::SetErrorListener(nsIDOMWorkerErrorListener* aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mErrorListener = aListener;
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::GetErrorListener(nsIDOMWorkerErrorListener** aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_IF_ADDREF(*aListener = mErrorListener);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerPool::CreateWorker(const nsAString& fullScript,
nsIDOMWorkerThread** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ENSURE_ARG(!fullScript.IsEmpty());
NS_ENSURE_ARG_POINTER(_retval);
nsRefPtr<nsDOMWorkerThread> worker(new nsDOMWorkerThread(this, fullScript));
NS_ENSURE_TRUE(worker, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = worker->Init();
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!mWorkers.Contains(worker), "Um?!");
mWorkers.AppendElement(worker);
NS_ADDREF(*_retval = worker);
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,123 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSDOMWORKERPOOL_H__
#define __NSDOMWORKERPOOL_H__
// Bases
#include "nsDOMWorkerBase.h"
#include "nsIClassInfo.h"
#include "nsIDOMThreads.h"
// Other includes
#include "jsapi.h"
#include "nsStringGlue.h"
#include "nsTPtrArray.h"
#include "prmon.h"
class nsDOMWorkerThread;
class nsIScriptError;
class nsIScriptGlobalObject;
/**
* The pool is almost always touched only on the main thread.
*/
class nsDOMWorkerPool : public nsDOMWorkerBase,
public nsIDOMWorkerPool,
public nsIClassInfo
{
friend class nsDOMThreadService;
friend class nsDOMWorkerFunctions;
friend class nsDOMWorkerPoolWeakRef;
friend class nsDOMWorkerThread;
friend class nsReportErrorRunnable;
friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMWORKERPOOL
NS_DECL_NSICLASSINFO
nsDOMWorkerPool();
// For nsDOMWorkerBase
virtual nsDOMWorkerPool* Pool() {
return this;
}
private:
virtual ~nsDOMWorkerPool();
nsresult Init();
// For nsDOMWorkerBase
virtual nsresult HandleMessage(const nsAString& aMessage,
nsDOMWorkerBase* aSourceThread);
// For nsDOMWorkerBase
virtual nsresult DispatchMessage(nsIRunnable* aRunnable);
void HandleError(nsIScriptError* aError,
nsDOMWorkerThread* aSource);
void NoteDyingWorker(nsDOMWorkerThread* aWorker);
void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
PRMonitor* Monitor() {
return mMonitor;
}
// Weak reference to the window that created and owns this pool.
nsISupports* mParentGlobal;
// Weak array of workers. The idea is that workers can be garbage collected
// independently of the owning pool and other workers.
nsTPtrArray<nsDOMWorkerThread> mWorkers;
// An error handler function, may be null.
nsCOMPtr<nsIDOMWorkerErrorListener> mErrorListener;
// Monitor for suspending and resuming workers.
PRMonitor* mMonitor;
};
#endif /* __NSDOMWORKERPOOL_H__ */

Просмотреть файл

@ -0,0 +1,90 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsDOMWorkerSecurityManager.h"
// Interfaces
#include "nsIClassInfo.h"
// Other includes
#include "jsapi.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerSecurityManager,
nsIXPCSecurityManager)
NS_IMETHODIMP
nsDOMWorkerSecurityManager::CanCreateWrapper(JSContext* aJSContext,
const nsIID& aIID,
nsISupports* aObj,
nsIClassInfo* aClassInfo,
void** aPolicy)
{
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerSecurityManager::CanCreateInstance(JSContext* aJSContext,
const nsCID& aCID)
{
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerSecurityManager::CanGetService(JSContext* aJSContext,
const nsCID& aCID)
{
return NS_OK;
}
NS_IMETHODIMP
nsDOMWorkerSecurityManager::CanAccess(PRUint32 aAction,
nsAXPCNativeCallContext* aCallContext,
JSContext* aJSContext,
JSObject* aJSObject,
nsISupports* aObj,
nsIClassInfo* aClassInfo,
jsval aName,
void** aPolicy)
{
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,51 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSDOMWORKERSECURITYMANAGER_H__
#define __NSDOMWORKERSECURITYMANAGER_H__
#include "nsIXPCSecurityManager.h"
class nsDOMWorkerSecurityManager : public nsIXPCSecurityManager
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCSECURITYMANAGER
};
#endif /* __NSDOMWORKERSECURITYMANAGER_H__ */

Просмотреть файл

@ -0,0 +1,752 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsDOMWorkerThread.h"
// Interfaces
#include "nsIDOMClassInfo.h"
#include "nsIJSContextStack.h"
#include "nsIJSRuntimeService.h"
#include "nsIScriptContext.h"
#include "nsIXPConnect.h"
// Other includes
#ifdef MOZ_SHARK
#include "jsdbgapi.h"
#endif
#include "nsAutoLock.h"
#include "nsContentUtils.h"
#include "nsJSUtils.h"
#include "nsJSEnvironment.h"
// DOMWorker includes
#include "nsDOMWorkerPool.h"
#include "nsDOMThreadService.h"
#include "nsDOMWorkerTimeout.h"
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
// XXX Could make these functions of nsDOMWorkerThread instead.
class nsDOMWorkerFunctions
{
public:
// Same as window.dump().
static JSBool Dump(JSContext* aCx, JSObject* aObj, uintN aArgc, jsval* aArgv,
jsval* aRval);
// Debug-only version of window.dump(), like the JS component loader has.
static JSBool DebugDump(JSContext* aCx, JSObject* aObj, uintN aArgc,
jsval* aArgv, jsval* aRval);
// Same as nsIDOMWorkerThread::PostMessage
static JSBool PostMessage(JSContext* aCx, JSObject* aObj, uintN aArgc,
jsval* aArgv, jsval* aRval);
// Same as window.setTimeout().
static JSBool SetTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
jsval* aArgv, jsval* aRval) {
return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_FALSE);
}
// Same as window.setInterval().
static JSBool SetInterval(JSContext* aCx, JSObject* aObj, uintN aArgc,
jsval* aArgv, jsval* aRval) {
return MakeTimeout(aCx, aObj, aArgc, aArgv, aRval, PR_TRUE);
}
// Used for both clearTimeout() and clearInterval().
static JSBool KillTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
jsval* aArgv, jsval* aRval);
private:
// Internal helper for SetTimeout and SetInterval.
static JSBool MakeTimeout(JSContext* aCx, JSObject* aObj, uintN aArgc,
jsval* aArgv, jsval* aRval, PRBool aIsInterval);
};
JSBool JS_DLL_CALLBACK
nsDOMWorkerFunctions::Dump(JSContext* aCx,
JSObject* /* aObj */,
uintN aArgc,
jsval* aArgv,
jsval* /* aRval */)
{
// XXX Expose this to the JS console? Only if that DOM pref is set?
JSString* str;
if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) {
nsDependentJSString string(str);
fputs(NS_ConvertUTF16toUTF8(nsDependentJSString(str)).get(), stderr);
fflush(stderr);
}
return JS_TRUE;
}
JSBool JS_DLL_CALLBACK
nsDOMWorkerFunctions::DebugDump(JSContext* aCx,
JSObject* aObj,
uintN aArgc,
jsval* aArgv,
jsval* aRval)
{
#ifdef DEBUG
return nsDOMWorkerFunctions::Dump(aCx, aObj, aArgc, aArgv, aRval);
#else
return JS_TRUE;
#endif
}
JSBool JS_DLL_CALLBACK
nsDOMWorkerFunctions::PostMessage(JSContext* aCx,
JSObject* /* aObj */,
uintN aArgc,
jsval* aArgv,
jsval* /* aRval */)
{
nsDOMWorkerThread* worker =
static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) {
return JS_FALSE;
}
nsRefPtr<nsDOMWorkerPool> pool = worker->Pool();
NS_ASSERTION(pool, "Shouldn't ever be null!");
nsresult rv;
JSString* str;
if (aArgc && (str = JS_ValueToString(aCx, aArgv[0])) && str) {
rv = pool->PostMessageInternal(nsDependentJSString(str), worker);
}
else {
rv = pool->PostMessageInternal(EmptyString(), worker);
}
NS_ENSURE_SUCCESS(rv, JS_FALSE);
return JS_TRUE;
}
JSBool JS_DLL_CALLBACK
nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
JSObject* /* aObj */,
uintN aArgc,
jsval* aArgv,
jsval* aRval,
PRBool aIsInterval)
{
nsDOMWorkerThread* worker =
static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) {
return JS_FALSE;
}
PRUint32 id = ++worker->mNextTimeoutId;
nsAutoPtr<nsDOMWorkerTimeout>
timeout(new nsDOMWorkerTimeout(worker, id));
NS_ENSURE_TRUE(timeout, JS_FALSE);
nsresult rv = timeout->Init(aCx, aArgc, aArgv, aIsInterval);
NS_ENSURE_SUCCESS(rv, JS_FALSE);
timeout.forget();
*aRval = INT_TO_JSVAL(id);
return JS_TRUE;
}
JSBool JS_DLL_CALLBACK
nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
JSObject* /* aObj */,
uintN aArgc,
jsval* aArgv,
jsval* /* aRval */)
{
nsDOMWorkerThread* worker =
static_cast<nsDOMWorkerThread*>(JS_GetContextPrivate(aCx));
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
// A canceled worker should have already killed all timeouts.
if (worker->IsCanceled()) {
return JS_TRUE;
}
if (!aArgc) {
JS_ReportError(aCx, "Function requires at least 1 parameter");
return JS_FALSE;
}
uint32 id;
if (!JS_ValueToECMAUint32(aCx, aArgv[0], &id)) {
JS_ReportError(aCx, "First argument must be a timeout id");
return JS_FALSE;
}
worker->CancelTimeout(PRUint32(id));
return JS_TRUE;
}
JSFunctionSpec gDOMWorkerFunctions[] = {
{ "dump", nsDOMWorkerFunctions::Dump, 1, 0, 0 },
{ "debug", nsDOMWorkerFunctions::DebugDump, 1, 0, 0 },
{ "postMessageToPool", nsDOMWorkerFunctions::PostMessage, 1, 0, 0 },
{ "setTimeout", nsDOMWorkerFunctions::SetTimeout, 1, 0, 0 },
{ "clearTimeout", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 },
{ "setInterval", nsDOMWorkerFunctions::SetInterval, 1, 0, 0 },
{ "clearInterval", nsDOMWorkerFunctions::KillTimeout, 1, 0, 0 },
#ifdef MOZ_SHARK
{ "startShark", js_StartShark, 0, 0, 0 },
{ "stopShark", js_StopShark, 0, 0, 0 },
{ "connectShark", js_ConnectShark, 0, 0, 0 },
{ "disconnectShark", js_DisconnectShark, 0, 0, 0 },
#endif
{ nsnull, nsnull, 0, 0, 0 }
};
/**
* An nsISupports that holds a weak ref to the worker. The worker owns the
* thread context so we don't have to worry about nulling this out ever.
*/
class nsDOMWorkerThreadWeakRef : public nsIDOMWorkerThread,
public nsIClassInfo
{
public:
NS_DECL_ISUPPORTS
NS_FORWARD_NSIDOMWORKERTHREAD(mWorker->)
NS_FORWARD_NSICLASSINFO(mWorker->)
nsDOMWorkerThreadWeakRef(nsDOMWorkerThread* aWorker)
: mWorker(aWorker) { }
protected:
nsDOMWorkerThread* mWorker;
};
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThreadWeakRef, nsIDOMWorkerThread,
nsIClassInfo)
/**
* The 'threadContext' object for a worker's JS global object.
*/
class nsDOMWorkerThreadContext : public nsIDOMWorkerThreadContext,
public nsIClassInfo
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMWORKERTHREADCONTEXT
NS_DECL_NSICLASSINFO
nsDOMWorkerThreadContext(nsDOMWorkerThread* aWorker)
: mWorker(aWorker) { }
protected:
nsDOMWorkerThread* mWorker;
nsCOMPtr<nsIDOMWorkerThread> mWeakRef;
};
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThreadContext,
nsIDOMWorkerThreadContext,
nsIClassInfo)
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerThreadContext,
nsIDOMWorkerThreadContext)
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerThreadContext)
NS_IMETHODIMP
nsDOMWorkerThreadContext::GetThisThread(nsIDOMWorkerThread** aThisThread)
{
if (!mWeakRef) {
mWeakRef = new nsDOMWorkerThreadWeakRef(mWorker);
NS_ENSURE_TRUE(mWeakRef, NS_ERROR_OUT_OF_MEMORY);
}
NS_ADDREF(*aThisThread = mWeakRef);
return NS_OK;
}
nsDOMWorkerThread::nsDOMWorkerThread(nsDOMWorkerPool* aPool,
const nsAString& aSource)
: mPool(aPool),
mSource(aSource),
mGlobal(nsnull),
mCompiled(PR_FALSE),
mCallbackCount(0),
mNextTimeoutId(0),
mLock(nsnull)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aSource.IsEmpty(), "Empty source string!");
PR_INIT_CLIST(&mTimeouts);
}
nsDOMWorkerThread::~nsDOMWorkerThread()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!IsCanceled()) {
nsRefPtr<nsDOMWorkerPool> pool = Pool();
pool->NoteDyingWorker(this);
}
ClearTimeouts();
// Only clean up if we created a global object
if (mGlobal) {
JSRuntime* rt;
if (NS_SUCCEEDED(nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt))) {
JS_RemoveRootRT(rt, &mGlobal);
}
else {
NS_ERROR("This shouldn't fail!");
}
}
if (mLock) {
nsAutoLock::DestroyLock(mLock);
}
}
NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerThread, nsIDOMWorkerThread,
nsIClassInfo)
NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerThread, nsIDOMWorkerThread)
NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerThread)
nsresult
nsDOMWorkerThread::Init()
{
mLock = nsAutoLock::NewLock("nsDOMWorkerThread::mLock");
NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
NS_ASSERTION(!mGlobal, "Already got a global?!");
// This is pretty cool - all we have to do to get our script executed is to
// pass a no-op runnable to the thread service and it will make sure we have
// a context and global object.
nsCOMPtr<nsIRunnable> runnable(new nsRunnable());
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = nsDOMThreadService::get()->Dispatch(this, runnable);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// From nsDOMWorkerBase
nsresult
nsDOMWorkerThread::HandleMessage(const nsAString& aMessage,
nsDOMWorkerBase* aSource)
{
nsCOMPtr<nsIDOMWorkerMessageListener> messageListener = GetMessageListener();
if (!messageListener) {
LOG(("Message received on a worker with no listener!"));
return NS_OK;
}
// We have to call this manually because XPConnect will replace our error
// reporter with its own and we won't properly notify the pool of any
// unhandled exceptions...
JSContext* cx;
nsresult rv =
nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
JSAutoRequest ar(cx);
if (JS_IsExceptionPending(cx)) {
JS_ClearPendingException(cx);
}
// Get a JS string for the message.
JSString* message = JS_NewUCStringCopyN(cx, (jschar*)aMessage.BeginReading(),
aMessage.Length());
NS_ENSURE_TRUE(message, NS_ERROR_FAILURE);
// Root it
jsval messageVal = STRING_TO_JSVAL(message);
nsAutoGCRoot rootedMessage(&messageVal, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsIXPConnect* xpc = nsContentUtils::XPConnect();
nsCOMPtr<nsISupports> source;
aSource->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(source));
NS_ASSERTION(source, "Impossible!");
// Wrap the source thread.
nsCOMPtr<nsIXPConnectJSObjectHolder> wrappedThread;
rv = xpc->WrapNative(cx, mGlobal, source, NS_GET_IID(nsISupports),
getter_AddRefs(wrappedThread));
NS_ENSURE_SUCCESS(rv, rv);
JSObject* sourceThread;
rv = wrappedThread->GetJSObject(&sourceThread);
NS_ENSURE_SUCCESS(rv, rv);
// Set up our arguments.
jsval argv[2] = {
STRING_TO_JSVAL(message),
OBJECT_TO_JSVAL(sourceThread)
};
// Get the listener object out of our wrapped listener.
nsCOMPtr<nsIXPConnectJSObjectHolder> wrappedListener =
do_QueryInterface(messageListener);
NS_ENSURE_TRUE(wrappedListener, NS_ERROR_NO_INTERFACE);
JSObject* listener;
rv = wrappedListener->GetJSObject(&listener);
NS_ENSURE_SUCCESS(rv, rv);
// And call it.
jsval rval;
PRBool success = JS_CallFunctionValue(cx, mGlobal, OBJECT_TO_JSVAL(listener),
2, argv, &rval);
if (!success) {
// Make sure any pending exceptions are converted to errors for the pool.
JS_ReportPendingException(cx);
}
// We shouldn't leave any pending exceptions - our error reporter should
// clear any exception it reports.
NS_ASSERTION(!JS_IsExceptionPending(cx), "Huh?!");
return NS_OK;
}
// From nsDOMWorkerBase
nsresult
nsDOMWorkerThread::DispatchMessage(nsIRunnable* aRunnable)
{
nsresult rv = nsDOMThreadService::get()->Dispatch(this, aRunnable);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
nsDOMWorkerThread::Cancel()
{
nsDOMWorkerBase::Cancel();
// If we're suspended there's a good chance that we're already paused waiting
// on the pool's monitor. Waiting on the thread service's lock will deadlock.
if (!IsSuspended()) {
nsDOMThreadService::get()->WaitForCanceledWorker(this);
}
ClearTimeouts();
}
void
nsDOMWorkerThread::Suspend()
{
nsDOMWorkerBase::Suspend();
SuspendTimeouts();
}
void
nsDOMWorkerThread::Resume()
{
nsDOMWorkerBase::Resume();
ResumeTimeouts();
}
PRBool
nsDOMWorkerThread::SetGlobalForContext(JSContext* aCx)
{
PRBool success = CompileGlobalObject(aCx);
NS_ENSURE_TRUE(success, PR_FALSE);
JS_SetGlobalObject(aCx, mGlobal);
return PR_TRUE;
}
PRBool
nsDOMWorkerThread::CompileGlobalObject(JSContext* aCx)
{
if (mGlobal) {
return PR_TRUE;
}
if (mCompiled) {
// Don't try to recompile a bad script.
return PR_FALSE;
}
mCompiled = PR_TRUE;
JSAutoRequest ar(aCx);
JSObject* global = JS_NewObject(aCx, nsnull, nsnull, nsnull);
NS_ENSURE_TRUE(global, PR_FALSE);
NS_ASSERTION(!JS_GetGlobalObject(aCx), "Global object should be unset!");
// This call will root global.
PRBool success = JS_InitStandardClasses(aCx, global);
NS_ENSURE_TRUE(success, PR_FALSE);
// Set up worker thread functions
success = JS_DefineFunctions(aCx, global, gDOMWorkerFunctions);
NS_ENSURE_TRUE(success, PR_FALSE);
nsRefPtr<nsDOMWorkerThreadContext>
context(new nsDOMWorkerThreadContext(this));
NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
nsIXPConnect* xpc = nsContentUtils::XPConnect();
nsresult rv = xpc->InitClasses(aCx, global);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
// XXX Fix this!
success = JS_DeleteProperty(aCx, global, "Components");
NS_ENSURE_TRUE(success, PR_FALSE);
nsCOMPtr<nsIXPConnectJSObjectHolder> contextWrapper;
rv = xpc->WrapNative(aCx, global,
NS_ISUPPORTS_CAST(nsIDOMWorkerThreadContext*, context),
NS_GET_IID(nsIDOMWorkerThreadContext),
getter_AddRefs(contextWrapper));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
JSObject* contextObj;
rv = contextWrapper->GetJSObject(&contextObj);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
// Set up a name for our worker object
success = JS_DefineProperty(aCx, global, "threadContext",
OBJECT_TO_JSVAL(contextObj), nsnull, nsnull,
JSPROP_ENUMERATE);
NS_ENSURE_TRUE(success, PR_FALSE);
JSScript* script = JS_CompileUCScript(aCx, global, mSource.BeginReading(),
mSource.Length(), nsnull, 1);
NS_ENSURE_TRUE(script, PR_FALSE);
JSRuntime* rt;
rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
mGlobal = global;
success = JS_AddNamedRootRT(rt, &mGlobal, "nsDOMWorkerThread Global Object");
if (!success) {
NS_WARNING("Failed to root global object for worker thread!");
mGlobal = nsnull;
return PR_FALSE;
}
// Execute the script
jsval val;
success = JS_ExecuteScript(aCx, global, script, &val);
if (!success) {
NS_WARNING("Failed to evaluate script for worker thread!");
JS_RemoveRootRT(rt, &mGlobal);
mGlobal = nsnull;
return PR_FALSE;
}
// See if the message listener function was defined.
nsCOMPtr<nsIDOMWorkerMessageListener> listener;
if (JS_LookupProperty(aCx, global, "messageListener", &val) &&
JSVAL_IS_OBJECT(val) &&
NS_SUCCEEDED(xpc->WrapJS(aCx, JSVAL_TO_OBJECT(val),
NS_GET_IID(nsIDOMWorkerMessageListener),
getter_AddRefs(listener)))) {
SetMessageListener(listener);
}
return PR_TRUE;
}
nsDOMWorkerTimeout*
nsDOMWorkerThread::FirstTimeout()
{
// Only called within the lock!
PRCList* first = PR_LIST_HEAD(&mTimeouts);
return first == &mTimeouts ?
nsnull :
static_cast<nsDOMWorkerTimeout*>(first);
}
nsDOMWorkerTimeout*
nsDOMWorkerThread::NextTimeout(nsDOMWorkerTimeout* aTimeout)
{
// Only called within the lock!
nsDOMWorkerTimeout* next =
static_cast<nsDOMWorkerTimeout*>(PR_NEXT_LINK(aTimeout));
return next == &mTimeouts ? nsnull : next;
}
void
nsDOMWorkerThread::AddTimeout(nsDOMWorkerTimeout* aTimeout)
{
// This should only ever be called on the worker thread... but there's no way
// to really assert that since we're using a thread pool.
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aTimeout, "Null pointer!");
PRIntervalTime newInterval = aTimeout->GetInterval();
if (IsSuspended()) {
aTimeout->Suspend(PR_Now());
}
nsAutoLock lock(mLock);
// XXX Currently stored in the order that they should execute (like the window
// timeouts are) but we don't flush all expired timeouts the same way that
// the window does... Either we should or this is unnecessary.
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
timeout;
timeout = NextTimeout(timeout)) {
if (timeout->GetInterval() > newInterval) {
PR_INSERT_BEFORE(aTimeout, timeout);
return;
}
}
PR_APPEND_LINK(aTimeout, &mTimeouts);
}
void
nsDOMWorkerThread::RemoveTimeout(nsDOMWorkerTimeout* aTimeout)
{
nsAutoLock lock(mLock);
PR_REMOVE_LINK(aTimeout);
}
void
nsDOMWorkerThread::ClearTimeouts()
{
nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
{
nsAutoLock lock(mLock);
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
timeout;
timeout = NextTimeout(timeout)) {
timeouts.AppendElement(timeout);
}
}
PRUint32 count = timeouts.Length();
for (PRUint32 i = 0; i < count; i++) {
timeouts[i]->Cancel();
}
}
void
nsDOMWorkerThread::CancelTimeout(PRUint32 aId)
{
nsRefPtr<nsDOMWorkerTimeout> foundTimeout;
{
nsAutoLock lock(mLock);
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
timeout;
timeout = NextTimeout(timeout)) {
if (timeout->GetId() == aId) {
foundTimeout = timeout;
break;
}
}
}
if (foundTimeout) {
foundTimeout->Cancel();
}
}
void
nsDOMWorkerThread::SuspendTimeouts()
{
nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
{
nsAutoLock lock(mLock);
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
timeout;
timeout = NextTimeout(timeout)) {
timeouts.AppendElement(timeout);
}
}
PRTime now = PR_Now();
PRUint32 count = timeouts.Length();
for (PRUint32 i = 0; i < count; i++) {
timeouts[i]->Suspend(now);
}
}
void
nsDOMWorkerThread::ResumeTimeouts()
{
nsAutoTArray<nsRefPtr<nsDOMWorkerTimeout>, 20> timeouts;
{
nsAutoLock lock(mLock);
for (nsDOMWorkerTimeout* timeout = FirstTimeout();
timeout;
timeout = NextTimeout(timeout)) {
NS_ASSERTION(timeout->IsSuspended(), "Should be suspended!");
timeouts.AppendElement(timeout);
}
}
PRTime now = PR_Now();
PRUint32 count = timeouts.Length();
for (PRUint32 i = 0; i < count; i++) {
timeouts[i]->Resume(now);
}
}
NS_IMETHODIMP
nsDOMWorkerThread::PostMessage(const nsAString& aMessage)
{
nsresult rv = PostMessageInternal(aMessage);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,186 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
* Ben Turner <bent.mozilla@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSDOMWORKERTHREAD_H__
#define __NSDOMWORKERTHREAD_H__
// Bases
#include "nsDOMWorkerBase.h"
#include "nsIClassInfo.h"
#include "nsIDOMThreads.h"
// Other includes
#include "jsapi.h"
#include "nsCOMPtr.h"
#include "nsStringGlue.h"
#include "nsThreadUtils.h"
#include "prclist.h"
#include "prlock.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
// Macro to generate nsIClassInfo methods for these threadsafe DOM classes
#define NS_IMPL_THREADSAFE_DOM_CI(_class) \
NS_IMETHODIMP \
_class::GetInterfaces(PRUint32* _count, nsIID*** _array) \
{ \
return NS_CI_INTERFACE_GETTER_NAME(_class)(_count, _array); \
} \
\
NS_IMETHODIMP \
_class::GetHelperForLanguage(PRUint32 _language, nsISupports** _retval) \
{ \
*_retval = nsnull; \
return NS_OK; \
} \
\
NS_IMETHODIMP \
_class::GetContractID(char** _contractID) \
{ \
*_contractID = nsnull; \
return NS_OK; \
} \
\
NS_IMETHODIMP \
_class::GetClassDescription(char** _classDescription) \
{ \
*_classDescription = nsnull; \
return NS_OK; \
} \
\
NS_IMETHODIMP \
_class::GetClassID(nsCID** _classID) \
{ \
*_classID = nsnull; \
return NS_OK; \
} \
\
NS_IMETHODIMP \
_class::GetImplementationLanguage(PRUint32* _language) \
{ \
*_language = nsIProgrammingLanguage::CPLUSPLUS; \
return NS_OK; \
} \
\
NS_IMETHODIMP \
_class::GetFlags(PRUint32* _flags) \
{ \
*_flags = nsIClassInfo::THREADSAFE | nsIClassInfo::DOM_OBJECT; \
return NS_OK; \
} \
\
NS_IMETHODIMP \
_class::GetClassIDNoAlloc(nsCID* _classIDNoAlloc) \
{ \
return NS_ERROR_NOT_AVAILABLE; \
}
class nsDOMWorkerPool;
class nsDOMWorkerTimeout;
class nsDOMWorkerThread : public nsDOMWorkerBase,
public nsIDOMWorkerThread,
public nsIClassInfo
{
friend class nsDOMCreateJSContextRunnable;
friend class nsDOMWorkerFunctions;
friend class nsDOMWorkerPool;
friend class nsDOMWorkerRunnable;
friend class nsDOMWorkerTimeout;
friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMWORKERTHREAD
NS_DECL_NSICLASSINFO
nsDOMWorkerThread(nsDOMWorkerPool* aPool,
const nsAString& aSource);
virtual nsDOMWorkerPool* Pool() {
NS_ASSERTION(!IsCanceled(), "Don't touch Pool after we've been canceled!");
return mPool;
}
private:
virtual ~nsDOMWorkerThread();
nsresult Init();
// For nsDOMWorkerBase
virtual nsresult HandleMessage(const nsAString& aMessage,
nsDOMWorkerBase* aSourceThread);
// For nsDOMWorkerBase
virtual nsresult DispatchMessage(nsIRunnable* aRunnable);
virtual void Cancel();
virtual void Suspend();
virtual void Resume();
PRBool SetGlobalForContext(JSContext* aCx);
PRBool CompileGlobalObject(JSContext* aCx);
inline nsDOMWorkerTimeout* FirstTimeout();
inline nsDOMWorkerTimeout* NextTimeout(nsDOMWorkerTimeout* aTimeout);
void AddTimeout(nsDOMWorkerTimeout* aTimeout);
void RemoveTimeout(nsDOMWorkerTimeout* aTimeout);
void ClearTimeouts();
void CancelTimeout(PRUint32 aId);
void SuspendTimeouts();
void ResumeTimeouts();
nsDOMWorkerPool* mPool;
nsString mSource;
JSObject* mGlobal;
PRBool mCompiled;
PRUint32 mCallbackCount;
PRUint32 mNextTimeoutId;
PRLock* mLock;
PRCList mTimeouts;
};
#endif /* __NSDOMWORKERTHREAD_H__ */

Просмотреть файл

@ -0,0 +1,515 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsDOMWorkerTimeout.h"
// Interfaces
#include "nsIJSContextStack.h"
#include "nsIJSRuntimeService.h"
#include "nsITimer.h"
#include "nsIXPConnect.h"
// Other includes
#include "nsContentUtils.h"
#include "nsJSUtils.h"
#include "pratom.h"
// DOMWorker includes
#include "nsDOMThreadService.h"
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
#define CONSTRUCTOR_ENSURE_TRUE(_cond, _rv) \
PR_BEGIN_MACRO \
if (NS_UNLIKELY(!(_cond))) { \
NS_WARNING("CONSTRUCTOR_ENSURE_TRUE(" #_cond ") failed"); \
(_rv) = NS_ERROR_FAILURE; \
return; \
} \
PR_END_MACRO
#define SUSPEND_SPINLOCK_COUNT 5000
static const char* kSetIntervalStr = "setInterval";
static const char* kSetTimeoutStr = "setTimeout";
nsDOMWorkerTimeout::FunctionCallback::FunctionCallback(PRUint32 aArgc,
jsval* aArgv,
nsresult* aRv)
: mCallback(nsnull),
mCallbackArgs(nsnull),
mCallbackArgsLength(0)
{
MOZ_COUNT_CTOR(nsDOMWorkerTimeout::FunctionCallback);
JSRuntime* rt;
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_ENSURE_SUCCESS(*aRv,);
PRBool success = JS_AddNamedRootRT(rt, &mCallback,
"nsDOMWorkerTimeout Callback Object");
CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
mCallback = aArgv[0];
// We want enough space for an extra lateness arg.
mCallbackArgsLength = aArgc > 2 ? aArgc - 1 : 1;
mCallbackArgs = new jsval[mCallbackArgsLength];
if (NS_UNLIKELY(!mCallbackArgs)) {
// Reset this!
mCallbackArgsLength = 0;
NS_ERROR("Out of memory!");
*aRv = NS_ERROR_OUT_OF_MEMORY;
return;
}
for (PRUint32 i = 0; i < mCallbackArgsLength - 1; i++) {
mCallbackArgs[i] = aArgv[i + 2];
success = JS_AddNamedRootRT(rt, &mCallbackArgs[i],
"nsDOMWorkerTimeout Callback Arg");
if (NS_UNLIKELY(!success)) {
// Set this to i so that the destructor only unroots the right number of
// values.
mCallbackArgsLength = i;
NS_WARNING("Failed to add root!");
*aRv = NS_ERROR_FAILURE;
return;
}
}
// Take care of the last arg.
mCallbackArgs[mCallbackArgsLength - 1] = 0;
success = JS_AddNamedRootRT(rt, &mCallbackArgs[mCallbackArgsLength - 1],
"nsDOMWorkerTimeout Callback Final Arg");
if (NS_UNLIKELY(!success)) {
// Decrement this so that the destructor only unroots the right number of
// values.
mCallbackArgsLength -= 1;
NS_WARNING("Failed to add root!");
*aRv = NS_ERROR_FAILURE;
return;
}
*aRv = NS_OK;
}
nsDOMWorkerTimeout::FunctionCallback::~FunctionCallback()
{
MOZ_COUNT_DTOR(nsDOMWorkerTimeout::FunctionCallback);
if (mCallback) {
JSRuntime* rt;
nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
if (NS_SUCCEEDED(rv)) {
for (PRUint32 i = 0; i < mCallbackArgsLength; i++) {
JS_RemoveRootRT(rt, &mCallbackArgs[i]);
}
JS_RemoveRootRT(rt, &mCallback);
}
}
delete [] mCallbackArgs;
}
nsresult
nsDOMWorkerTimeout::FunctionCallback::Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx)
{
PRInt32 lateness = PR_MAX(0, PRInt32(PR_Now() - aTimeout->mTargetTime)) /
(PRTime)PR_USEC_PER_MSEC;
mCallbackArgs[mCallbackArgsLength - 1] = INT_TO_JSVAL(lateness);
JSObject* global = JS_GetGlobalObject(aCx);
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
jsval rval;
PRBool success =
JS_CallFunctionValue(aCx, global, mCallback, mCallbackArgsLength,
mCallbackArgs, &rval);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
return NS_OK;
}
nsDOMWorkerTimeout::ExpressionCallback::ExpressionCallback(PRUint32 aArgc,
jsval* aArgv,
JSContext* aCx,
nsresult* aRv)
: mExpression(nsnull),
mLineNumber(0)
{
MOZ_COUNT_CTOR(nsDOMWorkerTimeout::ExpressionCallback);
JSString* expr = JS_ValueToString(aCx, aArgv[0]);
*aRv = expr ? NS_OK : NS_ERROR_FAILURE;
NS_ENSURE_SUCCESS(*aRv,);
JSRuntime* rt;
*aRv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_ENSURE_SUCCESS(*aRv,);
PRBool success = JS_AddNamedRootRT(rt, &mExpression,
"nsDOMWorkerTimeout Expression");
CONSTRUCTOR_ENSURE_TRUE(success, *aRv);
mExpression = expr;
// Get the calling location.
const char* fileName;
PRUint32 lineNumber;
if (nsJSUtils::GetCallingLocation(aCx, &fileName, &lineNumber, nsnull)) {
CopyUTF8toUTF16(nsDependentCString(fileName), mFileName);
mLineNumber = lineNumber;
}
*aRv = NS_OK;
}
nsDOMWorkerTimeout::ExpressionCallback::~ExpressionCallback()
{
MOZ_COUNT_DTOR(nsDOMWorkerTimeout::ExpressionCallback);
if (mExpression) {
JSRuntime* rt;
nsresult rv = nsDOMThreadService::JSRuntimeService()->GetRuntime(&rt);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Can't unroot callback objects!");
if (NS_SUCCEEDED(rv)) {
JS_RemoveRootRT(rt, &mExpression);
}
}
}
nsresult
nsDOMWorkerTimeout::ExpressionCallback::Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx)
{
NS_ERROR("Not yet implemented!");
return NS_ERROR_NOT_IMPLEMENTED;
}
nsDOMWorkerTimeout::nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker,
PRUint32 aId)
: mWorker(aWorker),
mInterval(0),
mIsInterval(PR_FALSE),
mId(aId),
mSuspendSpinlock(0),
mIsSuspended(PR_FALSE),
mSuspendInterval(0)
#ifdef DEBUG
, mFiredOrCanceled(PR_FALSE)
#endif
{
MOZ_COUNT_CTOR(nsDOMWorkerTimeout);
NS_ASSERTION(mWorker, "Need a worker here!");
}
nsDOMWorkerTimeout::~nsDOMWorkerTimeout()
{
MOZ_COUNT_DTOR(nsDOMWorkerTimeout);
// If we have a timer then we assume we added ourselves to the thread's list.
if (mTimer) {
NS_ASSERTION(mFiredOrCanceled || mWorker->IsCanceled(),
"Timeout should have fired or been canceled!");
mWorker->RemoveTimeout(this);
}
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeout, nsITimerCallback)
nsresult
nsDOMWorkerTimeout::Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
PRBool aIsInterval)
{
NS_ASSERTION(aCx, "Null pointer!");
NS_ASSERTION(aArgv, "Null pointer!");
JSAutoRequest ar(aCx);
if (!aArgc) {
JS_ReportError(aCx, "Function %s requires at least 1 parameter",
aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
return NS_ERROR_INVALID_ARG;
}
PRUint32 interval;
if (aArgc > 1) {
if (!JS_ValueToECMAUint32(aCx, aArgv[1], (uint32*)&interval)) {
JS_ReportError(aCx, "Second argument to %s must be a millisecond value",
aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
return NS_ERROR_INVALID_ARG;
}
}
else {
// If no interval was specified, treat this like a timeout, to avoid
// setting an interval of 0 milliseconds.
aIsInterval = PR_FALSE;
}
mInterval = interval;
mTargetTime = PR_Now() + interval * (PRTime)PR_USEC_PER_MSEC;
nsresult rv;
switch (JS_TypeOfValue(aCx, aArgv[0])) {
case JSTYPE_FUNCTION:
mCallback = new FunctionCallback(aArgc, aArgv, &rv);
NS_ENSURE_TRUE(mCallback, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
break;
case JSTYPE_STRING:
case JSTYPE_OBJECT:
mCallback = new ExpressionCallback(aArgc, aArgv, aCx, &rv);
NS_ENSURE_TRUE(mCallback, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
break;
default:
JS_ReportError(aCx, "useless %s call (missing quotes around argument?)",
aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
// Return an error that nsGlobalWindow can recognize and turn into NS_OK.
return NS_ERROR_INVALID_ARG;
}
PRInt32 type;
if (aIsInterval) {
type = nsITimer::TYPE_REPEATING_SLACK;
}
else {
type = nsITimer::TYPE_ONE_SHOT;
}
mIsInterval = aIsInterval;
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsIEventTarget* target =
static_cast<nsIEventTarget*>(nsDOMThreadService::get());
rv = mTimer->SetTarget(target);
NS_ENSURE_SUCCESS(rv, rv);
rv = mTimer->InitWithCallback(this, interval, type);
NS_ENSURE_SUCCESS(rv, rv);
mWorker->AddTimeout(this);
return NS_OK;
}
nsresult
nsDOMWorkerTimeout::Run()
{
NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
LOG(("Worker [0x%p] running timeout [0x%p] with id %u",
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
#ifdef DEBUG
mFiredOrCanceled = PR_TRUE;
#endif
JSContext* cx;
nsresult rv =
nsDOMThreadService::ThreadJSContextStack()->GetSafeJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
JSAutoRequest ar(cx);
rv = mCallback->Run(this, cx);
// Make sure any pending exceptions are converted to errors for the pool.
JS_ReportPendingException(cx);
if (mIsInterval) {
mTargetTime = PR_Now() + mInterval * (PRTime)PR_USEC_PER_MSEC;
}
return rv;
}
void
nsDOMWorkerTimeout::Cancel()
{
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
LOG(("Worker [0x%p] canceling timeout [0x%p] with id %u",
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
#ifdef DEBUG
mFiredOrCanceled = PR_TRUE;
#endif
{
AutoSpinlock lock(this);
if (IsSuspendedNoLock()) {
mIsSuspended = PR_FALSE;
// This should kill us when all is said and done.
mSuspendedRef = nsnull;
}
}
// This call to Cancel should kill us.
mTimer->Cancel();
}
void
nsDOMWorkerTimeout::Suspend(PRTime aNow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
AutoSpinlock lock(this);
if (!mIsSuspended) {
mIsSuspended = PR_TRUE;
mSuspendedRef = this;
}
mTimer->Cancel();
mSuspendInterval = PR_MAX(0, PRInt32(mTargetTime - aNow)) /
(PRTime)PR_USEC_PER_MSEC;
LOG(("Worker [0x%p] suspending timeout [0x%p] with id %u (interval = %u)",
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId,
mSuspendInterval));
}
void
nsDOMWorkerTimeout::Resume(PRTime aNow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mTimer, "Impossible to get here without a timer!");
LOG(("Worker [0x%p] resuming timeout [0x%p] with id %u",
static_cast<void*>(mWorker.get()), static_cast<void*>(this), mId));
AutoSpinlock lock(this);
NS_ASSERTION(IsSuspendedNoLock(), "Should be suspended!");
mTargetTime = aNow + mSuspendInterval * (PRTime)PR_USEC_PER_MSEC;
#ifdef DEBUG
nsresult rv =
#endif
mTimer->InitWithCallback(this, mSuspendInterval, nsITimer::TYPE_ONE_SHOT);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to init timer!");
}
void
nsDOMWorkerTimeout::AcquireSpinlock()
{
PRUint32 loopCount = 0;
while (PR_AtomicSet(&mSuspendSpinlock, 1) == 1) {
if (++loopCount > SUSPEND_SPINLOCK_COUNT) {
LOG(("AcquireSpinlock taking too long (looped %u times), yielding.",
loopCount));
loopCount = 0;
PR_Sleep(PR_INTERVAL_NO_WAIT);
}
}
#ifdef PR_LOGGING
if (loopCount) {
LOG(("AcquireSpinlock needed %u loops", loopCount));
}
#endif
}
void
nsDOMWorkerTimeout::ReleaseSpinlock()
{
#ifdef DEBUG
PRInt32 suspended =
#endif
PR_AtomicSet(&mSuspendSpinlock, 0);
NS_ASSERTION(suspended == 1, "Huh?!");
}
NS_IMETHODIMP
nsDOMWorkerTimeout::Notify(nsITimer* aTimer)
{
// Should be on the timer thread.
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aTimer == mTimer, "Wrong timer?!");
PRUint32 type;
nsresult rv = aTimer->GetType(&type);
NS_ENSURE_SUCCESS(rv, rv);
// We only care about one-shot timers here because that may be the one that
// we set from Resume().
if (type == nsITimer::TYPE_ONE_SHOT) {
AutoSpinlock lock(this);
if (mIsSuspended) {
if (mIsInterval) {
//LOG(("Timeout [0x%p] resuming normal interval (%u) with id %u",
//static_cast<void*>(this), mInterval, mId));
// This is the first fire since we resumed. Set our interval back to the
// real interval.
mTargetTime = PR_Now() + mInterval * (PRTime)PR_USEC_PER_MSEC;
rv = aTimer->InitWithCallback(this, mInterval,
nsITimer::TYPE_REPEATING_SLACK);
NS_ENSURE_SUCCESS(rv, rv);
}
mIsSuspended = PR_FALSE;
mSuspendedRef = nsnull;
}
}
nsDOMThreadService::get()->TimeoutReady(this);
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,189 @@
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is worker threads.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NSDOMWORKERTIMEOUT_H__
#define __NSDOMWORKERTIMEOUT_H__
// Interfaces
#include "nsITimer.h"
// Other includes
#include "jsapi.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsStringGlue.h"
#include "prclist.h"
// DOMWorker includes
#include "nsDOMWorkerThread.h"
/**
* The nsDOMWorkerTimeout has a slightly complicated life cycle. It's created
* by an nsDOMWorkerThread (or one of its JS context functions) and immediately
* takes a strong reference to the worker that created it. It does this so that
* the worker can't be collected while a timeout is outstanding. However, the
* worker needs a weak reference to the timeout so that it can be canceled if
* the worker is canceled (in the event that the page falls out of the fastback
* cache or the application is exiting, for instance). The only thing that holds
* the timeout alive is it's mTimer via the nsITimerCallback interface. If the
* timer is single-shot and has run already or if the timer is canceled then
* this object should die.
*/
class nsDOMWorkerTimeout : public PRCList,
public nsITimerCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
nsDOMWorkerTimeout(nsDOMWorkerThread* aWorker, PRUint32 aId);
~nsDOMWorkerTimeout();
nsresult Init(JSContext* aCx, PRUint32 aArgc, jsval* aArgv,
PRBool aIsInterval);
nsresult Run();
void Cancel();
void Suspend(PRTime aNow);
void Resume(PRTime aNow);
PRIntervalTime GetInterval() {
return mInterval;
}
nsDOMWorkerThread* GetWorker() {
return mWorker;
}
PRUint32 GetId() {
return mId;
}
PRBool IsSuspended() {
AutoSpinlock lock(this);
return IsSuspendedNoLock();
}
private:
void AcquireSpinlock();
void ReleaseSpinlock();
PRBool IsSuspendedNoLock() {
return mIsSuspended;
}
class AutoSpinlock
{
public:
AutoSpinlock(nsDOMWorkerTimeout* aTimeout)
: mTimeout(aTimeout) {
aTimeout->AcquireSpinlock();
}
~AutoSpinlock() {
mTimeout->ReleaseSpinlock();
}
private:
nsDOMWorkerTimeout* mTimeout;
};
// We support two types of callbacks (functions and expressions) just like the
// normal window timeouts. Each type has its own member and rooting needs so
// we split them into two classes with a common base.
class CallbackBase
{
public:
virtual ~CallbackBase() { }
virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx) = 0;
};
class FunctionCallback : public CallbackBase
{
public:
FunctionCallback(PRUint32 aArgc, jsval* aArgv, nsresult* aRv);
virtual ~FunctionCallback();
virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx);
protected:
jsval mCallback;
jsval* mCallbackArgs;
PRUint32 mCallbackArgsLength;
};
class ExpressionCallback : public CallbackBase
{
public:
ExpressionCallback(PRUint32 aArgc, jsval* aArgv, JSContext* aCx,
nsresult* aRv);
virtual ~ExpressionCallback();
virtual nsresult Run(nsDOMWorkerTimeout* aTimeout,
JSContext* aCx);
protected:
JSString* mExpression;
nsString mFileName;
PRUint32 mLineNumber;
};
// Hold the worker alive!
nsRefPtr<nsDOMWorkerThread> mWorker;
// Hold this object alive!
nsCOMPtr<nsITimer> mTimer;
PRUint32 mInterval;
PRBool mIsInterval;
PRTime mTargetTime;
nsAutoPtr<CallbackBase> mCallback;
PRUint32 mId;
PRInt32 mSuspendSpinlock;
PRBool mIsSuspended;
PRUint32 mSuspendInterval;
nsRefPtr<nsDOMWorkerTimeout> mSuspendedRef;
#ifdef DEBUG
PRBool mFiredOrCanceled;
#endif
};
#endif /* __NSDOMWORKERTIMEOUT_H__ */

Просмотреть файл

@ -0,0 +1,57 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is worker threads.
#
# The Initial Developer of the Original Code is
# Mozilla Corporation
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
# Ben Turner <bent.mozilla@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = dom/src/threads/tests
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
test_simpleThread.html \
test_threadErrors.html \
test_threadTimeouts.html \
test_longThread.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)

Просмотреть файл

@ -0,0 +1,82 @@
<!DOCTYPE HTML>
<html>
<!--
Tests of DOM Worker Threads (Bug 437152)
-->
<head>
<title>Test for DOM Worker Threads (Bug 437152)</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
prefs.setIntPref("javascript.options.gczeal", 2);
var workerScript =
"function messageListener(message, source) { " +
" switch (message) { " +
" case 'start': " +
" /* do a ton of stuff! */ " +
" for (var i = 0; i < 10000000; i++) { } " +
" dump('done!\\n'); " +
" /* pass message to source */ " +
" source.postMessage('done'); " +
" break; " +
" default: " +
" throw 'Bad message: ' + message; " +
" } " +
"} " +
"";
var pool = navigator.newWorkerPool();
ok(pool, "Couldn't get worker pool");
const numThreads = 10;
var doneThreads = 0;
pool.messageListener = function(message, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
switch (message) {
case "done":
if (++doneThreads == numThreads) {
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
break;
default:
ok(false, "Unexpected message");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
pool.errorListener = function(error, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(false, "Worker had an error");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
};
for (var i = 0; i < numThreads; i++) {
var worker = pool.createWorker(workerScript);
worker.postMessage("start");
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,97 @@
<!DOCTYPE HTML>
<html>
<!--
Tests of DOM Worker Threads (Bug 437152)
-->
<head>
<title>Test for DOM Worker Threads (Bug 437152)</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
prefs.setIntPref("javascript.options.gczeal", 2);
var workerScript =
"function messageListener(message, source) { " +
" switch (message) { " +
" case 'no-op': " +
" break; " +
" case 'start': " +
" /* do a ton of stuff! */ " +
" for (var i = 0; i < 1000; i++) { } " +
" /* pass message to source */ " +
" source.postMessage('started'); " +
" break; " +
" case 'stop': " +
" /* do some more stuff! */ " +
" for (var i = 0; i < 1000; i++) { } " +
" /* pass message to self */ " +
" threadContext.thisThread.postMessage('no-op'); " +
" /* pass message to pool */ " +
" postMessageToPool('stopped'); " +
" break; " +
" default: " +
" throw 'Bad message: ' + message; " +
" } " +
"} " +
"";
var pool = navigator.newWorkerPool();
ok(pool, "Couldn't get worker pool");
pool.messageListener = function(message, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
switch (message) {
case "no-op":
break;
case "started":
// pass message to self
pool.postMessage("no-op");
// pass message to source
source.postMessage("stop");
break;
case "stopped":
// pass message to worker
worker.postMessage("no-op");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
break;
default:
ok(false, "Unexpected message");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
pool.errorListener = function(error, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(false, "Worker had an error");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
};
var worker = pool.createWorker(workerScript);
ok(worker, "Couldn't make worker");
pool.postMessage("no-op");
worker.postMessage("start");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,100 @@
<!DOCTYPE HTML>
<html>
<!--
Tests of DOM Worker Threads (Bug 437152)
-->
<head>
<title>Test for DOM Worker Threads (Bug 437152)</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
prefs.setIntPref("javascript.options.gczeal", 2);
var badWorkerScripts = [
// Syntax error
"function messageListener(message, source) { " +
" for (var i = 0; i < 10) { } " +
"} " +
"",
// Bad function error
"function messageListener(message, source) { " +
" foopy(); " +
"} " +
"",
// Unhandled exception in body
"function messageListener(message, source) { " +
"} " +
"throw new Error('Bah!'); " +
"",
// Throwing message listener
"function messageListener(message, source) { " +
" throw 'Bad message: ' + message; " +
"} " +
""
];
var expectedErrorCount = badWorkerScripts.length;
var pool = navigator.newWorkerPool();
pool.messageListener = function(message, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
prefs.setIntPref("javascript.options.gczeal", 0);
ok(false, "Unexpected message");
SimpleTest.finish();
};
var actualErrorCount = 0;
var failedWorkers = [];
pool.errorListener = function(error, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
dump("Worker[" + actualErrorCount + "]: " + source + "\n\nError: " + error + "\n\nArray: " + failedWorkers + "\n\n");
if (failedWorkers.indexOf(source) != -1) {
dump("Already seen worker: " + source + "\n");
ok(false, "Seen an extra error from this worker");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
return;
}
failedWorkers.push(source);
actualErrorCount++;
if (actualErrorCount == expectedErrorCount) {
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
for (var i = 0; i < expectedErrorCount; i++) {
var worker = pool.createWorker(badWorkerScripts[i]);
worker.postMessage("Hi");
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,105 @@
<!DOCTYPE HTML>
<html>
<!--
Tests of DOM Worker Threads (Bug 437152)
-->
<head>
<title>Test for DOM Worker Threads (Bug 437152)</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
prefs.setIntPref("javascript.options.gczeal", 2);
var workerScript =
"var gTimeoutId; " +
"var gTimeoutCount = 0; " +
"var gIntervalCount = 0; " +
"" +
"function timeoutFunc() { " +
" if (++gTimeoutCount > 1) { " +
" throw new Error('Timeout called more than once!'); " +
" } " +
" postMessageToPool('timeoutFinished'); " +
"} " +
"" +
"function intervalFunc() { " +
" if (++gIntervalCount == 2) { " +
" postMessageToPool('intervalFinished'); " +
" } " +
"} " +
"" +
"function messageListener(message, source) { " +
" switch (message) { " +
" case 'startTimeout': " +
" gTimeoutId = setTimeout(timeoutFunc, 5000); " +
" clearTimeout(gTimeoutId); " +
" gTimeoutId = setTimeout(timeoutFunc, 5000); " +
" break; " +
" case 'startInterval': " +
" gTimeoutId = setInterval(intervalFunc, 5000); " +
" break; " +
" case 'cancelInterval': " +
" clearInterval(gTimeoutId); " +
" postMessageToPool('intervalCanceled'); " +
" break; " +
" default: " +
" throw 'Bad message: ' + message; " +
" } " +
"} " +
"";
var pool = navigator.newWorkerPool();
ok(pool, "Couldn't get worker pool");
pool.messageListener = function(message, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
switch (message) {
case "timeoutFinished":
source.postMessage("startInterval");
break;
case "intervalFinished":
source.postMessage("cancelInterval");
break;
case "intervalCanceled":
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
break;
default:
ok(false, "Unexpected message");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
}
};
pool.errorListener = function(error, source) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(false, "Worker had an error");
prefs.setIntPref("javascript.options.gczeal", 0);
SimpleTest.finish();
};
var worker = pool.createWorker(workerScript);
ok(worker, "Couldn't make worker");
worker.postMessage("startTimeout");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -139,6 +139,7 @@ SHARED_LIBRARY_LIBS = \
$(DEPTH)/dom/src/storage/$(LIB_PREFIX)jsdomstorage_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/offline/$(LIB_PREFIX)jsdomoffline_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/geolocation/$(LIB_PREFIX)jsdomgeolocation_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/threads/$(LIB_PREFIX)domthreads_s.$(LIB_SUFFIX) \
$(DEPTH)/editor/libeditor/text/$(LIB_PREFIX)texteditor_s.$(LIB_SUFFIX) \
$(DEPTH)/editor/libeditor/base/$(LIB_PREFIX)editorbase_s.$(LIB_SUFFIX) \
$(NULL)
@ -287,6 +288,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \
-I$(topsrcdir)/dom/src/storage \
-I$(topsrcdir)/dom/src/offline \
-I$(topsrcdir)/dom/src/geolocation \
-I$(topsrcdir)/dom/src/threads \
-I. \
-I$(topsrcdir)/editor/libeditor/base \
-I$(topsrcdir)/editor/libeditor/text \

Просмотреть файл

@ -83,6 +83,7 @@
#include "nsCSSRuleProcessor.h"
#include "nsXMLHttpRequest.h"
#include "nsIFocusEventSuppressor.h"
#include "nsDOMThreadService.h"
#ifdef MOZ_XUL
#include "nsXULPopupManager.h"
@ -336,6 +337,8 @@ nsLayoutStatics::Shutdown()
nsTextServicesDocument::Shutdown();
#endif
nsDOMThreadService::Shutdown();
NS_ShutdownFocusSuppressor();
#ifdef MOZ_OGG