зеркало из https://github.com/mozilla/gecko-dev.git
Bug 437152 - "implement worker threads". r+sr=jst.
This commit is contained in:
Родитель
8e8f112b0a
Коммит
8f2cbea28f
|
@ -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(¤tThreadCount);
|
||||
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче