Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "RuntimeService.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "nsIChannel.h" |
michael@0 | 10 | #include "nsIContentSecurityPolicy.h" |
michael@0 | 11 | #include "nsIDocument.h" |
michael@0 | 12 | #include "nsIDOMChromeWindow.h" |
michael@0 | 13 | #include "nsIEffectiveTLDService.h" |
michael@0 | 14 | #include "nsIObserverService.h" |
michael@0 | 15 | #include "nsIPrincipal.h" |
michael@0 | 16 | #include "nsIScriptContext.h" |
michael@0 | 17 | #include "nsIScriptSecurityManager.h" |
michael@0 | 18 | #include "nsISupportsPriority.h" |
michael@0 | 19 | #include "nsITimer.h" |
michael@0 | 20 | #include "nsIURI.h" |
michael@0 | 21 | #include "nsPIDOMWindow.h" |
michael@0 | 22 | |
michael@0 | 23 | #include <algorithm> |
michael@0 | 24 | #include "GeckoProfiler.h" |
michael@0 | 25 | #include "js/OldDebugAPI.h" |
michael@0 | 26 | #include "jsfriendapi.h" |
michael@0 | 27 | #include "mozilla/ArrayUtils.h" |
michael@0 | 28 | #include "mozilla/CycleCollectedJSRuntime.h" |
michael@0 | 29 | #include "mozilla/dom/asmjscache/AsmJSCache.h" |
michael@0 | 30 | #include "mozilla/dom/AtomList.h" |
michael@0 | 31 | #include "mozilla/dom/BindingUtils.h" |
michael@0 | 32 | #include "mozilla/dom/ErrorEventBinding.h" |
michael@0 | 33 | #include "mozilla/dom/EventTargetBinding.h" |
michael@0 | 34 | #include "mozilla/dom/MessageEventBinding.h" |
michael@0 | 35 | #include "mozilla/dom/WorkerBinding.h" |
michael@0 | 36 | #include "mozilla/DebugOnly.h" |
michael@0 | 37 | #include "mozilla/Preferences.h" |
michael@0 | 38 | #include "mozilla/dom/Navigator.h" |
michael@0 | 39 | #include "nsContentUtils.h" |
michael@0 | 40 | #include "nsCycleCollector.h" |
michael@0 | 41 | #include "nsDOMJSUtils.h" |
michael@0 | 42 | #include "nsISupportsImpl.h" |
michael@0 | 43 | #include "nsLayoutStatics.h" |
michael@0 | 44 | #include "nsNetUtil.h" |
michael@0 | 45 | #include "nsServiceManagerUtils.h" |
michael@0 | 46 | #include "nsThread.h" |
michael@0 | 47 | #include "nsThreadUtils.h" |
michael@0 | 48 | #include "nsXPCOM.h" |
michael@0 | 49 | #include "nsXPCOMPrivate.h" |
michael@0 | 50 | #include "OSFileConstants.h" |
michael@0 | 51 | #include "xpcpublic.h" |
michael@0 | 52 | |
michael@0 | 53 | #ifdef MOZ_NUWA_PROCESS |
michael@0 | 54 | #include "ipc/Nuwa.h" |
michael@0 | 55 | #endif |
michael@0 | 56 | |
michael@0 | 57 | #ifdef DEBUG |
michael@0 | 58 | #include "nsThreadManager.h" |
michael@0 | 59 | #endif |
michael@0 | 60 | |
michael@0 | 61 | #include "SharedWorker.h" |
michael@0 | 62 | #include "WorkerPrivate.h" |
michael@0 | 63 | #include "WorkerRunnable.h" |
michael@0 | 64 | |
michael@0 | 65 | using namespace mozilla; |
michael@0 | 66 | using namespace mozilla::dom; |
michael@0 | 67 | |
michael@0 | 68 | USING_WORKERS_NAMESPACE |
michael@0 | 69 | |
michael@0 | 70 | using mozilla::MutexAutoLock; |
michael@0 | 71 | using mozilla::MutexAutoUnlock; |
michael@0 | 72 | using mozilla::Preferences; |
michael@0 | 73 | |
michael@0 | 74 | // The size of the worker runtime heaps in bytes. May be changed via pref. |
michael@0 | 75 | #define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024 |
michael@0 | 76 | |
michael@0 | 77 | // The size of the worker JS allocation threshold in MB. May be changed via pref. |
michael@0 | 78 | #define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30 |
michael@0 | 79 | |
michael@0 | 80 | // The C stack size. We use the same stack size on all platforms for |
michael@0 | 81 | // consistency. |
michael@0 | 82 | #define WORKER_STACK_SIZE 256 * sizeof(size_t) * 1024 |
michael@0 | 83 | |
michael@0 | 84 | // Half the size of the actual C stack, to be safe. |
michael@0 | 85 | #define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024 |
michael@0 | 86 | |
michael@0 | 87 | // The maximum number of threads to use for workers, overridable via pref. |
michael@0 | 88 | #define MAX_WORKERS_PER_DOMAIN 10 |
michael@0 | 89 | |
michael@0 | 90 | static_assert(MAX_WORKERS_PER_DOMAIN >= 1, |
michael@0 | 91 | "We should allow at least one worker per domain."); |
michael@0 | 92 | |
michael@0 | 93 | // The default number of seconds that close handlers will be allowed to run for |
michael@0 | 94 | // content workers. |
michael@0 | 95 | #define MAX_SCRIPT_RUN_TIME_SEC 10 |
michael@0 | 96 | |
michael@0 | 97 | // The number of seconds that idle threads can hang around before being killed. |
michael@0 | 98 | #define IDLE_THREAD_TIMEOUT_SEC 30 |
michael@0 | 99 | |
michael@0 | 100 | // The maximum number of threads that can be idle at one time. |
michael@0 | 101 | #define MAX_IDLE_THREADS 20 |
michael@0 | 102 | |
michael@0 | 103 | #define PREF_WORKERS_PREFIX "dom.workers." |
michael@0 | 104 | #define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain" |
michael@0 | 105 | |
michael@0 | 106 | #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time" |
michael@0 | 107 | #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time" |
michael@0 | 108 | |
michael@0 | 109 | #define GC_REQUEST_OBSERVER_TOPIC "child-gc-request" |
michael@0 | 110 | #define CC_REQUEST_OBSERVER_TOPIC "child-cc-request" |
michael@0 | 111 | #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure" |
michael@0 | 112 | |
michael@0 | 113 | #define PREF_GENERAL_APPNAME_OVERRIDE "general.appname.override" |
michael@0 | 114 | #define PREF_GENERAL_APPVERSION_OVERRIDE "general.appversion.override" |
michael@0 | 115 | #define PREF_GENERAL_PLATFORM_OVERRIDE "general.platform.override" |
michael@0 | 116 | |
michael@0 | 117 | #define BROADCAST_ALL_WORKERS(_func, ...) \ |
michael@0 | 118 | PR_BEGIN_MACRO \ |
michael@0 | 119 | AssertIsOnMainThread(); \ |
michael@0 | 120 | \ |
michael@0 | 121 | nsAutoTArray<WorkerPrivate*, 100> workers; \ |
michael@0 | 122 | { \ |
michael@0 | 123 | MutexAutoLock lock(mMutex); \ |
michael@0 | 124 | \ |
michael@0 | 125 | mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers); \ |
michael@0 | 126 | } \ |
michael@0 | 127 | \ |
michael@0 | 128 | if (!workers.IsEmpty()) { \ |
michael@0 | 129 | AutoSafeJSContext cx; \ |
michael@0 | 130 | JSAutoRequest ar(cx); \ |
michael@0 | 131 | for (uint32_t index = 0; index < workers.Length(); index++) { \ |
michael@0 | 132 | workers[index]-> _func (cx, __VA_ARGS__); \ |
michael@0 | 133 | } \ |
michael@0 | 134 | } \ |
michael@0 | 135 | PR_END_MACRO |
michael@0 | 136 | |
michael@0 | 137 | // Prefixes for observing preference changes. |
michael@0 | 138 | #define PREF_JS_OPTIONS_PREFIX "javascript.options." |
michael@0 | 139 | #define PREF_WORKERS_OPTIONS_PREFIX PREF_WORKERS_PREFIX "options." |
michael@0 | 140 | #define PREF_MEM_OPTIONS_PREFIX "mem." |
michael@0 | 141 | #define PREF_GCZEAL "gcZeal" |
michael@0 | 142 | |
michael@0 | 143 | #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP)) |
michael@0 | 144 | #define DUMP_CONTROLLED_BY_PREF 1 |
michael@0 | 145 | #define PREF_DOM_WINDOW_DUMP_ENABLED "browser.dom.window.dump.enabled" |
michael@0 | 146 | #endif |
michael@0 | 147 | |
michael@0 | 148 | #define PREF_WORKERS_LATEST_JS_VERSION "dom.workers.latestJSVersion" |
michael@0 | 149 | |
michael@0 | 150 | namespace { |
michael@0 | 151 | |
michael@0 | 152 | const uint32_t kNoIndex = uint32_t(-1); |
michael@0 | 153 | |
michael@0 | 154 | const JS::ContextOptions kRequiredContextOptions = |
michael@0 | 155 | JS::ContextOptions().setDontReportUncaught(true) |
michael@0 | 156 | .setNoScriptRval(true); |
michael@0 | 157 | |
michael@0 | 158 | uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN; |
michael@0 | 159 | |
michael@0 | 160 | // Does not hold an owning reference. |
michael@0 | 161 | RuntimeService* gRuntimeService = nullptr; |
michael@0 | 162 | |
michael@0 | 163 | // Only non-null during the call to Init. |
michael@0 | 164 | RuntimeService* gRuntimeServiceDuringInit = nullptr; |
michael@0 | 165 | |
michael@0 | 166 | enum { |
michael@0 | 167 | ID_Worker = 0, |
michael@0 | 168 | ID_ChromeWorker, |
michael@0 | 169 | ID_Event, |
michael@0 | 170 | ID_MessageEvent, |
michael@0 | 171 | ID_ErrorEvent, |
michael@0 | 172 | |
michael@0 | 173 | ID_COUNT |
michael@0 | 174 | }; |
michael@0 | 175 | |
michael@0 | 176 | // These are jsids for the main runtime. Only touched on the main thread. |
michael@0 | 177 | jsid gStringIDs[ID_COUNT] = { JSID_VOID }; |
michael@0 | 178 | |
michael@0 | 179 | const char* gStringChars[] = { |
michael@0 | 180 | "Worker", |
michael@0 | 181 | "ChromeWorker", |
michael@0 | 182 | "Event", |
michael@0 | 183 | "MessageEvent", |
michael@0 | 184 | "ErrorEvent" |
michael@0 | 185 | |
michael@0 | 186 | // XXX Don't care about ProgressEvent since it should never leak to the main |
michael@0 | 187 | // thread. |
michael@0 | 188 | }; |
michael@0 | 189 | |
michael@0 | 190 | static_assert(MOZ_ARRAY_LENGTH(gStringChars) == ID_COUNT, |
michael@0 | 191 | "gStringChars should have the right length."); |
michael@0 | 192 | |
michael@0 | 193 | class LiteralRebindingCString : public nsDependentCString |
michael@0 | 194 | { |
michael@0 | 195 | public: |
michael@0 | 196 | template<int N> |
michael@0 | 197 | void RebindLiteral(const char (&aStr)[N]) |
michael@0 | 198 | { |
michael@0 | 199 | Rebind(aStr, N-1); |
michael@0 | 200 | } |
michael@0 | 201 | }; |
michael@0 | 202 | |
michael@0 | 203 | template <typename T> |
michael@0 | 204 | struct PrefTraits; |
michael@0 | 205 | |
michael@0 | 206 | template <> |
michael@0 | 207 | struct PrefTraits<bool> |
michael@0 | 208 | { |
michael@0 | 209 | typedef bool PrefValueType; |
michael@0 | 210 | |
michael@0 | 211 | static const PrefValueType kDefaultValue = false; |
michael@0 | 212 | |
michael@0 | 213 | static inline PrefValueType |
michael@0 | 214 | Get(const char* aPref) |
michael@0 | 215 | { |
michael@0 | 216 | AssertIsOnMainThread(); |
michael@0 | 217 | return Preferences::GetBool(aPref); |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | static inline bool |
michael@0 | 221 | Exists(const char* aPref) |
michael@0 | 222 | { |
michael@0 | 223 | AssertIsOnMainThread(); |
michael@0 | 224 | return Preferences::GetType(aPref) == nsIPrefBranch::PREF_BOOL; |
michael@0 | 225 | } |
michael@0 | 226 | }; |
michael@0 | 227 | |
michael@0 | 228 | template <> |
michael@0 | 229 | struct PrefTraits<int32_t> |
michael@0 | 230 | { |
michael@0 | 231 | typedef int32_t PrefValueType; |
michael@0 | 232 | |
michael@0 | 233 | static inline PrefValueType |
michael@0 | 234 | Get(const char* aPref) |
michael@0 | 235 | { |
michael@0 | 236 | AssertIsOnMainThread(); |
michael@0 | 237 | return Preferences::GetInt(aPref); |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | static inline bool |
michael@0 | 241 | Exists(const char* aPref) |
michael@0 | 242 | { |
michael@0 | 243 | AssertIsOnMainThread(); |
michael@0 | 244 | return Preferences::GetType(aPref) == nsIPrefBranch::PREF_INT; |
michael@0 | 245 | } |
michael@0 | 246 | }; |
michael@0 | 247 | |
michael@0 | 248 | template <typename T> |
michael@0 | 249 | T |
michael@0 | 250 | GetWorkerPref(const nsACString& aPref, |
michael@0 | 251 | const T aDefault = PrefTraits<T>::kDefaultValue) |
michael@0 | 252 | { |
michael@0 | 253 | AssertIsOnMainThread(); |
michael@0 | 254 | |
michael@0 | 255 | typedef PrefTraits<T> PrefHelper; |
michael@0 | 256 | |
michael@0 | 257 | T result; |
michael@0 | 258 | |
michael@0 | 259 | nsAutoCString prefName; |
michael@0 | 260 | prefName.AssignLiteral(PREF_WORKERS_OPTIONS_PREFIX); |
michael@0 | 261 | prefName.Append(aPref); |
michael@0 | 262 | |
michael@0 | 263 | if (PrefHelper::Exists(prefName.get())) { |
michael@0 | 264 | result = PrefHelper::Get(prefName.get()); |
michael@0 | 265 | } |
michael@0 | 266 | else { |
michael@0 | 267 | prefName.AssignLiteral(PREF_JS_OPTIONS_PREFIX); |
michael@0 | 268 | prefName.Append(aPref); |
michael@0 | 269 | |
michael@0 | 270 | if (PrefHelper::Exists(prefName.get())) { |
michael@0 | 271 | result = PrefHelper::Get(prefName.get()); |
michael@0 | 272 | } |
michael@0 | 273 | else { |
michael@0 | 274 | result = aDefault; |
michael@0 | 275 | } |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | return result; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | // This function creates a key for a SharedWorker composed by "name|scriptSpec". |
michael@0 | 282 | // If the name contains a '|', this will be replaced by '||'. |
michael@0 | 283 | void |
michael@0 | 284 | GenerateSharedWorkerKey(const nsACString& aScriptSpec, const nsACString& aName, |
michael@0 | 285 | nsCString& aKey) |
michael@0 | 286 | { |
michael@0 | 287 | aKey.Truncate(); |
michael@0 | 288 | aKey.SetCapacity(aScriptSpec.Length() + aName.Length() + 1); |
michael@0 | 289 | |
michael@0 | 290 | nsACString::const_iterator start, end; |
michael@0 | 291 | aName.BeginReading(start); |
michael@0 | 292 | aName.EndReading(end); |
michael@0 | 293 | for (; start != end; ++start) { |
michael@0 | 294 | if (*start == '|') { |
michael@0 | 295 | aKey.AppendASCII("||"); |
michael@0 | 296 | } else { |
michael@0 | 297 | aKey.Append(*start); |
michael@0 | 298 | } |
michael@0 | 299 | } |
michael@0 | 300 | |
michael@0 | 301 | aKey.Append('|'); |
michael@0 | 302 | aKey.Append(aScriptSpec); |
michael@0 | 303 | } |
michael@0 | 304 | |
michael@0 | 305 | void |
michael@0 | 306 | LoadRuntimeAndContextOptions(const char* aPrefName, void* /* aClosure */) |
michael@0 | 307 | { |
michael@0 | 308 | AssertIsOnMainThread(); |
michael@0 | 309 | |
michael@0 | 310 | RuntimeService* rts = RuntimeService::GetService(); |
michael@0 | 311 | if (!rts && !gRuntimeServiceDuringInit) { |
michael@0 | 312 | // May be shutting down, just bail. |
michael@0 | 313 | return; |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | const nsDependentCString prefName(aPrefName); |
michael@0 | 317 | |
michael@0 | 318 | // Several other pref branches will get included here so bail out if there is |
michael@0 | 319 | // another callback that will handle this change. |
michael@0 | 320 | if (StringBeginsWith(prefName, |
michael@0 | 321 | NS_LITERAL_CSTRING(PREF_JS_OPTIONS_PREFIX |
michael@0 | 322 | PREF_MEM_OPTIONS_PREFIX)) || |
michael@0 | 323 | StringBeginsWith(prefName, |
michael@0 | 324 | NS_LITERAL_CSTRING(PREF_WORKERS_OPTIONS_PREFIX |
michael@0 | 325 | PREF_MEM_OPTIONS_PREFIX))) { |
michael@0 | 326 | return; |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | #ifdef JS_GC_ZEAL |
michael@0 | 330 | if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) || |
michael@0 | 331 | prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) { |
michael@0 | 332 | return; |
michael@0 | 333 | } |
michael@0 | 334 | #endif |
michael@0 | 335 | |
michael@0 | 336 | // Runtime options. |
michael@0 | 337 | JS::RuntimeOptions runtimeOptions; |
michael@0 | 338 | if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs"))) { |
michael@0 | 339 | runtimeOptions.setAsmJS(true); |
michael@0 | 340 | } |
michael@0 | 341 | if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit"))) { |
michael@0 | 342 | runtimeOptions.setBaseline(true); |
michael@0 | 343 | } |
michael@0 | 344 | if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion"))) { |
michael@0 | 345 | runtimeOptions.setIon(true); |
michael@0 | 346 | } |
michael@0 | 347 | |
michael@0 | 348 | // Common options. |
michael@0 | 349 | JS::ContextOptions commonContextOptions = kRequiredContextOptions; |
michael@0 | 350 | if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict"))) { |
michael@0 | 351 | commonContextOptions.setExtraWarnings(true); |
michael@0 | 352 | } |
michael@0 | 353 | if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror"))) { |
michael@0 | 354 | commonContextOptions.setWerror(true); |
michael@0 | 355 | } |
michael@0 | 356 | |
michael@0 | 357 | // Content options. |
michael@0 | 358 | JS::ContextOptions contentContextOptions = commonContextOptions; |
michael@0 | 359 | |
michael@0 | 360 | // Chrome options. |
michael@0 | 361 | JS::ContextOptions chromeContextOptions = commonContextOptions; |
michael@0 | 362 | #ifdef DEBUG |
michael@0 | 363 | if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict.debug"))) { |
michael@0 | 364 | chromeContextOptions.setExtraWarnings(true); |
michael@0 | 365 | } |
michael@0 | 366 | #endif |
michael@0 | 367 | |
michael@0 | 368 | RuntimeService::SetDefaultRuntimeAndContextOptions(runtimeOptions, |
michael@0 | 369 | contentContextOptions, |
michael@0 | 370 | chromeContextOptions); |
michael@0 | 371 | |
michael@0 | 372 | if (rts) { |
michael@0 | 373 | rts->UpdateAllWorkerRuntimeAndContextOptions(); |
michael@0 | 374 | } |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | #ifdef JS_GC_ZEAL |
michael@0 | 378 | void |
michael@0 | 379 | LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) |
michael@0 | 380 | { |
michael@0 | 381 | AssertIsOnMainThread(); |
michael@0 | 382 | |
michael@0 | 383 | RuntimeService* rts = RuntimeService::GetService(); |
michael@0 | 384 | if (!rts && !gRuntimeServiceDuringInit) { |
michael@0 | 385 | // May be shutting down, just bail. |
michael@0 | 386 | return; |
michael@0 | 387 | } |
michael@0 | 388 | |
michael@0 | 389 | int32_t gczeal = GetWorkerPref<int32_t>(NS_LITERAL_CSTRING(PREF_GCZEAL), -1); |
michael@0 | 390 | if (gczeal < 0) { |
michael@0 | 391 | gczeal = 0; |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | int32_t frequency = |
michael@0 | 395 | GetWorkerPref<int32_t>(NS_LITERAL_CSTRING("gcZeal.frequency"), -1); |
michael@0 | 396 | if (frequency < 0) { |
michael@0 | 397 | frequency = JS_DEFAULT_ZEAL_FREQ; |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | RuntimeService::SetDefaultGCZeal(uint8_t(gczeal), uint32_t(frequency)); |
michael@0 | 401 | |
michael@0 | 402 | if (rts) { |
michael@0 | 403 | rts->UpdateAllWorkerGCZeal(); |
michael@0 | 404 | } |
michael@0 | 405 | } |
michael@0 | 406 | #endif |
michael@0 | 407 | |
michael@0 | 408 | void |
michael@0 | 409 | UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService, |
michael@0 | 410 | const nsACString& aPrefName, JSGCParamKey aKey) |
michael@0 | 411 | { |
michael@0 | 412 | AssertIsOnMainThread(); |
michael@0 | 413 | NS_ASSERTION(!aPrefName.IsEmpty(), "Empty pref name!"); |
michael@0 | 414 | |
michael@0 | 415 | int32_t prefValue = GetWorkerPref(aPrefName, -1); |
michael@0 | 416 | uint32_t value = |
michael@0 | 417 | (prefValue < 0 || prefValue >= 10000) ? 0 : uint32_t(prefValue); |
michael@0 | 418 | |
michael@0 | 419 | RuntimeService::SetDefaultJSGCSettings(aKey, value); |
michael@0 | 420 | |
michael@0 | 421 | if (aRuntimeService) { |
michael@0 | 422 | aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, value); |
michael@0 | 423 | } |
michael@0 | 424 | } |
michael@0 | 425 | |
michael@0 | 426 | void |
michael@0 | 427 | UpdatOtherJSGCMemoryOption(RuntimeService* aRuntimeService, |
michael@0 | 428 | JSGCParamKey aKey, uint32_t aValue) |
michael@0 | 429 | { |
michael@0 | 430 | AssertIsOnMainThread(); |
michael@0 | 431 | |
michael@0 | 432 | RuntimeService::SetDefaultJSGCSettings(aKey, aValue); |
michael@0 | 433 | |
michael@0 | 434 | if (aRuntimeService) { |
michael@0 | 435 | aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, aValue); |
michael@0 | 436 | } |
michael@0 | 437 | } |
michael@0 | 438 | |
michael@0 | 439 | |
michael@0 | 440 | void |
michael@0 | 441 | LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) |
michael@0 | 442 | { |
michael@0 | 443 | AssertIsOnMainThread(); |
michael@0 | 444 | |
michael@0 | 445 | RuntimeService* rts = RuntimeService::GetService(); |
michael@0 | 446 | |
michael@0 | 447 | if (!rts && !gRuntimeServiceDuringInit) { |
michael@0 | 448 | // May be shutting down, just bail. |
michael@0 | 449 | return; |
michael@0 | 450 | } |
michael@0 | 451 | |
michael@0 | 452 | NS_NAMED_LITERAL_CSTRING(jsPrefix, PREF_JS_OPTIONS_PREFIX); |
michael@0 | 453 | NS_NAMED_LITERAL_CSTRING(workersPrefix, PREF_WORKERS_OPTIONS_PREFIX); |
michael@0 | 454 | |
michael@0 | 455 | const nsDependentCString fullPrefName(aPrefName); |
michael@0 | 456 | |
michael@0 | 457 | // Pull out the string that actually distinguishes the parameter we need to |
michael@0 | 458 | // change. |
michael@0 | 459 | nsDependentCSubstring memPrefName; |
michael@0 | 460 | if (StringBeginsWith(fullPrefName, jsPrefix)) { |
michael@0 | 461 | memPrefName.Rebind(fullPrefName, jsPrefix.Length()); |
michael@0 | 462 | } |
michael@0 | 463 | else if (StringBeginsWith(fullPrefName, workersPrefix)) { |
michael@0 | 464 | memPrefName.Rebind(fullPrefName, workersPrefix.Length()); |
michael@0 | 465 | } |
michael@0 | 466 | else { |
michael@0 | 467 | NS_ERROR("Unknown pref name!"); |
michael@0 | 468 | return; |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | #ifdef DEBUG |
michael@0 | 472 | // During Init() we get called back with a branch string here, so there should |
michael@0 | 473 | // be no just a "mem." pref here. |
michael@0 | 474 | if (!rts) { |
michael@0 | 475 | NS_ASSERTION(memPrefName.EqualsLiteral(PREF_MEM_OPTIONS_PREFIX), "Huh?!"); |
michael@0 | 476 | } |
michael@0 | 477 | #endif |
michael@0 | 478 | |
michael@0 | 479 | // If we're running in Init() then do this for every pref we care about. |
michael@0 | 480 | // Otherwise we just want to update the parameter that changed. |
michael@0 | 481 | for (uint32_t index = rts ? JSSettings::kGCSettingsArraySize - 1 : 0; |
michael@0 | 482 | index < JSSettings::kGCSettingsArraySize; |
michael@0 | 483 | index++) { |
michael@0 | 484 | LiteralRebindingCString matchName; |
michael@0 | 485 | |
michael@0 | 486 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "max"); |
michael@0 | 487 | if (memPrefName == matchName || (!rts && index == 0)) { |
michael@0 | 488 | int32_t prefValue = GetWorkerPref(matchName, -1); |
michael@0 | 489 | uint32_t value = (prefValue <= 0 || prefValue >= 0x1000) ? |
michael@0 | 490 | uint32_t(-1) : |
michael@0 | 491 | uint32_t(prefValue) * 1024 * 1024; |
michael@0 | 492 | UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value); |
michael@0 | 493 | continue; |
michael@0 | 494 | } |
michael@0 | 495 | |
michael@0 | 496 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark"); |
michael@0 | 497 | if (memPrefName == matchName || (!rts && index == 1)) { |
michael@0 | 498 | int32_t prefValue = GetWorkerPref(matchName, 128); |
michael@0 | 499 | UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES, |
michael@0 | 500 | uint32_t(prefValue) * 1024 * 1024); |
michael@0 | 501 | continue; |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX |
michael@0 | 505 | "gc_high_frequency_time_limit_ms"); |
michael@0 | 506 | if (memPrefName == matchName || (!rts && index == 2)) { |
michael@0 | 507 | UpdateCommonJSGCMemoryOption(rts, matchName, |
michael@0 | 508 | JSGC_HIGH_FREQUENCY_TIME_LIMIT); |
michael@0 | 509 | continue; |
michael@0 | 510 | } |
michael@0 | 511 | |
michael@0 | 512 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX |
michael@0 | 513 | "gc_low_frequency_heap_growth"); |
michael@0 | 514 | if (memPrefName == matchName || (!rts && index == 3)) { |
michael@0 | 515 | UpdateCommonJSGCMemoryOption(rts, matchName, |
michael@0 | 516 | JSGC_LOW_FREQUENCY_HEAP_GROWTH); |
michael@0 | 517 | continue; |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX |
michael@0 | 521 | "gc_high_frequency_heap_growth_min"); |
michael@0 | 522 | if (memPrefName == matchName || (!rts && index == 4)) { |
michael@0 | 523 | UpdateCommonJSGCMemoryOption(rts, matchName, |
michael@0 | 524 | JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN); |
michael@0 | 525 | continue; |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX |
michael@0 | 529 | "gc_high_frequency_heap_growth_max"); |
michael@0 | 530 | if (memPrefName == matchName || (!rts && index == 5)) { |
michael@0 | 531 | UpdateCommonJSGCMemoryOption(rts, matchName, |
michael@0 | 532 | JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX); |
michael@0 | 533 | continue; |
michael@0 | 534 | } |
michael@0 | 535 | |
michael@0 | 536 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX |
michael@0 | 537 | "gc_high_frequency_low_limit_mb"); |
michael@0 | 538 | if (memPrefName == matchName || (!rts && index == 6)) { |
michael@0 | 539 | UpdateCommonJSGCMemoryOption(rts, matchName, |
michael@0 | 540 | JSGC_HIGH_FREQUENCY_LOW_LIMIT); |
michael@0 | 541 | continue; |
michael@0 | 542 | } |
michael@0 | 543 | |
michael@0 | 544 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX |
michael@0 | 545 | "gc_high_frequency_high_limit_mb"); |
michael@0 | 546 | if (memPrefName == matchName || (!rts && index == 7)) { |
michael@0 | 547 | UpdateCommonJSGCMemoryOption(rts, matchName, |
michael@0 | 548 | JSGC_HIGH_FREQUENCY_HIGH_LIMIT); |
michael@0 | 549 | continue; |
michael@0 | 550 | } |
michael@0 | 551 | |
michael@0 | 552 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX |
michael@0 | 553 | "gc_allocation_threshold_mb"); |
michael@0 | 554 | if (memPrefName == matchName || (!rts && index == 8)) { |
michael@0 | 555 | UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD); |
michael@0 | 556 | continue; |
michael@0 | 557 | } |
michael@0 | 558 | |
michael@0 | 559 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms"); |
michael@0 | 560 | if (memPrefName == matchName || (!rts && index == 9)) { |
michael@0 | 561 | int32_t prefValue = GetWorkerPref(matchName, -1); |
michael@0 | 562 | uint32_t value = |
michael@0 | 563 | (prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue); |
michael@0 | 564 | UpdatOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET, value); |
michael@0 | 565 | continue; |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth"); |
michael@0 | 569 | if (memPrefName == matchName || (!rts && index == 10)) { |
michael@0 | 570 | bool prefValue = GetWorkerPref(matchName, false); |
michael@0 | 571 | UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH, |
michael@0 | 572 | prefValue ? 0 : 1); |
michael@0 | 573 | continue; |
michael@0 | 574 | } |
michael@0 | 575 | |
michael@0 | 576 | matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice"); |
michael@0 | 577 | if (memPrefName == matchName || (!rts && index == 11)) { |
michael@0 | 578 | bool prefValue = GetWorkerPref(matchName, false); |
michael@0 | 579 | UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE, |
michael@0 | 580 | prefValue ? 0 : 1); |
michael@0 | 581 | continue; |
michael@0 | 582 | } |
michael@0 | 583 | |
michael@0 | 584 | #ifdef DEBUG |
michael@0 | 585 | nsAutoCString message("Workers don't support the 'mem."); |
michael@0 | 586 | message.Append(memPrefName); |
michael@0 | 587 | message.AppendLiteral("' preference!"); |
michael@0 | 588 | NS_WARNING(message.get()); |
michael@0 | 589 | #endif |
michael@0 | 590 | } |
michael@0 | 591 | } |
michael@0 | 592 | |
michael@0 | 593 | void |
michael@0 | 594 | ErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aReport) |
michael@0 | 595 | { |
michael@0 | 596 | WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); |
michael@0 | 597 | MOZ_ASSERT(worker); |
michael@0 | 598 | |
michael@0 | 599 | return worker->ReportError(aCx, aMessage, aReport); |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | bool |
michael@0 | 603 | InterruptCallback(JSContext* aCx) |
michael@0 | 604 | { |
michael@0 | 605 | WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); |
michael@0 | 606 | MOZ_ASSERT(worker); |
michael@0 | 607 | |
michael@0 | 608 | // Now is a good time to turn on profiling if it's pending. |
michael@0 | 609 | profiler_js_operation_callback(); |
michael@0 | 610 | |
michael@0 | 611 | return worker->InterruptCallback(aCx); |
michael@0 | 612 | } |
michael@0 | 613 | |
michael@0 | 614 | class LogViolationDetailsRunnable MOZ_FINAL : public nsRunnable |
michael@0 | 615 | { |
michael@0 | 616 | WorkerPrivate* mWorkerPrivate; |
michael@0 | 617 | nsCOMPtr<nsIEventTarget> mSyncLoopTarget; |
michael@0 | 618 | nsString mFileName; |
michael@0 | 619 | uint32_t mLineNum; |
michael@0 | 620 | |
michael@0 | 621 | public: |
michael@0 | 622 | LogViolationDetailsRunnable(WorkerPrivate* aWorker, |
michael@0 | 623 | const nsString& aFileName, |
michael@0 | 624 | uint32_t aLineNum) |
michael@0 | 625 | : mWorkerPrivate(aWorker), mFileName(aFileName), mLineNum(aLineNum) |
michael@0 | 626 | { |
michael@0 | 627 | MOZ_ASSERT(aWorker); |
michael@0 | 628 | } |
michael@0 | 629 | |
michael@0 | 630 | NS_DECL_ISUPPORTS_INHERITED |
michael@0 | 631 | |
michael@0 | 632 | bool |
michael@0 | 633 | Dispatch(JSContext* aCx) |
michael@0 | 634 | { |
michael@0 | 635 | AutoSyncLoopHolder syncLoop(mWorkerPrivate); |
michael@0 | 636 | |
michael@0 | 637 | mSyncLoopTarget = syncLoop.EventTarget(); |
michael@0 | 638 | MOZ_ASSERT(mSyncLoopTarget); |
michael@0 | 639 | |
michael@0 | 640 | if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { |
michael@0 | 641 | JS_ReportError(aCx, "Failed to dispatch to main thread!"); |
michael@0 | 642 | return false; |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | return syncLoop.Run(); |
michael@0 | 646 | } |
michael@0 | 647 | |
michael@0 | 648 | private: |
michael@0 | 649 | NS_DECL_NSIRUNNABLE |
michael@0 | 650 | }; |
michael@0 | 651 | |
michael@0 | 652 | bool |
michael@0 | 653 | ContentSecurityPolicyAllows(JSContext* aCx) |
michael@0 | 654 | { |
michael@0 | 655 | WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); |
michael@0 | 656 | worker->AssertIsOnWorkerThread(); |
michael@0 | 657 | |
michael@0 | 658 | if (worker->GetReportCSPViolations()) { |
michael@0 | 659 | nsString fileName; |
michael@0 | 660 | uint32_t lineNum = 0; |
michael@0 | 661 | |
michael@0 | 662 | JS::AutoFilename file; |
michael@0 | 663 | if (JS::DescribeScriptedCaller(aCx, &file, &lineNum) && file.get()) { |
michael@0 | 664 | fileName = NS_ConvertUTF8toUTF16(file.get()); |
michael@0 | 665 | } else { |
michael@0 | 666 | JS_ReportPendingException(aCx); |
michael@0 | 667 | } |
michael@0 | 668 | |
michael@0 | 669 | nsRefPtr<LogViolationDetailsRunnable> runnable = |
michael@0 | 670 | new LogViolationDetailsRunnable(worker, fileName, lineNum); |
michael@0 | 671 | |
michael@0 | 672 | if (!runnable->Dispatch(aCx)) { |
michael@0 | 673 | JS_ReportPendingException(aCx); |
michael@0 | 674 | } |
michael@0 | 675 | } |
michael@0 | 676 | |
michael@0 | 677 | return worker->IsEvalAllowed(); |
michael@0 | 678 | } |
michael@0 | 679 | |
michael@0 | 680 | void |
michael@0 | 681 | CTypesActivityCallback(JSContext* aCx, |
michael@0 | 682 | js::CTypesActivityType aType) |
michael@0 | 683 | { |
michael@0 | 684 | WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); |
michael@0 | 685 | worker->AssertIsOnWorkerThread(); |
michael@0 | 686 | |
michael@0 | 687 | switch (aType) { |
michael@0 | 688 | case js::CTYPES_CALL_BEGIN: |
michael@0 | 689 | worker->BeginCTypesCall(); |
michael@0 | 690 | break; |
michael@0 | 691 | |
michael@0 | 692 | case js::CTYPES_CALL_END: |
michael@0 | 693 | worker->EndCTypesCall(); |
michael@0 | 694 | break; |
michael@0 | 695 | |
michael@0 | 696 | case js::CTYPES_CALLBACK_BEGIN: |
michael@0 | 697 | worker->BeginCTypesCallback(); |
michael@0 | 698 | break; |
michael@0 | 699 | |
michael@0 | 700 | case js::CTYPES_CALLBACK_END: |
michael@0 | 701 | worker->EndCTypesCallback(); |
michael@0 | 702 | break; |
michael@0 | 703 | |
michael@0 | 704 | default: |
michael@0 | 705 | MOZ_CRASH("Unknown type flag!"); |
michael@0 | 706 | } |
michael@0 | 707 | } |
michael@0 | 708 | |
michael@0 | 709 | static nsIPrincipal* |
michael@0 | 710 | GetPrincipalForAsmJSCacheOp() |
michael@0 | 711 | { |
michael@0 | 712 | WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); |
michael@0 | 713 | if (!workerPrivate) { |
michael@0 | 714 | return nullptr; |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | // asmjscache::OpenEntryForX guarnatee to only access the given nsIPrincipal |
michael@0 | 718 | // from the main thread. |
michael@0 | 719 | return workerPrivate->GetPrincipalDontAssertMainThread(); |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | static bool |
michael@0 | 723 | AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal, |
michael@0 | 724 | const jschar* aBegin, |
michael@0 | 725 | const jschar* aLimit, |
michael@0 | 726 | size_t* aSize, |
michael@0 | 727 | const uint8_t** aMemory, |
michael@0 | 728 | intptr_t *aHandle) |
michael@0 | 729 | { |
michael@0 | 730 | nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp(); |
michael@0 | 731 | if (!principal) { |
michael@0 | 732 | return false; |
michael@0 | 733 | } |
michael@0 | 734 | |
michael@0 | 735 | return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory, |
michael@0 | 736 | aHandle); |
michael@0 | 737 | } |
michael@0 | 738 | |
michael@0 | 739 | static bool |
michael@0 | 740 | AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal, |
michael@0 | 741 | bool aInstalled, |
michael@0 | 742 | const jschar* aBegin, |
michael@0 | 743 | const jschar* aEnd, |
michael@0 | 744 | size_t aSize, |
michael@0 | 745 | uint8_t** aMemory, |
michael@0 | 746 | intptr_t* aHandle) |
michael@0 | 747 | { |
michael@0 | 748 | nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp(); |
michael@0 | 749 | if (!principal) { |
michael@0 | 750 | return false; |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | return asmjscache::OpenEntryForWrite(principal, aInstalled, aBegin, aEnd, |
michael@0 | 754 | aSize, aMemory, aHandle); |
michael@0 | 755 | } |
michael@0 | 756 | |
michael@0 | 757 | struct WorkerThreadRuntimePrivate : public PerThreadAtomCache |
michael@0 | 758 | { |
michael@0 | 759 | WorkerPrivate* mWorkerPrivate; |
michael@0 | 760 | }; |
michael@0 | 761 | |
michael@0 | 762 | JSContext* |
michael@0 | 763 | CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSRuntime* aRuntime) |
michael@0 | 764 | { |
michael@0 | 765 | aWorkerPrivate->AssertIsOnWorkerThread(); |
michael@0 | 766 | NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!"); |
michael@0 | 767 | |
michael@0 | 768 | JSSettings settings; |
michael@0 | 769 | aWorkerPrivate->CopyJSSettings(settings); |
michael@0 | 770 | |
michael@0 | 771 | JS::RuntimeOptionsRef(aRuntime) = settings.runtimeOptions; |
michael@0 | 772 | |
michael@0 | 773 | JSSettings::JSGCSettingsArray& gcSettings = settings.gcSettings; |
michael@0 | 774 | |
michael@0 | 775 | // This is the real place where we set the max memory for the runtime. |
michael@0 | 776 | for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) { |
michael@0 | 777 | const JSSettings::JSGCSetting& setting = gcSettings[index]; |
michael@0 | 778 | if (setting.IsSet()) { |
michael@0 | 779 | NS_ASSERTION(setting.value, "Can't handle 0 values!"); |
michael@0 | 780 | JS_SetGCParameter(aRuntime, setting.key, setting.value); |
michael@0 | 781 | } |
michael@0 | 782 | } |
michael@0 | 783 | |
michael@0 | 784 | JS_SetIsWorkerRuntime(aRuntime); |
michael@0 | 785 | |
michael@0 | 786 | JS_SetNativeStackQuota(aRuntime, WORKER_CONTEXT_NATIVE_STACK_LIMIT); |
michael@0 | 787 | |
michael@0 | 788 | // Security policy: |
michael@0 | 789 | static JSSecurityCallbacks securityCallbacks = { |
michael@0 | 790 | ContentSecurityPolicyAllows |
michael@0 | 791 | }; |
michael@0 | 792 | JS_SetSecurityCallbacks(aRuntime, &securityCallbacks); |
michael@0 | 793 | |
michael@0 | 794 | // DOM helpers: |
michael@0 | 795 | static js::DOMCallbacks DOMCallbacks = { |
michael@0 | 796 | InstanceClassHasProtoAtDepth |
michael@0 | 797 | }; |
michael@0 | 798 | SetDOMCallbacks(aRuntime, &DOMCallbacks); |
michael@0 | 799 | |
michael@0 | 800 | // Set up the asm.js cache callbacks |
michael@0 | 801 | static JS::AsmJSCacheOps asmJSCacheOps = { |
michael@0 | 802 | AsmJSCacheOpenEntryForRead, |
michael@0 | 803 | asmjscache::CloseEntryForRead, |
michael@0 | 804 | AsmJSCacheOpenEntryForWrite, |
michael@0 | 805 | asmjscache::CloseEntryForWrite, |
michael@0 | 806 | asmjscache::GetBuildId |
michael@0 | 807 | }; |
michael@0 | 808 | JS::SetAsmJSCacheOps(aRuntime, &asmJSCacheOps); |
michael@0 | 809 | |
michael@0 | 810 | JSContext* workerCx = JS_NewContext(aRuntime, 0); |
michael@0 | 811 | if (!workerCx) { |
michael@0 | 812 | NS_WARNING("Could not create new context!"); |
michael@0 | 813 | return nullptr; |
michael@0 | 814 | } |
michael@0 | 815 | |
michael@0 | 816 | auto rtPrivate = new WorkerThreadRuntimePrivate(); |
michael@0 | 817 | memset(rtPrivate, 0, sizeof(WorkerThreadRuntimePrivate)); |
michael@0 | 818 | rtPrivate->mWorkerPrivate = aWorkerPrivate; |
michael@0 | 819 | JS_SetRuntimePrivate(aRuntime, rtPrivate); |
michael@0 | 820 | |
michael@0 | 821 | JS_SetErrorReporter(workerCx, ErrorReporter); |
michael@0 | 822 | |
michael@0 | 823 | JS_SetInterruptCallback(aRuntime, InterruptCallback); |
michael@0 | 824 | |
michael@0 | 825 | js::SetCTypesActivityCallback(aRuntime, CTypesActivityCallback); |
michael@0 | 826 | |
michael@0 | 827 | JS::ContextOptionsRef(workerCx) = |
michael@0 | 828 | aWorkerPrivate->IsChromeWorker() ? settings.chrome.contextOptions |
michael@0 | 829 | : settings.content.contextOptions; |
michael@0 | 830 | |
michael@0 | 831 | #ifdef JS_GC_ZEAL |
michael@0 | 832 | JS_SetGCZeal(workerCx, settings.gcZeal, settings.gcZealFrequency); |
michael@0 | 833 | #endif |
michael@0 | 834 | |
michael@0 | 835 | return workerCx; |
michael@0 | 836 | } |
michael@0 | 837 | |
michael@0 | 838 | class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime |
michael@0 | 839 | { |
michael@0 | 840 | public: |
michael@0 | 841 | // The heap size passed here doesn't matter, we will change it later in the |
michael@0 | 842 | // call to JS_SetGCParameter inside CreateJSContextForWorker. |
michael@0 | 843 | WorkerJSRuntime(JSRuntime* aParentRuntime, WorkerPrivate* aWorkerPrivate) |
michael@0 | 844 | : CycleCollectedJSRuntime(aParentRuntime, |
michael@0 | 845 | WORKER_DEFAULT_RUNTIME_HEAPSIZE, |
michael@0 | 846 | JS_NO_HELPER_THREADS), |
michael@0 | 847 | mWorkerPrivate(aWorkerPrivate) |
michael@0 | 848 | { |
michael@0 | 849 | } |
michael@0 | 850 | |
michael@0 | 851 | ~WorkerJSRuntime() |
michael@0 | 852 | { |
michael@0 | 853 | auto rtPrivate = static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(Runtime())); |
michael@0 | 854 | delete rtPrivate; |
michael@0 | 855 | JS_SetRuntimePrivate(Runtime(), nullptr); |
michael@0 | 856 | |
michael@0 | 857 | // The worker global should be unrooted and the shutdown cycle collection |
michael@0 | 858 | // should break all remaining cycles. The superclass destructor will run |
michael@0 | 859 | // the GC one final time and finalize any JSObjects that were participating |
michael@0 | 860 | // in cycles that were broken during CC shutdown. |
michael@0 | 861 | nsCycleCollector_shutdown(); |
michael@0 | 862 | |
michael@0 | 863 | // The CC is shut down, and the superclass destructor will GC, so make sure |
michael@0 | 864 | // we don't try to CC again. |
michael@0 | 865 | mWorkerPrivate = nullptr; |
michael@0 | 866 | } |
michael@0 | 867 | |
michael@0 | 868 | virtual void |
michael@0 | 869 | PrepareForForgetSkippable() MOZ_OVERRIDE |
michael@0 | 870 | { |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | virtual void |
michael@0 | 874 | BeginCycleCollectionCallback() MOZ_OVERRIDE |
michael@0 | 875 | { |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | virtual void |
michael@0 | 879 | EndCycleCollectionCallback(CycleCollectorResults &aResults) MOZ_OVERRIDE |
michael@0 | 880 | { |
michael@0 | 881 | } |
michael@0 | 882 | |
michael@0 | 883 | void |
michael@0 | 884 | DispatchDeferredDeletion(bool aContinuation) MOZ_OVERRIDE |
michael@0 | 885 | { |
michael@0 | 886 | MOZ_ASSERT(!aContinuation); |
michael@0 | 887 | |
michael@0 | 888 | // Do it immediately, no need for asynchronous behavior here. |
michael@0 | 889 | nsCycleCollector_doDeferredDeletion(); |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | virtual void CustomGCCallback(JSGCStatus aStatus) MOZ_OVERRIDE |
michael@0 | 893 | { |
michael@0 | 894 | if (!mWorkerPrivate) { |
michael@0 | 895 | // We're shutting down, no need to do anything. |
michael@0 | 896 | return; |
michael@0 | 897 | } |
michael@0 | 898 | |
michael@0 | 899 | mWorkerPrivate->AssertIsOnWorkerThread(); |
michael@0 | 900 | |
michael@0 | 901 | if (aStatus == JSGC_END) { |
michael@0 | 902 | nsCycleCollector_collect(nullptr); |
michael@0 | 903 | } |
michael@0 | 904 | } |
michael@0 | 905 | |
michael@0 | 906 | private: |
michael@0 | 907 | WorkerPrivate* mWorkerPrivate; |
michael@0 | 908 | }; |
michael@0 | 909 | |
michael@0 | 910 | class WorkerThreadPrimaryRunnable MOZ_FINAL : public nsRunnable |
michael@0 | 911 | { |
michael@0 | 912 | WorkerPrivate* mWorkerPrivate; |
michael@0 | 913 | nsRefPtr<RuntimeService::WorkerThread> mThread; |
michael@0 | 914 | JSRuntime* mParentRuntime; |
michael@0 | 915 | |
michael@0 | 916 | class FinishedRunnable MOZ_FINAL : public nsRunnable |
michael@0 | 917 | { |
michael@0 | 918 | nsRefPtr<RuntimeService::WorkerThread> mThread; |
michael@0 | 919 | |
michael@0 | 920 | public: |
michael@0 | 921 | FinishedRunnable(already_AddRefed<RuntimeService::WorkerThread> aThread) |
michael@0 | 922 | : mThread(aThread) |
michael@0 | 923 | { |
michael@0 | 924 | MOZ_ASSERT(mThread); |
michael@0 | 925 | } |
michael@0 | 926 | |
michael@0 | 927 | NS_DECL_ISUPPORTS_INHERITED |
michael@0 | 928 | |
michael@0 | 929 | private: |
michael@0 | 930 | ~FinishedRunnable() |
michael@0 | 931 | { } |
michael@0 | 932 | |
michael@0 | 933 | NS_DECL_NSIRUNNABLE |
michael@0 | 934 | }; |
michael@0 | 935 | |
michael@0 | 936 | public: |
michael@0 | 937 | WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate, |
michael@0 | 938 | RuntimeService::WorkerThread* aThread, |
michael@0 | 939 | JSRuntime* aParentRuntime) |
michael@0 | 940 | : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentRuntime(aParentRuntime) |
michael@0 | 941 | { |
michael@0 | 942 | MOZ_ASSERT(aWorkerPrivate); |
michael@0 | 943 | MOZ_ASSERT(aThread); |
michael@0 | 944 | } |
michael@0 | 945 | |
michael@0 | 946 | NS_DECL_ISUPPORTS_INHERITED |
michael@0 | 947 | |
michael@0 | 948 | private: |
michael@0 | 949 | ~WorkerThreadPrimaryRunnable() |
michael@0 | 950 | { } |
michael@0 | 951 | |
michael@0 | 952 | NS_DECL_NSIRUNNABLE |
michael@0 | 953 | }; |
michael@0 | 954 | |
michael@0 | 955 | class WorkerTaskRunnable MOZ_FINAL : public WorkerRunnable |
michael@0 | 956 | { |
michael@0 | 957 | nsRefPtr<WorkerTask> mTask; |
michael@0 | 958 | |
michael@0 | 959 | public: |
michael@0 | 960 | WorkerTaskRunnable(WorkerPrivate* aWorkerPrivate, WorkerTask* aTask) |
michael@0 | 961 | : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), mTask(aTask) |
michael@0 | 962 | { |
michael@0 | 963 | MOZ_ASSERT(aTask); |
michael@0 | 964 | } |
michael@0 | 965 | |
michael@0 | 966 | private: |
michael@0 | 967 | virtual bool |
michael@0 | 968 | PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
michael@0 | 969 | { |
michael@0 | 970 | // May be called on any thread! |
michael@0 | 971 | return true; |
michael@0 | 972 | } |
michael@0 | 973 | |
michael@0 | 974 | virtual void |
michael@0 | 975 | PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, |
michael@0 | 976 | bool aDispatchResult) MOZ_OVERRIDE |
michael@0 | 977 | { |
michael@0 | 978 | // May be called on any thread! |
michael@0 | 979 | } |
michael@0 | 980 | |
michael@0 | 981 | virtual bool |
michael@0 | 982 | WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE |
michael@0 | 983 | { |
michael@0 | 984 | return mTask->RunTask(aCx); |
michael@0 | 985 | } |
michael@0 | 986 | }; |
michael@0 | 987 | |
michael@0 | 988 | void |
michael@0 | 989 | AppNameOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) |
michael@0 | 990 | { |
michael@0 | 991 | AssertIsOnMainThread(); |
michael@0 | 992 | |
michael@0 | 993 | const nsAdoptingString& override = |
michael@0 | 994 | mozilla::Preferences::GetString(PREF_GENERAL_APPNAME_OVERRIDE); |
michael@0 | 995 | |
michael@0 | 996 | RuntimeService* runtime = RuntimeService::GetService(); |
michael@0 | 997 | if (runtime) { |
michael@0 | 998 | runtime->UpdateAppNameOverridePreference(override); |
michael@0 | 999 | } |
michael@0 | 1000 | } |
michael@0 | 1001 | |
michael@0 | 1002 | void |
michael@0 | 1003 | AppVersionOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) |
michael@0 | 1004 | { |
michael@0 | 1005 | AssertIsOnMainThread(); |
michael@0 | 1006 | |
michael@0 | 1007 | const nsAdoptingString& override = |
michael@0 | 1008 | mozilla::Preferences::GetString(PREF_GENERAL_APPVERSION_OVERRIDE); |
michael@0 | 1009 | |
michael@0 | 1010 | RuntimeService* runtime = RuntimeService::GetService(); |
michael@0 | 1011 | if (runtime) { |
michael@0 | 1012 | runtime->UpdateAppVersionOverridePreference(override); |
michael@0 | 1013 | } |
michael@0 | 1014 | } |
michael@0 | 1015 | |
michael@0 | 1016 | void |
michael@0 | 1017 | PlatformOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) |
michael@0 | 1018 | { |
michael@0 | 1019 | AssertIsOnMainThread(); |
michael@0 | 1020 | |
michael@0 | 1021 | const nsAdoptingString& override = |
michael@0 | 1022 | mozilla::Preferences::GetString(PREF_GENERAL_PLATFORM_OVERRIDE); |
michael@0 | 1023 | |
michael@0 | 1024 | RuntimeService* runtime = RuntimeService::GetService(); |
michael@0 | 1025 | if (runtime) { |
michael@0 | 1026 | runtime->UpdatePlatformOverridePreference(override); |
michael@0 | 1027 | } |
michael@0 | 1028 | } |
michael@0 | 1029 | |
michael@0 | 1030 | } /* anonymous namespace */ |
michael@0 | 1031 | |
michael@0 | 1032 | class RuntimeService::WorkerThread MOZ_FINAL : public nsThread |
michael@0 | 1033 | { |
michael@0 | 1034 | class Observer MOZ_FINAL : public nsIThreadObserver |
michael@0 | 1035 | { |
michael@0 | 1036 | WorkerPrivate* mWorkerPrivate; |
michael@0 | 1037 | |
michael@0 | 1038 | public: |
michael@0 | 1039 | Observer(WorkerPrivate* aWorkerPrivate) |
michael@0 | 1040 | : mWorkerPrivate(aWorkerPrivate) |
michael@0 | 1041 | { |
michael@0 | 1042 | MOZ_ASSERT(aWorkerPrivate); |
michael@0 | 1043 | aWorkerPrivate->AssertIsOnWorkerThread(); |
michael@0 | 1044 | } |
michael@0 | 1045 | |
michael@0 | 1046 | NS_DECL_THREADSAFE_ISUPPORTS |
michael@0 | 1047 | |
michael@0 | 1048 | private: |
michael@0 | 1049 | ~Observer() |
michael@0 | 1050 | { |
michael@0 | 1051 | mWorkerPrivate->AssertIsOnWorkerThread(); |
michael@0 | 1052 | } |
michael@0 | 1053 | |
michael@0 | 1054 | NS_DECL_NSITHREADOBSERVER |
michael@0 | 1055 | }; |
michael@0 | 1056 | |
michael@0 | 1057 | WorkerPrivate* mWorkerPrivate; |
michael@0 | 1058 | nsRefPtr<Observer> mObserver; |
michael@0 | 1059 | |
michael@0 | 1060 | #ifdef DEBUG |
michael@0 | 1061 | // Protected by nsThread::mLock. |
michael@0 | 1062 | bool mAcceptingNonWorkerRunnables; |
michael@0 | 1063 | #endif |
michael@0 | 1064 | |
michael@0 | 1065 | public: |
michael@0 | 1066 | static already_AddRefed<WorkerThread> |
michael@0 | 1067 | Create(); |
michael@0 | 1068 | |
michael@0 | 1069 | void |
michael@0 | 1070 | SetWorker(WorkerPrivate* aWorkerPrivate); |
michael@0 | 1071 | |
michael@0 | 1072 | NS_DECL_ISUPPORTS_INHERITED |
michael@0 | 1073 | |
michael@0 | 1074 | NS_IMETHOD |
michael@0 | 1075 | Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE; |
michael@0 | 1076 | |
michael@0 | 1077 | #ifdef DEBUG |
michael@0 | 1078 | bool |
michael@0 | 1079 | IsAcceptingNonWorkerRunnables() |
michael@0 | 1080 | { |
michael@0 | 1081 | MutexAutoLock lock(mLock); |
michael@0 | 1082 | return mAcceptingNonWorkerRunnables; |
michael@0 | 1083 | } |
michael@0 | 1084 | |
michael@0 | 1085 | void |
michael@0 | 1086 | SetAcceptingNonWorkerRunnables(bool aAcceptingNonWorkerRunnables) |
michael@0 | 1087 | { |
michael@0 | 1088 | MutexAutoLock lock(mLock); |
michael@0 | 1089 | mAcceptingNonWorkerRunnables = aAcceptingNonWorkerRunnables; |
michael@0 | 1090 | } |
michael@0 | 1091 | #endif |
michael@0 | 1092 | |
michael@0 | 1093 | private: |
michael@0 | 1094 | WorkerThread() |
michael@0 | 1095 | : nsThread(nsThread::NOT_MAIN_THREAD, WORKER_STACK_SIZE), |
michael@0 | 1096 | mWorkerPrivate(nullptr) |
michael@0 | 1097 | #ifdef DEBUG |
michael@0 | 1098 | , mAcceptingNonWorkerRunnables(true) |
michael@0 | 1099 | #endif |
michael@0 | 1100 | { } |
michael@0 | 1101 | |
michael@0 | 1102 | ~WorkerThread() |
michael@0 | 1103 | { } |
michael@0 | 1104 | }; |
michael@0 | 1105 | |
michael@0 | 1106 | BEGIN_WORKERS_NAMESPACE |
michael@0 | 1107 | |
michael@0 | 1108 | // Entry point for main thread non-window globals. |
michael@0 | 1109 | bool |
michael@0 | 1110 | ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId, |
michael@0 | 1111 | JS::MutableHandle<JSObject*> aObjp) |
michael@0 | 1112 | { |
michael@0 | 1113 | AssertIsOnMainThread(); |
michael@0 | 1114 | MOZ_ASSERT(nsContentUtils::IsCallerChrome()); |
michael@0 | 1115 | |
michael@0 | 1116 | // Make sure our strings are interned. |
michael@0 | 1117 | if (JSID_IS_VOID(gStringIDs[0])) { |
michael@0 | 1118 | for (uint32_t i = 0; i < ID_COUNT; i++) { |
michael@0 | 1119 | JSString* str = JS_InternString(aCx, gStringChars[i]); |
michael@0 | 1120 | if (!str) { |
michael@0 | 1121 | while (i) { |
michael@0 | 1122 | gStringIDs[--i] = JSID_VOID; |
michael@0 | 1123 | } |
michael@0 | 1124 | return false; |
michael@0 | 1125 | } |
michael@0 | 1126 | gStringIDs[i] = INTERNED_STRING_TO_JSID(aCx, str); |
michael@0 | 1127 | } |
michael@0 | 1128 | } |
michael@0 | 1129 | |
michael@0 | 1130 | bool shouldResolve = false; |
michael@0 | 1131 | |
michael@0 | 1132 | for (uint32_t i = 0; i < ID_COUNT; i++) { |
michael@0 | 1133 | if (gStringIDs[i] == aId) { |
michael@0 | 1134 | shouldResolve = true; |
michael@0 | 1135 | break; |
michael@0 | 1136 | } |
michael@0 | 1137 | } |
michael@0 | 1138 | |
michael@0 | 1139 | if (!shouldResolve) { |
michael@0 | 1140 | aObjp.set(nullptr); |
michael@0 | 1141 | return true; |
michael@0 | 1142 | } |
michael@0 | 1143 | |
michael@0 | 1144 | if (!WorkerBinding::GetConstructorObject(aCx, aObj) || |
michael@0 | 1145 | !ChromeWorkerBinding::GetConstructorObject(aCx, aObj) || |
michael@0 | 1146 | !ErrorEventBinding::GetConstructorObject(aCx, aObj) || |
michael@0 | 1147 | !MessageEventBinding::GetConstructorObject(aCx, aObj)) { |
michael@0 | 1148 | return false; |
michael@0 | 1149 | } |
michael@0 | 1150 | |
michael@0 | 1151 | aObjp.set(aObj); |
michael@0 | 1152 | return true; |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | void |
michael@0 | 1156 | CancelWorkersForWindow(nsPIDOMWindow* aWindow) |
michael@0 | 1157 | { |
michael@0 | 1158 | AssertIsOnMainThread(); |
michael@0 | 1159 | RuntimeService* runtime = RuntimeService::GetService(); |
michael@0 | 1160 | if (runtime) { |
michael@0 | 1161 | runtime->CancelWorkersForWindow(aWindow); |
michael@0 | 1162 | } |
michael@0 | 1163 | } |
michael@0 | 1164 | |
michael@0 | 1165 | void |
michael@0 | 1166 | SuspendWorkersForWindow(nsPIDOMWindow* aWindow) |
michael@0 | 1167 | { |
michael@0 | 1168 | AssertIsOnMainThread(); |
michael@0 | 1169 | RuntimeService* runtime = RuntimeService::GetService(); |
michael@0 | 1170 | if (runtime) { |
michael@0 | 1171 | runtime->SuspendWorkersForWindow(aWindow); |
michael@0 | 1172 | } |
michael@0 | 1173 | } |
michael@0 | 1174 | |
michael@0 | 1175 | void |
michael@0 | 1176 | ResumeWorkersForWindow(nsPIDOMWindow* aWindow) |
michael@0 | 1177 | { |
michael@0 | 1178 | AssertIsOnMainThread(); |
michael@0 | 1179 | RuntimeService* runtime = RuntimeService::GetService(); |
michael@0 | 1180 | if (runtime) { |
michael@0 | 1181 | runtime->ResumeWorkersForWindow(aWindow); |
michael@0 | 1182 | } |
michael@0 | 1183 | } |
michael@0 | 1184 | |
michael@0 | 1185 | WorkerCrossThreadDispatcher::WorkerCrossThreadDispatcher( |
michael@0 | 1186 | WorkerPrivate* aWorkerPrivate) |
michael@0 | 1187 | : mMutex("WorkerCrossThreadDispatcher::mMutex"), |
michael@0 | 1188 | mWorkerPrivate(aWorkerPrivate) |
michael@0 | 1189 | { |
michael@0 | 1190 | MOZ_ASSERT(aWorkerPrivate); |
michael@0 | 1191 | } |
michael@0 | 1192 | |
michael@0 | 1193 | bool |
michael@0 | 1194 | WorkerCrossThreadDispatcher::PostTask(WorkerTask* aTask) |
michael@0 | 1195 | { |
michael@0 | 1196 | MOZ_ASSERT(aTask); |
michael@0 | 1197 | |
michael@0 | 1198 | MutexAutoLock lock(mMutex); |
michael@0 | 1199 | |
michael@0 | 1200 | if (!mWorkerPrivate) { |
michael@0 | 1201 | NS_WARNING("Posted a task to a WorkerCrossThreadDispatcher that is no " |
michael@0 | 1202 | "longer accepting tasks!"); |
michael@0 | 1203 | return false; |
michael@0 | 1204 | } |
michael@0 | 1205 | |
michael@0 | 1206 | nsRefPtr<WorkerTaskRunnable> runnable = |
michael@0 | 1207 | new WorkerTaskRunnable(mWorkerPrivate, aTask); |
michael@0 | 1208 | return runnable->Dispatch(nullptr); |
michael@0 | 1209 | } |
michael@0 | 1210 | |
michael@0 | 1211 | WorkerPrivate* |
michael@0 | 1212 | GetWorkerPrivateFromContext(JSContext* aCx) |
michael@0 | 1213 | { |
michael@0 | 1214 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 1215 | MOZ_ASSERT(aCx); |
michael@0 | 1216 | |
michael@0 | 1217 | JSRuntime* rt = JS_GetRuntime(aCx); |
michael@0 | 1218 | MOZ_ASSERT(rt); |
michael@0 | 1219 | |
michael@0 | 1220 | void* rtPrivate = JS_GetRuntimePrivate(rt); |
michael@0 | 1221 | MOZ_ASSERT(rtPrivate); |
michael@0 | 1222 | |
michael@0 | 1223 | return static_cast<WorkerThreadRuntimePrivate*>(rtPrivate)->mWorkerPrivate; |
michael@0 | 1224 | } |
michael@0 | 1225 | |
michael@0 | 1226 | WorkerPrivate* |
michael@0 | 1227 | GetCurrentThreadWorkerPrivate() |
michael@0 | 1228 | { |
michael@0 | 1229 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 1230 | |
michael@0 | 1231 | CycleCollectedJSRuntime* ccrt = CycleCollectedJSRuntime::Get(); |
michael@0 | 1232 | if (!ccrt) { |
michael@0 | 1233 | return nullptr; |
michael@0 | 1234 | } |
michael@0 | 1235 | |
michael@0 | 1236 | JSRuntime* rt = ccrt->Runtime(); |
michael@0 | 1237 | MOZ_ASSERT(rt); |
michael@0 | 1238 | |
michael@0 | 1239 | void* rtPrivate = JS_GetRuntimePrivate(rt); |
michael@0 | 1240 | MOZ_ASSERT(rtPrivate); |
michael@0 | 1241 | |
michael@0 | 1242 | return static_cast<WorkerThreadRuntimePrivate*>(rtPrivate)->mWorkerPrivate; |
michael@0 | 1243 | } |
michael@0 | 1244 | |
michael@0 | 1245 | bool |
michael@0 | 1246 | IsCurrentThreadRunningChromeWorker() |
michael@0 | 1247 | { |
michael@0 | 1248 | return GetCurrentThreadWorkerPrivate()->UsesSystemPrincipal(); |
michael@0 | 1249 | } |
michael@0 | 1250 | |
michael@0 | 1251 | JSContext* |
michael@0 | 1252 | GetCurrentThreadJSContext() |
michael@0 | 1253 | { |
michael@0 | 1254 | return GetCurrentThreadWorkerPrivate()->GetJSContext(); |
michael@0 | 1255 | } |
michael@0 | 1256 | |
michael@0 | 1257 | END_WORKERS_NAMESPACE |
michael@0 | 1258 | |
michael@0 | 1259 | // This is only touched on the main thread. Initialized in Init() below. |
michael@0 | 1260 | JSSettings RuntimeService::sDefaultJSSettings; |
michael@0 | 1261 | bool RuntimeService::sDefaultPreferences[WORKERPREF_COUNT] = { false }; |
michael@0 | 1262 | |
michael@0 | 1263 | RuntimeService::RuntimeService() |
michael@0 | 1264 | : mMutex("RuntimeService::mMutex"), mObserved(false), |
michael@0 | 1265 | mShuttingDown(false), mNavigatorPropertiesLoaded(false) |
michael@0 | 1266 | { |
michael@0 | 1267 | AssertIsOnMainThread(); |
michael@0 | 1268 | NS_ASSERTION(!gRuntimeService, "More than one service!"); |
michael@0 | 1269 | } |
michael@0 | 1270 | |
michael@0 | 1271 | RuntimeService::~RuntimeService() |
michael@0 | 1272 | { |
michael@0 | 1273 | AssertIsOnMainThread(); |
michael@0 | 1274 | |
michael@0 | 1275 | // gRuntimeService can be null if Init() fails. |
michael@0 | 1276 | NS_ASSERTION(!gRuntimeService || gRuntimeService == this, |
michael@0 | 1277 | "More than one service!"); |
michael@0 | 1278 | |
michael@0 | 1279 | gRuntimeService = nullptr; |
michael@0 | 1280 | } |
michael@0 | 1281 | |
michael@0 | 1282 | // static |
michael@0 | 1283 | RuntimeService* |
michael@0 | 1284 | RuntimeService::GetOrCreateService() |
michael@0 | 1285 | { |
michael@0 | 1286 | AssertIsOnMainThread(); |
michael@0 | 1287 | |
michael@0 | 1288 | if (!gRuntimeService) { |
michael@0 | 1289 | nsRefPtr<RuntimeService> service = new RuntimeService(); |
michael@0 | 1290 | if (NS_FAILED(service->Init())) { |
michael@0 | 1291 | NS_WARNING("Failed to initialize!"); |
michael@0 | 1292 | service->Cleanup(); |
michael@0 | 1293 | return nullptr; |
michael@0 | 1294 | } |
michael@0 | 1295 | |
michael@0 | 1296 | // The observer service now owns us until shutdown. |
michael@0 | 1297 | gRuntimeService = service; |
michael@0 | 1298 | } |
michael@0 | 1299 | |
michael@0 | 1300 | return gRuntimeService; |
michael@0 | 1301 | } |
michael@0 | 1302 | |
michael@0 | 1303 | // static |
michael@0 | 1304 | RuntimeService* |
michael@0 | 1305 | RuntimeService::GetService() |
michael@0 | 1306 | { |
michael@0 | 1307 | return gRuntimeService; |
michael@0 | 1308 | } |
michael@0 | 1309 | |
michael@0 | 1310 | bool |
michael@0 | 1311 | RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) |
michael@0 | 1312 | { |
michael@0 | 1313 | aWorkerPrivate->AssertIsOnParentThread(); |
michael@0 | 1314 | |
michael@0 | 1315 | WorkerPrivate* parent = aWorkerPrivate->GetParent(); |
michael@0 | 1316 | if (!parent) { |
michael@0 | 1317 | AssertIsOnMainThread(); |
michael@0 | 1318 | |
michael@0 | 1319 | if (mShuttingDown) { |
michael@0 | 1320 | JS_ReportError(aCx, "Cannot create worker during shutdown!"); |
michael@0 | 1321 | return false; |
michael@0 | 1322 | } |
michael@0 | 1323 | } |
michael@0 | 1324 | |
michael@0 | 1325 | bool isSharedWorker = aWorkerPrivate->IsSharedWorker(); |
michael@0 | 1326 | |
michael@0 | 1327 | const nsCString& sharedWorkerName = aWorkerPrivate->SharedWorkerName(); |
michael@0 | 1328 | nsCString sharedWorkerScriptSpec; |
michael@0 | 1329 | |
michael@0 | 1330 | if (isSharedWorker) { |
michael@0 | 1331 | AssertIsOnMainThread(); |
michael@0 | 1332 | |
michael@0 | 1333 | nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI(); |
michael@0 | 1334 | NS_ASSERTION(scriptURI, "Null script URI!"); |
michael@0 | 1335 | |
michael@0 | 1336 | nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec); |
michael@0 | 1337 | if (NS_FAILED(rv)) { |
michael@0 | 1338 | NS_WARNING("GetSpec failed?!"); |
michael@0 | 1339 | xpc::Throw(aCx, rv); |
michael@0 | 1340 | return false; |
michael@0 | 1341 | } |
michael@0 | 1342 | |
michael@0 | 1343 | NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!"); |
michael@0 | 1344 | } |
michael@0 | 1345 | |
michael@0 | 1346 | const nsCString& domain = aWorkerPrivate->Domain(); |
michael@0 | 1347 | |
michael@0 | 1348 | WorkerDomainInfo* domainInfo; |
michael@0 | 1349 | bool queued = false; |
michael@0 | 1350 | { |
michael@0 | 1351 | MutexAutoLock lock(mMutex); |
michael@0 | 1352 | |
michael@0 | 1353 | if (!mDomainMap.Get(domain, &domainInfo)) { |
michael@0 | 1354 | NS_ASSERTION(!parent, "Shouldn't have a parent here!"); |
michael@0 | 1355 | |
michael@0 | 1356 | domainInfo = new WorkerDomainInfo(); |
michael@0 | 1357 | domainInfo->mDomain = domain; |
michael@0 | 1358 | mDomainMap.Put(domain, domainInfo); |
michael@0 | 1359 | } |
michael@0 | 1360 | |
michael@0 | 1361 | queued = gMaxWorkersPerDomain && |
michael@0 | 1362 | domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain && |
michael@0 | 1363 | !domain.IsEmpty(); |
michael@0 | 1364 | |
michael@0 | 1365 | if (queued) { |
michael@0 | 1366 | domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate); |
michael@0 | 1367 | } |
michael@0 | 1368 | else if (parent) { |
michael@0 | 1369 | domainInfo->mChildWorkerCount++; |
michael@0 | 1370 | } |
michael@0 | 1371 | else { |
michael@0 | 1372 | domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate); |
michael@0 | 1373 | } |
michael@0 | 1374 | |
michael@0 | 1375 | if (isSharedWorker) { |
michael@0 | 1376 | nsAutoCString key; |
michael@0 | 1377 | GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName, key); |
michael@0 | 1378 | MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key)); |
michael@0 | 1379 | |
michael@0 | 1380 | SharedWorkerInfo* sharedWorkerInfo = |
michael@0 | 1381 | new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec, |
michael@0 | 1382 | sharedWorkerName); |
michael@0 | 1383 | domainInfo->mSharedWorkerInfos.Put(key, sharedWorkerInfo); |
michael@0 | 1384 | } |
michael@0 | 1385 | } |
michael@0 | 1386 | |
michael@0 | 1387 | // From here on out we must call UnregisterWorker if something fails! |
michael@0 | 1388 | if (parent) { |
michael@0 | 1389 | if (!parent->AddChildWorker(aCx, aWorkerPrivate)) { |
michael@0 | 1390 | UnregisterWorker(aCx, aWorkerPrivate); |
michael@0 | 1391 | return false; |
michael@0 | 1392 | } |
michael@0 | 1393 | } |
michael@0 | 1394 | else { |
michael@0 | 1395 | if (!mNavigatorPropertiesLoaded) { |
michael@0 | 1396 | Navigator::AppName(mNavigatorProperties.mAppName, |
michael@0 | 1397 | false /* aUsePrefOverriddenValue */); |
michael@0 | 1398 | if (NS_FAILED(Navigator::GetAppVersion(mNavigatorProperties.mAppVersion, |
michael@0 | 1399 | false /* aUsePrefOverriddenValue */)) || |
michael@0 | 1400 | NS_FAILED(Navigator::GetPlatform(mNavigatorProperties.mPlatform, |
michael@0 | 1401 | false /* aUsePrefOverriddenValue */)) || |
michael@0 | 1402 | NS_FAILED(NS_GetNavigatorUserAgent(mNavigatorProperties.mUserAgent))) { |
michael@0 | 1403 | JS_ReportError(aCx, "Failed to load navigator strings!"); |
michael@0 | 1404 | UnregisterWorker(aCx, aWorkerPrivate); |
michael@0 | 1405 | return false; |
michael@0 | 1406 | } |
michael@0 | 1407 | |
michael@0 | 1408 | mNavigatorProperties.mAppNameOverridden = |
michael@0 | 1409 | mozilla::Preferences::GetString(PREF_GENERAL_APPNAME_OVERRIDE); |
michael@0 | 1410 | mNavigatorProperties.mAppVersionOverridden = |
michael@0 | 1411 | mozilla::Preferences::GetString(PREF_GENERAL_APPVERSION_OVERRIDE); |
michael@0 | 1412 | mNavigatorProperties.mPlatformOverridden = |
michael@0 | 1413 | mozilla::Preferences::GetString(PREF_GENERAL_PLATFORM_OVERRIDE); |
michael@0 | 1414 | |
michael@0 | 1415 | mNavigatorPropertiesLoaded = true; |
michael@0 | 1416 | } |
michael@0 | 1417 | |
michael@0 | 1418 | nsPIDOMWindow* window = aWorkerPrivate->GetWindow(); |
michael@0 | 1419 | |
michael@0 | 1420 | nsTArray<WorkerPrivate*>* windowArray; |
michael@0 | 1421 | if (!mWindowMap.Get(window, &windowArray)) { |
michael@0 | 1422 | windowArray = new nsTArray<WorkerPrivate*>(1); |
michael@0 | 1423 | mWindowMap.Put(window, windowArray); |
michael@0 | 1424 | } |
michael@0 | 1425 | |
michael@0 | 1426 | if (!windowArray->Contains(aWorkerPrivate)) { |
michael@0 | 1427 | windowArray->AppendElement(aWorkerPrivate); |
michael@0 | 1428 | } else { |
michael@0 | 1429 | MOZ_ASSERT(aWorkerPrivate->IsSharedWorker()); |
michael@0 | 1430 | } |
michael@0 | 1431 | } |
michael@0 | 1432 | |
michael@0 | 1433 | if (!queued && !ScheduleWorker(aCx, aWorkerPrivate)) { |
michael@0 | 1434 | return false; |
michael@0 | 1435 | } |
michael@0 | 1436 | |
michael@0 | 1437 | return true; |
michael@0 | 1438 | } |
michael@0 | 1439 | |
michael@0 | 1440 | void |
michael@0 | 1441 | RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) |
michael@0 | 1442 | { |
michael@0 | 1443 | aWorkerPrivate->AssertIsOnParentThread(); |
michael@0 | 1444 | |
michael@0 | 1445 | WorkerPrivate* parent = aWorkerPrivate->GetParent(); |
michael@0 | 1446 | if (!parent) { |
michael@0 | 1447 | AssertIsOnMainThread(); |
michael@0 | 1448 | } |
michael@0 | 1449 | |
michael@0 | 1450 | const nsCString& domain = aWorkerPrivate->Domain(); |
michael@0 | 1451 | |
michael@0 | 1452 | WorkerPrivate* queuedWorker = nullptr; |
michael@0 | 1453 | { |
michael@0 | 1454 | MutexAutoLock lock(mMutex); |
michael@0 | 1455 | |
michael@0 | 1456 | WorkerDomainInfo* domainInfo; |
michael@0 | 1457 | if (!mDomainMap.Get(domain, &domainInfo)) { |
michael@0 | 1458 | NS_ERROR("Don't have an entry for this domain!"); |
michael@0 | 1459 | } |
michael@0 | 1460 | |
michael@0 | 1461 | // Remove old worker from everywhere. |
michael@0 | 1462 | uint32_t index = domainInfo->mQueuedWorkers.IndexOf(aWorkerPrivate); |
michael@0 | 1463 | if (index != kNoIndex) { |
michael@0 | 1464 | // Was queued, remove from the list. |
michael@0 | 1465 | domainInfo->mQueuedWorkers.RemoveElementAt(index); |
michael@0 | 1466 | } |
michael@0 | 1467 | else if (parent) { |
michael@0 | 1468 | NS_ASSERTION(domainInfo->mChildWorkerCount, "Must be non-zero!"); |
michael@0 | 1469 | domainInfo->mChildWorkerCount--; |
michael@0 | 1470 | } |
michael@0 | 1471 | else { |
michael@0 | 1472 | NS_ASSERTION(domainInfo->mActiveWorkers.Contains(aWorkerPrivate), |
michael@0 | 1473 | "Don't know about this worker!"); |
michael@0 | 1474 | domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate); |
michael@0 | 1475 | } |
michael@0 | 1476 | |
michael@0 | 1477 | if (aWorkerPrivate->IsSharedWorker()) { |
michael@0 | 1478 | MatchSharedWorkerInfo match(aWorkerPrivate); |
michael@0 | 1479 | domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo, |
michael@0 | 1480 | &match); |
michael@0 | 1481 | |
michael@0 | 1482 | if (match.mSharedWorkerInfo) { |
michael@0 | 1483 | nsAutoCString key; |
michael@0 | 1484 | GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec, |
michael@0 | 1485 | match.mSharedWorkerInfo->mName, key); |
michael@0 | 1486 | domainInfo->mSharedWorkerInfos.Remove(key); |
michael@0 | 1487 | } |
michael@0 | 1488 | } |
michael@0 | 1489 | |
michael@0 | 1490 | // See if there's a queued worker we can schedule. |
michael@0 | 1491 | if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain && |
michael@0 | 1492 | !domainInfo->mQueuedWorkers.IsEmpty()) { |
michael@0 | 1493 | queuedWorker = domainInfo->mQueuedWorkers[0]; |
michael@0 | 1494 | domainInfo->mQueuedWorkers.RemoveElementAt(0); |
michael@0 | 1495 | |
michael@0 | 1496 | if (queuedWorker->GetParent()) { |
michael@0 | 1497 | domainInfo->mChildWorkerCount++; |
michael@0 | 1498 | } |
michael@0 | 1499 | else { |
michael@0 | 1500 | domainInfo->mActiveWorkers.AppendElement(queuedWorker); |
michael@0 | 1501 | } |
michael@0 | 1502 | } |
michael@0 | 1503 | |
michael@0 | 1504 | if (!domainInfo->ActiveWorkerCount()) { |
michael@0 | 1505 | MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty()); |
michael@0 | 1506 | mDomainMap.Remove(domain); |
michael@0 | 1507 | } |
michael@0 | 1508 | } |
michael@0 | 1509 | |
michael@0 | 1510 | if (aWorkerPrivate->IsSharedWorker()) { |
michael@0 | 1511 | AssertIsOnMainThread(); |
michael@0 | 1512 | |
michael@0 | 1513 | nsAutoTArray<nsRefPtr<SharedWorker>, 5> sharedWorkersToNotify; |
michael@0 | 1514 | aWorkerPrivate->GetAllSharedWorkers(sharedWorkersToNotify); |
michael@0 | 1515 | |
michael@0 | 1516 | for (uint32_t index = 0; index < sharedWorkersToNotify.Length(); index++) { |
michael@0 | 1517 | MOZ_ASSERT(sharedWorkersToNotify[index]); |
michael@0 | 1518 | sharedWorkersToNotify[index]->NoteDeadWorker(aCx); |
michael@0 | 1519 | } |
michael@0 | 1520 | } |
michael@0 | 1521 | |
michael@0 | 1522 | if (parent) { |
michael@0 | 1523 | parent->RemoveChildWorker(aCx, aWorkerPrivate); |
michael@0 | 1524 | } |
michael@0 | 1525 | else if (aWorkerPrivate->IsSharedWorker()) { |
michael@0 | 1526 | mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate); |
michael@0 | 1527 | } |
michael@0 | 1528 | else { |
michael@0 | 1529 | // May be null. |
michael@0 | 1530 | nsPIDOMWindow* window = aWorkerPrivate->GetWindow(); |
michael@0 | 1531 | |
michael@0 | 1532 | nsTArray<WorkerPrivate*>* windowArray; |
michael@0 | 1533 | MOZ_ALWAYS_TRUE(mWindowMap.Get(window, &windowArray)); |
michael@0 | 1534 | |
michael@0 | 1535 | MOZ_ALWAYS_TRUE(windowArray->RemoveElement(aWorkerPrivate)); |
michael@0 | 1536 | |
michael@0 | 1537 | if (windowArray->IsEmpty()) { |
michael@0 | 1538 | mWindowMap.Remove(window); |
michael@0 | 1539 | } |
michael@0 | 1540 | } |
michael@0 | 1541 | |
michael@0 | 1542 | if (queuedWorker && !ScheduleWorker(aCx, queuedWorker)) { |
michael@0 | 1543 | UnregisterWorker(aCx, queuedWorker); |
michael@0 | 1544 | } |
michael@0 | 1545 | } |
michael@0 | 1546 | |
michael@0 | 1547 | bool |
michael@0 | 1548 | RuntimeService::ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) |
michael@0 | 1549 | { |
michael@0 | 1550 | if (!aWorkerPrivate->Start()) { |
michael@0 | 1551 | // This is ok, means that we didn't need to make a thread for this worker. |
michael@0 | 1552 | return true; |
michael@0 | 1553 | } |
michael@0 | 1554 | |
michael@0 | 1555 | nsRefPtr<WorkerThread> thread; |
michael@0 | 1556 | { |
michael@0 | 1557 | MutexAutoLock lock(mMutex); |
michael@0 | 1558 | if (!mIdleThreadArray.IsEmpty()) { |
michael@0 | 1559 | uint32_t index = mIdleThreadArray.Length() - 1; |
michael@0 | 1560 | mIdleThreadArray[index].mThread.swap(thread); |
michael@0 | 1561 | mIdleThreadArray.RemoveElementAt(index); |
michael@0 | 1562 | } |
michael@0 | 1563 | } |
michael@0 | 1564 | |
michael@0 | 1565 | if (!thread) { |
michael@0 | 1566 | thread = WorkerThread::Create(); |
michael@0 | 1567 | if (!thread) { |
michael@0 | 1568 | UnregisterWorker(aCx, aWorkerPrivate); |
michael@0 | 1569 | JS_ReportError(aCx, "Could not create new thread!"); |
michael@0 | 1570 | return false; |
michael@0 | 1571 | } |
michael@0 | 1572 | } |
michael@0 | 1573 | |
michael@0 | 1574 | MOZ_ASSERT(thread->IsAcceptingNonWorkerRunnables()); |
michael@0 | 1575 | |
michael@0 | 1576 | int32_t priority = aWorkerPrivate->IsChromeWorker() ? |
michael@0 | 1577 | nsISupportsPriority::PRIORITY_NORMAL : |
michael@0 | 1578 | nsISupportsPriority::PRIORITY_LOW; |
michael@0 | 1579 | |
michael@0 | 1580 | if (NS_FAILED(thread->SetPriority(priority))) { |
michael@0 | 1581 | NS_WARNING("Could not set the thread's priority!"); |
michael@0 | 1582 | } |
michael@0 | 1583 | |
michael@0 | 1584 | nsCOMPtr<nsIRunnable> runnable = |
michael@0 | 1585 | new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread, JS_GetParentRuntime(aCx)); |
michael@0 | 1586 | if (NS_FAILED(thread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { |
michael@0 | 1587 | UnregisterWorker(aCx, aWorkerPrivate); |
michael@0 | 1588 | JS_ReportError(aCx, "Could not dispatch to thread!"); |
michael@0 | 1589 | return false; |
michael@0 | 1590 | } |
michael@0 | 1591 | |
michael@0 | 1592 | #ifdef DEBUG |
michael@0 | 1593 | thread->SetAcceptingNonWorkerRunnables(false); |
michael@0 | 1594 | #endif |
michael@0 | 1595 | |
michael@0 | 1596 | return true; |
michael@0 | 1597 | } |
michael@0 | 1598 | |
michael@0 | 1599 | // static |
michael@0 | 1600 | void |
michael@0 | 1601 | RuntimeService::ShutdownIdleThreads(nsITimer* aTimer, void* /* aClosure */) |
michael@0 | 1602 | { |
michael@0 | 1603 | AssertIsOnMainThread(); |
michael@0 | 1604 | |
michael@0 | 1605 | RuntimeService* runtime = RuntimeService::GetService(); |
michael@0 | 1606 | NS_ASSERTION(runtime, "This should never be null!"); |
michael@0 | 1607 | |
michael@0 | 1608 | NS_ASSERTION(aTimer == runtime->mIdleThreadTimer, "Wrong timer!"); |
michael@0 | 1609 | |
michael@0 | 1610 | // Cheat a little and grab all threads that expire within one second of now. |
michael@0 | 1611 | TimeStamp now = TimeStamp::Now() + TimeDuration::FromSeconds(1); |
michael@0 | 1612 | |
michael@0 | 1613 | TimeStamp nextExpiration; |
michael@0 | 1614 | |
michael@0 | 1615 | nsAutoTArray<nsRefPtr<WorkerThread>, 20> expiredThreads; |
michael@0 | 1616 | { |
michael@0 | 1617 | MutexAutoLock lock(runtime->mMutex); |
michael@0 | 1618 | |
michael@0 | 1619 | for (uint32_t index = 0; index < runtime->mIdleThreadArray.Length(); |
michael@0 | 1620 | index++) { |
michael@0 | 1621 | IdleThreadInfo& info = runtime->mIdleThreadArray[index]; |
michael@0 | 1622 | if (info.mExpirationTime > now) { |
michael@0 | 1623 | nextExpiration = info.mExpirationTime; |
michael@0 | 1624 | break; |
michael@0 | 1625 | } |
michael@0 | 1626 | |
michael@0 | 1627 | nsRefPtr<WorkerThread>* thread = expiredThreads.AppendElement(); |
michael@0 | 1628 | thread->swap(info.mThread); |
michael@0 | 1629 | } |
michael@0 | 1630 | |
michael@0 | 1631 | if (!expiredThreads.IsEmpty()) { |
michael@0 | 1632 | runtime->mIdleThreadArray.RemoveElementsAt(0, expiredThreads.Length()); |
michael@0 | 1633 | } |
michael@0 | 1634 | } |
michael@0 | 1635 | |
michael@0 | 1636 | NS_ASSERTION(nextExpiration.IsNull() || !expiredThreads.IsEmpty(), |
michael@0 | 1637 | "Should have a new time or there should be some threads to shut " |
michael@0 | 1638 | "down"); |
michael@0 | 1639 | |
michael@0 | 1640 | for (uint32_t index = 0; index < expiredThreads.Length(); index++) { |
michael@0 | 1641 | if (NS_FAILED(expiredThreads[index]->Shutdown())) { |
michael@0 | 1642 | NS_WARNING("Failed to shutdown thread!"); |
michael@0 | 1643 | } |
michael@0 | 1644 | } |
michael@0 | 1645 | |
michael@0 | 1646 | if (!nextExpiration.IsNull()) { |
michael@0 | 1647 | TimeDuration delta = nextExpiration - TimeStamp::Now(); |
michael@0 | 1648 | uint32_t delay(delta > TimeDuration(0) ? delta.ToMilliseconds() : 0); |
michael@0 | 1649 | |
michael@0 | 1650 | // Reschedule the timer. |
michael@0 | 1651 | if (NS_FAILED(aTimer->InitWithFuncCallback(ShutdownIdleThreads, nullptr, |
michael@0 | 1652 | delay, |
michael@0 | 1653 | nsITimer::TYPE_ONE_SHOT))) { |
michael@0 | 1654 | NS_ERROR("Can't schedule timer!"); |
michael@0 | 1655 | } |
michael@0 | 1656 | } |
michael@0 | 1657 | } |
michael@0 | 1658 | |
michael@0 | 1659 | nsresult |
michael@0 | 1660 | RuntimeService::Init() |
michael@0 | 1661 | { |
michael@0 | 1662 | AssertIsOnMainThread(); |
michael@0 | 1663 | |
michael@0 | 1664 | nsLayoutStatics::AddRef(); |
michael@0 | 1665 | |
michael@0 | 1666 | // Initialize JSSettings. |
michael@0 | 1667 | if (!sDefaultJSSettings.gcSettings[0].IsSet()) { |
michael@0 | 1668 | sDefaultJSSettings.runtimeOptions = JS::RuntimeOptions(); |
michael@0 | 1669 | sDefaultJSSettings.chrome.contextOptions = kRequiredContextOptions; |
michael@0 | 1670 | sDefaultJSSettings.chrome.maxScriptRuntime = -1; |
michael@0 | 1671 | sDefaultJSSettings.chrome.compartmentOptions.setVersion(JSVERSION_LATEST); |
michael@0 | 1672 | sDefaultJSSettings.content.contextOptions = kRequiredContextOptions; |
michael@0 | 1673 | sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC; |
michael@0 | 1674 | #ifdef JS_GC_ZEAL |
michael@0 | 1675 | sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ; |
michael@0 | 1676 | sDefaultJSSettings.gcZeal = 0; |
michael@0 | 1677 | #endif |
michael@0 | 1678 | SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE); |
michael@0 | 1679 | SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD, |
michael@0 | 1680 | WORKER_DEFAULT_ALLOCATION_THRESHOLD); |
michael@0 | 1681 | } |
michael@0 | 1682 | |
michael@0 | 1683 | // If dump is not controlled by pref, it's set to true. |
michael@0 | 1684 | #ifndef DUMP_CONTROLLED_BY_PREF |
michael@0 | 1685 | sDefaultPreferences[WORKERPREF_DUMP] = true; |
michael@0 | 1686 | #endif |
michael@0 | 1687 | |
michael@0 | 1688 | mIdleThreadTimer = do_CreateInstance(NS_TIMER_CONTRACTID); |
michael@0 | 1689 | NS_ENSURE_STATE(mIdleThreadTimer); |
michael@0 | 1690 | |
michael@0 | 1691 | nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
michael@0 | 1692 | NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); |
michael@0 | 1693 | |
michael@0 | 1694 | nsresult rv = |
michael@0 | 1695 | obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false); |
michael@0 | 1696 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1697 | |
michael@0 | 1698 | rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); |
michael@0 | 1699 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1700 | |
michael@0 | 1701 | mObserved = true; |
michael@0 | 1702 | |
michael@0 | 1703 | if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) { |
michael@0 | 1704 | NS_WARNING("Failed to register for GC request notifications!"); |
michael@0 | 1705 | } |
michael@0 | 1706 | |
michael@0 | 1707 | if (NS_FAILED(obs->AddObserver(this, CC_REQUEST_OBSERVER_TOPIC, false))) { |
michael@0 | 1708 | NS_WARNING("Failed to register for CC request notifications!"); |
michael@0 | 1709 | } |
michael@0 | 1710 | |
michael@0 | 1711 | if (NS_FAILED(obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC, |
michael@0 | 1712 | false))) { |
michael@0 | 1713 | NS_WARNING("Failed to register for memory pressure notifications!"); |
michael@0 | 1714 | } |
michael@0 | 1715 | |
michael@0 | 1716 | if (NS_FAILED(obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) { |
michael@0 | 1717 | NS_WARNING("Failed to register for offline notification event!"); |
michael@0 | 1718 | } |
michael@0 | 1719 | |
michael@0 | 1720 | NS_ASSERTION(!gRuntimeServiceDuringInit, "This should be null!"); |
michael@0 | 1721 | gRuntimeServiceDuringInit = this; |
michael@0 | 1722 | |
michael@0 | 1723 | if (NS_FAILED(Preferences::RegisterCallback( |
michael@0 | 1724 | LoadJSGCMemoryOptions, |
michael@0 | 1725 | PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, |
michael@0 | 1726 | nullptr)) || |
michael@0 | 1727 | NS_FAILED(Preferences::RegisterCallbackAndCall( |
michael@0 | 1728 | LoadJSGCMemoryOptions, |
michael@0 | 1729 | PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, |
michael@0 | 1730 | nullptr)) || |
michael@0 | 1731 | #ifdef JS_GC_ZEAL |
michael@0 | 1732 | NS_FAILED(Preferences::RegisterCallback( |
michael@0 | 1733 | LoadGCZealOptions, |
michael@0 | 1734 | PREF_JS_OPTIONS_PREFIX PREF_GCZEAL, |
michael@0 | 1735 | nullptr)) || |
michael@0 | 1736 | NS_FAILED(Preferences::RegisterCallbackAndCall( |
michael@0 | 1737 | LoadGCZealOptions, |
michael@0 | 1738 | PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL, |
michael@0 | 1739 | nullptr)) || |
michael@0 | 1740 | #endif |
michael@0 | 1741 | #if DUMP_CONTROLLED_BY_PREF |
michael@0 | 1742 | NS_FAILED(Preferences::RegisterCallbackAndCall( |
michael@0 | 1743 | WorkerPrefChanged, |
michael@0 | 1744 | PREF_DOM_WINDOW_DUMP_ENABLED, |
michael@0 | 1745 | reinterpret_cast<void *>(WORKERPREF_DUMP))) || |
michael@0 | 1746 | #endif |
michael@0 | 1747 | NS_FAILED(Preferences::RegisterCallback(LoadRuntimeAndContextOptions, |
michael@0 | 1748 | PREF_JS_OPTIONS_PREFIX, |
michael@0 | 1749 | nullptr)) || |
michael@0 | 1750 | NS_FAILED(Preferences::RegisterCallbackAndCall( |
michael@0 | 1751 | LoadRuntimeAndContextOptions, |
michael@0 | 1752 | PREF_WORKERS_OPTIONS_PREFIX, |
michael@0 | 1753 | nullptr)) || |
michael@0 | 1754 | NS_FAILED(Preferences::RegisterCallbackAndCall( |
michael@0 | 1755 | AppNameOverrideChanged, |
michael@0 | 1756 | PREF_GENERAL_APPNAME_OVERRIDE, |
michael@0 | 1757 | nullptr)) || |
michael@0 | 1758 | NS_FAILED(Preferences::RegisterCallbackAndCall( |
michael@0 | 1759 | AppVersionOverrideChanged, |
michael@0 | 1760 | PREF_GENERAL_APPVERSION_OVERRIDE, |
michael@0 | 1761 | nullptr)) || |
michael@0 | 1762 | NS_FAILED(Preferences::RegisterCallbackAndCall( |
michael@0 | 1763 | PlatformOverrideChanged, |
michael@0 | 1764 | PREF_GENERAL_PLATFORM_OVERRIDE, |
michael@0 | 1765 | nullptr)) || |
michael@0 | 1766 | NS_FAILED(Preferences::RegisterCallbackAndCall( |
michael@0 | 1767 | JSVersionChanged, |
michael@0 | 1768 | PREF_WORKERS_LATEST_JS_VERSION, |
michael@0 | 1769 | nullptr))) { |
michael@0 | 1770 | NS_WARNING("Failed to register pref callbacks!"); |
michael@0 | 1771 | } |
michael@0 | 1772 | |
michael@0 | 1773 | NS_ASSERTION(gRuntimeServiceDuringInit == this, "Should be 'this'!"); |
michael@0 | 1774 | gRuntimeServiceDuringInit = nullptr; |
michael@0 | 1775 | |
michael@0 | 1776 | // We assume atomic 32bit reads/writes. If this assumption doesn't hold on |
michael@0 | 1777 | // some wacky platform then the worst that could happen is that the close |
michael@0 | 1778 | // handler will run for a slightly different amount of time. |
michael@0 | 1779 | if (NS_FAILED(Preferences::AddIntVarCache( |
michael@0 | 1780 | &sDefaultJSSettings.content.maxScriptRuntime, |
michael@0 | 1781 | PREF_MAX_SCRIPT_RUN_TIME_CONTENT, |
michael@0 | 1782 | MAX_SCRIPT_RUN_TIME_SEC)) || |
michael@0 | 1783 | NS_FAILED(Preferences::AddIntVarCache( |
michael@0 | 1784 | &sDefaultJSSettings.chrome.maxScriptRuntime, |
michael@0 | 1785 | PREF_MAX_SCRIPT_RUN_TIME_CHROME, -1))) { |
michael@0 | 1786 | NS_WARNING("Failed to register timeout cache!"); |
michael@0 | 1787 | } |
michael@0 | 1788 | |
michael@0 | 1789 | int32_t maxPerDomain = Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, |
michael@0 | 1790 | MAX_WORKERS_PER_DOMAIN); |
michael@0 | 1791 | gMaxWorkersPerDomain = std::max(0, maxPerDomain); |
michael@0 | 1792 | |
michael@0 | 1793 | rv = InitOSFileConstants(); |
michael@0 | 1794 | if (NS_FAILED(rv)) { |
michael@0 | 1795 | return rv; |
michael@0 | 1796 | } |
michael@0 | 1797 | |
michael@0 | 1798 | return NS_OK; |
michael@0 | 1799 | } |
michael@0 | 1800 | |
michael@0 | 1801 | void |
michael@0 | 1802 | RuntimeService::Shutdown() |
michael@0 | 1803 | { |
michael@0 | 1804 | AssertIsOnMainThread(); |
michael@0 | 1805 | |
michael@0 | 1806 | MOZ_ASSERT(!mShuttingDown); |
michael@0 | 1807 | // That's it, no more workers. |
michael@0 | 1808 | mShuttingDown = true; |
michael@0 | 1809 | |
michael@0 | 1810 | nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
michael@0 | 1811 | NS_WARN_IF_FALSE(obs, "Failed to get observer service?!"); |
michael@0 | 1812 | |
michael@0 | 1813 | // Tell anyone that cares that they're about to lose worker support. |
michael@0 | 1814 | if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC, |
michael@0 | 1815 | nullptr))) { |
michael@0 | 1816 | NS_WARNING("NotifyObservers failed!"); |
michael@0 | 1817 | } |
michael@0 | 1818 | |
michael@0 | 1819 | { |
michael@0 | 1820 | MutexAutoLock lock(mMutex); |
michael@0 | 1821 | |
michael@0 | 1822 | nsAutoTArray<WorkerPrivate*, 100> workers; |
michael@0 | 1823 | mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers); |
michael@0 | 1824 | |
michael@0 | 1825 | if (!workers.IsEmpty()) { |
michael@0 | 1826 | // Cancel all top-level workers. |
michael@0 | 1827 | { |
michael@0 | 1828 | MutexAutoUnlock unlock(mMutex); |
michael@0 | 1829 | |
michael@0 | 1830 | AutoSafeJSContext cx; |
michael@0 | 1831 | JSAutoRequest ar(cx); |
michael@0 | 1832 | |
michael@0 | 1833 | for (uint32_t index = 0; index < workers.Length(); index++) { |
michael@0 | 1834 | if (!workers[index]->Kill(cx)) { |
michael@0 | 1835 | NS_WARNING("Failed to cancel worker!"); |
michael@0 | 1836 | } |
michael@0 | 1837 | } |
michael@0 | 1838 | } |
michael@0 | 1839 | } |
michael@0 | 1840 | } |
michael@0 | 1841 | } |
michael@0 | 1842 | |
michael@0 | 1843 | // This spins the event loop until all workers are finished and their threads |
michael@0 | 1844 | // have been joined. |
michael@0 | 1845 | void |
michael@0 | 1846 | RuntimeService::Cleanup() |
michael@0 | 1847 | { |
michael@0 | 1848 | AssertIsOnMainThread(); |
michael@0 | 1849 | |
michael@0 | 1850 | nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
michael@0 | 1851 | NS_WARN_IF_FALSE(obs, "Failed to get observer service?!"); |
michael@0 | 1852 | |
michael@0 | 1853 | if (mIdleThreadTimer) { |
michael@0 | 1854 | if (NS_FAILED(mIdleThreadTimer->Cancel())) { |
michael@0 | 1855 | NS_WARNING("Failed to cancel idle timer!"); |
michael@0 | 1856 | } |
michael@0 | 1857 | mIdleThreadTimer = nullptr; |
michael@0 | 1858 | } |
michael@0 | 1859 | |
michael@0 | 1860 | { |
michael@0 | 1861 | MutexAutoLock lock(mMutex); |
michael@0 | 1862 | |
michael@0 | 1863 | nsAutoTArray<WorkerPrivate*, 100> workers; |
michael@0 | 1864 | mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers); |
michael@0 | 1865 | |
michael@0 | 1866 | if (!workers.IsEmpty()) { |
michael@0 | 1867 | nsIThread* currentThread = NS_GetCurrentThread(); |
michael@0 | 1868 | NS_ASSERTION(currentThread, "This should never be null!"); |
michael@0 | 1869 | |
michael@0 | 1870 | // Shut down any idle threads. |
michael@0 | 1871 | if (!mIdleThreadArray.IsEmpty()) { |
michael@0 | 1872 | nsAutoTArray<nsRefPtr<WorkerThread>, 20> idleThreads; |
michael@0 | 1873 | |
michael@0 | 1874 | uint32_t idleThreadCount = mIdleThreadArray.Length(); |
michael@0 | 1875 | idleThreads.SetLength(idleThreadCount); |
michael@0 | 1876 | |
michael@0 | 1877 | for (uint32_t index = 0; index < idleThreadCount; index++) { |
michael@0 | 1878 | NS_ASSERTION(mIdleThreadArray[index].mThread, "Null thread!"); |
michael@0 | 1879 | idleThreads[index].swap(mIdleThreadArray[index].mThread); |
michael@0 | 1880 | } |
michael@0 | 1881 | |
michael@0 | 1882 | mIdleThreadArray.Clear(); |
michael@0 | 1883 | |
michael@0 | 1884 | MutexAutoUnlock unlock(mMutex); |
michael@0 | 1885 | |
michael@0 | 1886 | for (uint32_t index = 0; index < idleThreadCount; index++) { |
michael@0 | 1887 | if (NS_FAILED(idleThreads[index]->Shutdown())) { |
michael@0 | 1888 | NS_WARNING("Failed to shutdown thread!"); |
michael@0 | 1889 | } |
michael@0 | 1890 | } |
michael@0 | 1891 | } |
michael@0 | 1892 | |
michael@0 | 1893 | // And make sure all their final messages have run and all their threads |
michael@0 | 1894 | // have joined. |
michael@0 | 1895 | while (mDomainMap.Count()) { |
michael@0 | 1896 | MutexAutoUnlock unlock(mMutex); |
michael@0 | 1897 | |
michael@0 | 1898 | if (!NS_ProcessNextEvent(currentThread)) { |
michael@0 | 1899 | NS_WARNING("Something bad happened!"); |
michael@0 | 1900 | break; |
michael@0 | 1901 | } |
michael@0 | 1902 | } |
michael@0 | 1903 | } |
michael@0 | 1904 | } |
michael@0 | 1905 | |
michael@0 | 1906 | NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!"); |
michael@0 | 1907 | |
michael@0 | 1908 | if (mObserved) { |
michael@0 | 1909 | if (NS_FAILED(Preferences::UnregisterCallback(JSVersionChanged, |
michael@0 | 1910 | PREF_WORKERS_LATEST_JS_VERSION, |
michael@0 | 1911 | nullptr)) || |
michael@0 | 1912 | NS_FAILED(Preferences::UnregisterCallback(AppNameOverrideChanged, |
michael@0 | 1913 | PREF_GENERAL_APPNAME_OVERRIDE, |
michael@0 | 1914 | nullptr)) || |
michael@0 | 1915 | NS_FAILED(Preferences::UnregisterCallback(AppVersionOverrideChanged, |
michael@0 | 1916 | PREF_GENERAL_APPVERSION_OVERRIDE, |
michael@0 | 1917 | nullptr)) || |
michael@0 | 1918 | NS_FAILED(Preferences::UnregisterCallback(PlatformOverrideChanged, |
michael@0 | 1919 | PREF_GENERAL_PLATFORM_OVERRIDE, |
michael@0 | 1920 | nullptr)) || |
michael@0 | 1921 | NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeAndContextOptions, |
michael@0 | 1922 | PREF_JS_OPTIONS_PREFIX, |
michael@0 | 1923 | nullptr)) || |
michael@0 | 1924 | NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeAndContextOptions, |
michael@0 | 1925 | PREF_WORKERS_OPTIONS_PREFIX, |
michael@0 | 1926 | nullptr)) || |
michael@0 | 1927 | #if DUMP_CONTROLLED_BY_PREF |
michael@0 | 1928 | NS_FAILED(Preferences::UnregisterCallback( |
michael@0 | 1929 | WorkerPrefChanged, |
michael@0 | 1930 | PREF_DOM_WINDOW_DUMP_ENABLED, |
michael@0 | 1931 | reinterpret_cast<void *>(WORKERPREF_DUMP))) || |
michael@0 | 1932 | #endif |
michael@0 | 1933 | #ifdef JS_GC_ZEAL |
michael@0 | 1934 | NS_FAILED(Preferences::UnregisterCallback( |
michael@0 | 1935 | LoadGCZealOptions, |
michael@0 | 1936 | PREF_JS_OPTIONS_PREFIX PREF_GCZEAL, |
michael@0 | 1937 | nullptr)) || |
michael@0 | 1938 | NS_FAILED(Preferences::UnregisterCallback( |
michael@0 | 1939 | LoadGCZealOptions, |
michael@0 | 1940 | PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL, |
michael@0 | 1941 | nullptr)) || |
michael@0 | 1942 | #endif |
michael@0 | 1943 | NS_FAILED(Preferences::UnregisterCallback( |
michael@0 | 1944 | LoadJSGCMemoryOptions, |
michael@0 | 1945 | PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, |
michael@0 | 1946 | nullptr)) || |
michael@0 | 1947 | NS_FAILED(Preferences::UnregisterCallback( |
michael@0 | 1948 | LoadJSGCMemoryOptions, |
michael@0 | 1949 | PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, |
michael@0 | 1950 | nullptr))) { |
michael@0 | 1951 | NS_WARNING("Failed to unregister pref callbacks!"); |
michael@0 | 1952 | } |
michael@0 | 1953 | |
michael@0 | 1954 | if (obs) { |
michael@0 | 1955 | if (NS_FAILED(obs->RemoveObserver(this, GC_REQUEST_OBSERVER_TOPIC))) { |
michael@0 | 1956 | NS_WARNING("Failed to unregister for GC request notifications!"); |
michael@0 | 1957 | } |
michael@0 | 1958 | |
michael@0 | 1959 | if (NS_FAILED(obs->RemoveObserver(this, CC_REQUEST_OBSERVER_TOPIC))) { |
michael@0 | 1960 | NS_WARNING("Failed to unregister for CC request notifications!"); |
michael@0 | 1961 | } |
michael@0 | 1962 | |
michael@0 | 1963 | if (NS_FAILED(obs->RemoveObserver(this, |
michael@0 | 1964 | MEMORY_PRESSURE_OBSERVER_TOPIC))) { |
michael@0 | 1965 | NS_WARNING("Failed to unregister for memory pressure notifications!"); |
michael@0 | 1966 | } |
michael@0 | 1967 | |
michael@0 | 1968 | if (NS_FAILED(obs->RemoveObserver(this, |
michael@0 | 1969 | NS_IOSERVICE_OFFLINE_STATUS_TOPIC))) { |
michael@0 | 1970 | NS_WARNING("Failed to unregister for offline notification event!"); |
michael@0 | 1971 | } |
michael@0 | 1972 | obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID); |
michael@0 | 1973 | obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); |
michael@0 | 1974 | mObserved = false; |
michael@0 | 1975 | } |
michael@0 | 1976 | } |
michael@0 | 1977 | |
michael@0 | 1978 | CleanupOSFileConstants(); |
michael@0 | 1979 | nsLayoutStatics::Release(); |
michael@0 | 1980 | } |
michael@0 | 1981 | |
michael@0 | 1982 | // static |
michael@0 | 1983 | PLDHashOperator |
michael@0 | 1984 | RuntimeService::AddAllTopLevelWorkersToArray(const nsACString& aKey, |
michael@0 | 1985 | WorkerDomainInfo* aData, |
michael@0 | 1986 | void* aUserArg) |
michael@0 | 1987 | { |
michael@0 | 1988 | nsTArray<WorkerPrivate*>* array = |
michael@0 | 1989 | static_cast<nsTArray<WorkerPrivate*>*>(aUserArg); |
michael@0 | 1990 | |
michael@0 | 1991 | #ifdef DEBUG |
michael@0 | 1992 | for (uint32_t index = 0; index < aData->mActiveWorkers.Length(); index++) { |
michael@0 | 1993 | NS_ASSERTION(!aData->mActiveWorkers[index]->GetParent(), |
michael@0 | 1994 | "Shouldn't have a parent in this list!"); |
michael@0 | 1995 | } |
michael@0 | 1996 | #endif |
michael@0 | 1997 | |
michael@0 | 1998 | array->AppendElements(aData->mActiveWorkers); |
michael@0 | 1999 | |
michael@0 | 2000 | // These might not be top-level workers... |
michael@0 | 2001 | for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) { |
michael@0 | 2002 | WorkerPrivate* worker = aData->mQueuedWorkers[index]; |
michael@0 | 2003 | if (!worker->GetParent()) { |
michael@0 | 2004 | array->AppendElement(worker); |
michael@0 | 2005 | } |
michael@0 | 2006 | } |
michael@0 | 2007 | |
michael@0 | 2008 | return PL_DHASH_NEXT; |
michael@0 | 2009 | } |
michael@0 | 2010 | |
michael@0 | 2011 | // static |
michael@0 | 2012 | PLDHashOperator |
michael@0 | 2013 | RuntimeService::RemoveSharedWorkerFromWindowMap( |
michael@0 | 2014 | nsPIDOMWindow* aKey, |
michael@0 | 2015 | nsAutoPtr<nsTArray<WorkerPrivate*> >& aData, |
michael@0 | 2016 | void* aUserArg) |
michael@0 | 2017 | { |
michael@0 | 2018 | AssertIsOnMainThread(); |
michael@0 | 2019 | MOZ_ASSERT(aData.get()); |
michael@0 | 2020 | MOZ_ASSERT(aUserArg); |
michael@0 | 2021 | |
michael@0 | 2022 | auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg); |
michael@0 | 2023 | |
michael@0 | 2024 | MOZ_ASSERT(workerPrivate->IsSharedWorker()); |
michael@0 | 2025 | |
michael@0 | 2026 | if (aData->RemoveElement(workerPrivate)) { |
michael@0 | 2027 | MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!"); |
michael@0 | 2028 | |
michael@0 | 2029 | if (aData->IsEmpty()) { |
michael@0 | 2030 | return PL_DHASH_REMOVE; |
michael@0 | 2031 | } |
michael@0 | 2032 | } |
michael@0 | 2033 | |
michael@0 | 2034 | return PL_DHASH_NEXT; |
michael@0 | 2035 | } |
michael@0 | 2036 | |
michael@0 | 2037 | // static |
michael@0 | 2038 | PLDHashOperator |
michael@0 | 2039 | RuntimeService::FindSharedWorkerInfo(const nsACString& aKey, |
michael@0 | 2040 | SharedWorkerInfo* aData, |
michael@0 | 2041 | void* aUserArg) |
michael@0 | 2042 | { |
michael@0 | 2043 | auto match = static_cast<MatchSharedWorkerInfo*>(aUserArg); |
michael@0 | 2044 | |
michael@0 | 2045 | if (aData->mWorkerPrivate == match->mWorkerPrivate) { |
michael@0 | 2046 | match->mSharedWorkerInfo = aData; |
michael@0 | 2047 | return PL_DHASH_STOP; |
michael@0 | 2048 | } |
michael@0 | 2049 | |
michael@0 | 2050 | return PL_DHASH_NEXT; |
michael@0 | 2051 | } |
michael@0 | 2052 | |
michael@0 | 2053 | void |
michael@0 | 2054 | RuntimeService::GetWorkersForWindow(nsPIDOMWindow* aWindow, |
michael@0 | 2055 | nsTArray<WorkerPrivate*>& aWorkers) |
michael@0 | 2056 | { |
michael@0 | 2057 | AssertIsOnMainThread(); |
michael@0 | 2058 | |
michael@0 | 2059 | nsTArray<WorkerPrivate*>* workers; |
michael@0 | 2060 | if (mWindowMap.Get(aWindow, &workers)) { |
michael@0 | 2061 | NS_ASSERTION(!workers->IsEmpty(), "Should have been removed!"); |
michael@0 | 2062 | aWorkers.AppendElements(*workers); |
michael@0 | 2063 | } |
michael@0 | 2064 | else { |
michael@0 | 2065 | NS_ASSERTION(aWorkers.IsEmpty(), "Should be empty!"); |
michael@0 | 2066 | } |
michael@0 | 2067 | } |
michael@0 | 2068 | |
michael@0 | 2069 | void |
michael@0 | 2070 | RuntimeService::CancelWorkersForWindow(nsPIDOMWindow* aWindow) |
michael@0 | 2071 | { |
michael@0 | 2072 | AssertIsOnMainThread(); |
michael@0 | 2073 | |
michael@0 | 2074 | nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers; |
michael@0 | 2075 | GetWorkersForWindow(aWindow, workers); |
michael@0 | 2076 | |
michael@0 | 2077 | if (!workers.IsEmpty()) { |
michael@0 | 2078 | nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow); |
michael@0 | 2079 | MOZ_ASSERT(sgo); |
michael@0 | 2080 | |
michael@0 | 2081 | nsIScriptContext* scx = sgo->GetContext(); |
michael@0 | 2082 | |
michael@0 | 2083 | AutoPushJSContext cx(scx ? |
michael@0 | 2084 | scx->GetNativeContext() : |
michael@0 | 2085 | nsContentUtils::GetSafeJSContext()); |
michael@0 | 2086 | |
michael@0 | 2087 | for (uint32_t index = 0; index < workers.Length(); index++) { |
michael@0 | 2088 | WorkerPrivate*& worker = workers[index]; |
michael@0 | 2089 | |
michael@0 | 2090 | if (worker->IsSharedWorker()) { |
michael@0 | 2091 | worker->CloseSharedWorkersForWindow(aWindow); |
michael@0 | 2092 | } else if (!worker->Cancel(cx)) { |
michael@0 | 2093 | JS_ReportPendingException(cx); |
michael@0 | 2094 | } |
michael@0 | 2095 | } |
michael@0 | 2096 | } |
michael@0 | 2097 | } |
michael@0 | 2098 | |
michael@0 | 2099 | void |
michael@0 | 2100 | RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow) |
michael@0 | 2101 | { |
michael@0 | 2102 | AssertIsOnMainThread(); |
michael@0 | 2103 | MOZ_ASSERT(aWindow); |
michael@0 | 2104 | |
michael@0 | 2105 | nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers; |
michael@0 | 2106 | GetWorkersForWindow(aWindow, workers); |
michael@0 | 2107 | |
michael@0 | 2108 | if (!workers.IsEmpty()) { |
michael@0 | 2109 | nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow); |
michael@0 | 2110 | MOZ_ASSERT(sgo); |
michael@0 | 2111 | |
michael@0 | 2112 | nsIScriptContext* scx = sgo->GetContext(); |
michael@0 | 2113 | |
michael@0 | 2114 | AutoPushJSContext cx(scx ? |
michael@0 | 2115 | scx->GetNativeContext() : |
michael@0 | 2116 | nsContentUtils::GetSafeJSContext()); |
michael@0 | 2117 | |
michael@0 | 2118 | for (uint32_t index = 0; index < workers.Length(); index++) { |
michael@0 | 2119 | if (!workers[index]->Suspend(cx, aWindow)) { |
michael@0 | 2120 | JS_ReportPendingException(cx); |
michael@0 | 2121 | } |
michael@0 | 2122 | } |
michael@0 | 2123 | } |
michael@0 | 2124 | } |
michael@0 | 2125 | |
michael@0 | 2126 | void |
michael@0 | 2127 | RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow) |
michael@0 | 2128 | { |
michael@0 | 2129 | AssertIsOnMainThread(); |
michael@0 | 2130 | MOZ_ASSERT(aWindow); |
michael@0 | 2131 | |
michael@0 | 2132 | nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers; |
michael@0 | 2133 | GetWorkersForWindow(aWindow, workers); |
michael@0 | 2134 | |
michael@0 | 2135 | if (!workers.IsEmpty()) { |
michael@0 | 2136 | nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow); |
michael@0 | 2137 | MOZ_ASSERT(sgo); |
michael@0 | 2138 | |
michael@0 | 2139 | nsIScriptContext* scx = sgo->GetContext(); |
michael@0 | 2140 | |
michael@0 | 2141 | AutoPushJSContext cx(scx ? |
michael@0 | 2142 | scx->GetNativeContext() : |
michael@0 | 2143 | nsContentUtils::GetSafeJSContext()); |
michael@0 | 2144 | |
michael@0 | 2145 | for (uint32_t index = 0; index < workers.Length(); index++) { |
michael@0 | 2146 | if (!workers[index]->SynchronizeAndResume(cx, aWindow, scx)) { |
michael@0 | 2147 | JS_ReportPendingException(cx); |
michael@0 | 2148 | } |
michael@0 | 2149 | } |
michael@0 | 2150 | } |
michael@0 | 2151 | } |
michael@0 | 2152 | |
michael@0 | 2153 | nsresult |
michael@0 | 2154 | RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal, |
michael@0 | 2155 | const nsAString& aScriptURL, |
michael@0 | 2156 | const nsACString& aName, |
michael@0 | 2157 | SharedWorker** aSharedWorker) |
michael@0 | 2158 | { |
michael@0 | 2159 | AssertIsOnMainThread(); |
michael@0 | 2160 | |
michael@0 | 2161 | nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); |
michael@0 | 2162 | MOZ_ASSERT(window); |
michael@0 | 2163 | |
michael@0 | 2164 | JSContext* cx = aGlobal.GetContext(); |
michael@0 | 2165 | |
michael@0 | 2166 | WorkerPrivate::LoadInfo loadInfo; |
michael@0 | 2167 | nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL, |
michael@0 | 2168 | false, &loadInfo); |
michael@0 | 2169 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2170 | |
michael@0 | 2171 | MOZ_ASSERT(loadInfo.mResolvedScriptURI); |
michael@0 | 2172 | |
michael@0 | 2173 | nsCString scriptSpec; |
michael@0 | 2174 | rv = loadInfo.mResolvedScriptURI->GetSpec(scriptSpec); |
michael@0 | 2175 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2176 | |
michael@0 | 2177 | nsRefPtr<WorkerPrivate> workerPrivate; |
michael@0 | 2178 | { |
michael@0 | 2179 | MutexAutoLock lock(mMutex); |
michael@0 | 2180 | |
michael@0 | 2181 | WorkerDomainInfo* domainInfo; |
michael@0 | 2182 | SharedWorkerInfo* sharedWorkerInfo; |
michael@0 | 2183 | |
michael@0 | 2184 | nsAutoCString key; |
michael@0 | 2185 | GenerateSharedWorkerKey(scriptSpec, aName, key); |
michael@0 | 2186 | |
michael@0 | 2187 | if (mDomainMap.Get(loadInfo.mDomain, &domainInfo) && |
michael@0 | 2188 | domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) { |
michael@0 | 2189 | workerPrivate = sharedWorkerInfo->mWorkerPrivate; |
michael@0 | 2190 | } |
michael@0 | 2191 | } |
michael@0 | 2192 | |
michael@0 | 2193 | bool created = false; |
michael@0 | 2194 | |
michael@0 | 2195 | if (!workerPrivate) { |
michael@0 | 2196 | ErrorResult rv; |
michael@0 | 2197 | workerPrivate = |
michael@0 | 2198 | WorkerPrivate::Constructor(aGlobal, aScriptURL, false, |
michael@0 | 2199 | WorkerPrivate::WorkerTypeShared, aName, |
michael@0 | 2200 | &loadInfo, rv); |
michael@0 | 2201 | NS_ENSURE_TRUE(workerPrivate, rv.ErrorCode()); |
michael@0 | 2202 | |
michael@0 | 2203 | created = true; |
michael@0 | 2204 | } |
michael@0 | 2205 | |
michael@0 | 2206 | MOZ_ASSERT(workerPrivate->IsSharedWorker()); |
michael@0 | 2207 | |
michael@0 | 2208 | nsRefPtr<SharedWorker> sharedWorker = |
michael@0 | 2209 | new SharedWorker(window, workerPrivate); |
michael@0 | 2210 | |
michael@0 | 2211 | if (!workerPrivate->RegisterSharedWorker(cx, sharedWorker)) { |
michael@0 | 2212 | NS_WARNING("Worker is unreachable, this shouldn't happen!"); |
michael@0 | 2213 | sharedWorker->Close(); |
michael@0 | 2214 | return NS_ERROR_FAILURE; |
michael@0 | 2215 | } |
michael@0 | 2216 | |
michael@0 | 2217 | // This is normally handled in RegisterWorker, but that wasn't called if the |
michael@0 | 2218 | // worker already existed. |
michael@0 | 2219 | if (!created) { |
michael@0 | 2220 | nsTArray<WorkerPrivate*>* windowArray; |
michael@0 | 2221 | if (!mWindowMap.Get(window, &windowArray)) { |
michael@0 | 2222 | windowArray = new nsTArray<WorkerPrivate*>(1); |
michael@0 | 2223 | mWindowMap.Put(window, windowArray); |
michael@0 | 2224 | } |
michael@0 | 2225 | |
michael@0 | 2226 | if (!windowArray->Contains(workerPrivate)) { |
michael@0 | 2227 | windowArray->AppendElement(workerPrivate); |
michael@0 | 2228 | } |
michael@0 | 2229 | } |
michael@0 | 2230 | |
michael@0 | 2231 | sharedWorker.forget(aSharedWorker); |
michael@0 | 2232 | return NS_OK; |
michael@0 | 2233 | } |
michael@0 | 2234 | |
michael@0 | 2235 | void |
michael@0 | 2236 | RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate) |
michael@0 | 2237 | { |
michael@0 | 2238 | AssertIsOnMainThread(); |
michael@0 | 2239 | MOZ_ASSERT(aWorkerPrivate); |
michael@0 | 2240 | MOZ_ASSERT(aWorkerPrivate->IsSharedWorker()); |
michael@0 | 2241 | |
michael@0 | 2242 | MutexAutoLock lock(mMutex); |
michael@0 | 2243 | |
michael@0 | 2244 | WorkerDomainInfo* domainInfo; |
michael@0 | 2245 | if (mDomainMap.Get(aWorkerPrivate->Domain(), &domainInfo)) { |
michael@0 | 2246 | MatchSharedWorkerInfo match(aWorkerPrivate); |
michael@0 | 2247 | domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo, |
michael@0 | 2248 | &match); |
michael@0 | 2249 | |
michael@0 | 2250 | if (match.mSharedWorkerInfo) { |
michael@0 | 2251 | nsAutoCString key; |
michael@0 | 2252 | GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec, |
michael@0 | 2253 | match.mSharedWorkerInfo->mName, key); |
michael@0 | 2254 | domainInfo->mSharedWorkerInfos.Remove(key); |
michael@0 | 2255 | } |
michael@0 | 2256 | } |
michael@0 | 2257 | } |
michael@0 | 2258 | |
michael@0 | 2259 | void |
michael@0 | 2260 | RuntimeService::NoteIdleThread(WorkerThread* aThread) |
michael@0 | 2261 | { |
michael@0 | 2262 | AssertIsOnMainThread(); |
michael@0 | 2263 | MOZ_ASSERT(aThread); |
michael@0 | 2264 | |
michael@0 | 2265 | #ifdef DEBUG |
michael@0 | 2266 | aThread->SetAcceptingNonWorkerRunnables(true); |
michael@0 | 2267 | #endif |
michael@0 | 2268 | |
michael@0 | 2269 | static TimeDuration timeout = |
michael@0 | 2270 | TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC); |
michael@0 | 2271 | |
michael@0 | 2272 | TimeStamp expirationTime = TimeStamp::Now() + timeout; |
michael@0 | 2273 | |
michael@0 | 2274 | bool shutdown; |
michael@0 | 2275 | if (mShuttingDown) { |
michael@0 | 2276 | shutdown = true; |
michael@0 | 2277 | } |
michael@0 | 2278 | else { |
michael@0 | 2279 | MutexAutoLock lock(mMutex); |
michael@0 | 2280 | |
michael@0 | 2281 | if (mIdleThreadArray.Length() < MAX_IDLE_THREADS) { |
michael@0 | 2282 | IdleThreadInfo* info = mIdleThreadArray.AppendElement(); |
michael@0 | 2283 | info->mThread = aThread; |
michael@0 | 2284 | info->mExpirationTime = expirationTime; |
michael@0 | 2285 | shutdown = false; |
michael@0 | 2286 | } |
michael@0 | 2287 | else { |
michael@0 | 2288 | shutdown = true; |
michael@0 | 2289 | } |
michael@0 | 2290 | } |
michael@0 | 2291 | |
michael@0 | 2292 | // Too many idle threads, just shut this one down. |
michael@0 | 2293 | if (shutdown) { |
michael@0 | 2294 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->Shutdown())); |
michael@0 | 2295 | return; |
michael@0 | 2296 | } |
michael@0 | 2297 | |
michael@0 | 2298 | // Schedule timer. |
michael@0 | 2299 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mIdleThreadTimer->InitWithFuncCallback( |
michael@0 | 2300 | ShutdownIdleThreads, nullptr, |
michael@0 | 2301 | IDLE_THREAD_TIMEOUT_SEC * 1000, |
michael@0 | 2302 | nsITimer::TYPE_ONE_SHOT))); |
michael@0 | 2303 | } |
michael@0 | 2304 | |
michael@0 | 2305 | void |
michael@0 | 2306 | RuntimeService::UpdateAllWorkerRuntimeAndContextOptions() |
michael@0 | 2307 | { |
michael@0 | 2308 | BROADCAST_ALL_WORKERS(UpdateRuntimeAndContextOptions, |
michael@0 | 2309 | sDefaultJSSettings.runtimeOptions, |
michael@0 | 2310 | sDefaultJSSettings.content.contextOptions, |
michael@0 | 2311 | sDefaultJSSettings.chrome.contextOptions); |
michael@0 | 2312 | } |
michael@0 | 2313 | |
michael@0 | 2314 | void |
michael@0 | 2315 | RuntimeService::UpdateAppNameOverridePreference(const nsAString& aValue) |
michael@0 | 2316 | { |
michael@0 | 2317 | AssertIsOnMainThread(); |
michael@0 | 2318 | mNavigatorProperties.mAppNameOverridden = aValue; |
michael@0 | 2319 | } |
michael@0 | 2320 | |
michael@0 | 2321 | void |
michael@0 | 2322 | RuntimeService::UpdateAppVersionOverridePreference(const nsAString& aValue) |
michael@0 | 2323 | { |
michael@0 | 2324 | AssertIsOnMainThread(); |
michael@0 | 2325 | mNavigatorProperties.mAppVersionOverridden = aValue; |
michael@0 | 2326 | } |
michael@0 | 2327 | |
michael@0 | 2328 | void |
michael@0 | 2329 | RuntimeService::UpdatePlatformOverridePreference(const nsAString& aValue) |
michael@0 | 2330 | { |
michael@0 | 2331 | AssertIsOnMainThread(); |
michael@0 | 2332 | mNavigatorProperties.mPlatformOverridden = aValue; |
michael@0 | 2333 | } |
michael@0 | 2334 | |
michael@0 | 2335 | void |
michael@0 | 2336 | RuntimeService::UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue) |
michael@0 | 2337 | { |
michael@0 | 2338 | BROADCAST_ALL_WORKERS(UpdatePreference, aPref, aValue); |
michael@0 | 2339 | } |
michael@0 | 2340 | |
michael@0 | 2341 | void |
michael@0 | 2342 | RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey, |
michael@0 | 2343 | uint32_t aValue) |
michael@0 | 2344 | { |
michael@0 | 2345 | BROADCAST_ALL_WORKERS(UpdateJSWorkerMemoryParameter, aKey, aValue); |
michael@0 | 2346 | } |
michael@0 | 2347 | |
michael@0 | 2348 | #ifdef JS_GC_ZEAL |
michael@0 | 2349 | void |
michael@0 | 2350 | RuntimeService::UpdateAllWorkerGCZeal() |
michael@0 | 2351 | { |
michael@0 | 2352 | BROADCAST_ALL_WORKERS(UpdateGCZeal, sDefaultJSSettings.gcZeal, |
michael@0 | 2353 | sDefaultJSSettings.gcZealFrequency); |
michael@0 | 2354 | } |
michael@0 | 2355 | #endif |
michael@0 | 2356 | |
michael@0 | 2357 | void |
michael@0 | 2358 | RuntimeService::GarbageCollectAllWorkers(bool aShrinking) |
michael@0 | 2359 | { |
michael@0 | 2360 | BROADCAST_ALL_WORKERS(GarbageCollect, aShrinking); |
michael@0 | 2361 | } |
michael@0 | 2362 | |
michael@0 | 2363 | void |
michael@0 | 2364 | RuntimeService::CycleCollectAllWorkers() |
michael@0 | 2365 | { |
michael@0 | 2366 | BROADCAST_ALL_WORKERS(CycleCollect, /* dummy = */ false); |
michael@0 | 2367 | } |
michael@0 | 2368 | |
michael@0 | 2369 | void |
michael@0 | 2370 | RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline) |
michael@0 | 2371 | { |
michael@0 | 2372 | BROADCAST_ALL_WORKERS(OfflineStatusChangeEvent, aIsOffline); |
michael@0 | 2373 | } |
michael@0 | 2374 | |
michael@0 | 2375 | // nsISupports |
michael@0 | 2376 | NS_IMPL_ISUPPORTS(RuntimeService, nsIObserver) |
michael@0 | 2377 | |
michael@0 | 2378 | // nsIObserver |
michael@0 | 2379 | NS_IMETHODIMP |
michael@0 | 2380 | RuntimeService::Observe(nsISupports* aSubject, const char* aTopic, |
michael@0 | 2381 | const char16_t* aData) |
michael@0 | 2382 | { |
michael@0 | 2383 | AssertIsOnMainThread(); |
michael@0 | 2384 | |
michael@0 | 2385 | if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { |
michael@0 | 2386 | Shutdown(); |
michael@0 | 2387 | return NS_OK; |
michael@0 | 2388 | } |
michael@0 | 2389 | if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) { |
michael@0 | 2390 | Cleanup(); |
michael@0 | 2391 | return NS_OK; |
michael@0 | 2392 | } |
michael@0 | 2393 | if (!strcmp(aTopic, GC_REQUEST_OBSERVER_TOPIC)) { |
michael@0 | 2394 | GarbageCollectAllWorkers(/* shrinking = */ false); |
michael@0 | 2395 | return NS_OK; |
michael@0 | 2396 | } |
michael@0 | 2397 | if (!strcmp(aTopic, CC_REQUEST_OBSERVER_TOPIC)) { |
michael@0 | 2398 | CycleCollectAllWorkers(); |
michael@0 | 2399 | return NS_OK; |
michael@0 | 2400 | } |
michael@0 | 2401 | if (!strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) { |
michael@0 | 2402 | GarbageCollectAllWorkers(/* shrinking = */ true); |
michael@0 | 2403 | CycleCollectAllWorkers(); |
michael@0 | 2404 | return NS_OK; |
michael@0 | 2405 | } |
michael@0 | 2406 | if (!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) { |
michael@0 | 2407 | SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline()); |
michael@0 | 2408 | return NS_OK; |
michael@0 | 2409 | } |
michael@0 | 2410 | |
michael@0 | 2411 | NS_NOTREACHED("Unknown observer topic!"); |
michael@0 | 2412 | return NS_OK; |
michael@0 | 2413 | } |
michael@0 | 2414 | |
michael@0 | 2415 | /* static */ void |
michael@0 | 2416 | RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure) |
michael@0 | 2417 | { |
michael@0 | 2418 | AssertIsOnMainThread(); |
michael@0 | 2419 | |
michael@0 | 2420 | uintptr_t tmp = reinterpret_cast<uintptr_t>(aClosure); |
michael@0 | 2421 | MOZ_ASSERT(tmp < WORKERPREF_COUNT); |
michael@0 | 2422 | WorkerPreference key = static_cast<WorkerPreference>(tmp); |
michael@0 | 2423 | |
michael@0 | 2424 | #ifdef DUMP_CONTROLLED_BY_PREF |
michael@0 | 2425 | if (key == WORKERPREF_DUMP) { |
michael@0 | 2426 | key = WORKERPREF_DUMP; |
michael@0 | 2427 | sDefaultPreferences[WORKERPREF_DUMP] = |
michael@0 | 2428 | Preferences::GetBool(PREF_DOM_WINDOW_DUMP_ENABLED, false); |
michael@0 | 2429 | } |
michael@0 | 2430 | #endif |
michael@0 | 2431 | |
michael@0 | 2432 | // This function should never be registered as a callback for a preference it |
michael@0 | 2433 | // does not handle. |
michael@0 | 2434 | MOZ_ASSERT(key != WORKERPREF_COUNT); |
michael@0 | 2435 | |
michael@0 | 2436 | RuntimeService* rts = RuntimeService::GetService(); |
michael@0 | 2437 | if (rts) { |
michael@0 | 2438 | rts->UpdateAllWorkerPreference(key, sDefaultPreferences[key]); |
michael@0 | 2439 | } |
michael@0 | 2440 | } |
michael@0 | 2441 | |
michael@0 | 2442 | void |
michael@0 | 2443 | RuntimeService::JSVersionChanged(const char* /* aPrefName */, void* /* aClosure */) |
michael@0 | 2444 | { |
michael@0 | 2445 | AssertIsOnMainThread(); |
michael@0 | 2446 | |
michael@0 | 2447 | bool useLatest = Preferences::GetBool(PREF_WORKERS_LATEST_JS_VERSION, false); |
michael@0 | 2448 | JS::CompartmentOptions& options = sDefaultJSSettings.content.compartmentOptions; |
michael@0 | 2449 | options.setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT); |
michael@0 | 2450 | } |
michael@0 | 2451 | |
michael@0 | 2452 | // static |
michael@0 | 2453 | already_AddRefed<RuntimeService::WorkerThread> |
michael@0 | 2454 | RuntimeService::WorkerThread::Create() |
michael@0 | 2455 | { |
michael@0 | 2456 | MOZ_ASSERT(nsThreadManager::get()); |
michael@0 | 2457 | |
michael@0 | 2458 | nsRefPtr<WorkerThread> thread = new WorkerThread(); |
michael@0 | 2459 | if (NS_FAILED(thread->Init())) { |
michael@0 | 2460 | NS_WARNING("Failed to create new thread!"); |
michael@0 | 2461 | return nullptr; |
michael@0 | 2462 | } |
michael@0 | 2463 | |
michael@0 | 2464 | NS_SetThreadName(thread, "DOM Worker"); |
michael@0 | 2465 | |
michael@0 | 2466 | return thread.forget(); |
michael@0 | 2467 | } |
michael@0 | 2468 | |
michael@0 | 2469 | void |
michael@0 | 2470 | RuntimeService::WorkerThread::SetWorker(WorkerPrivate* aWorkerPrivate) |
michael@0 | 2471 | { |
michael@0 | 2472 | MOZ_ASSERT(PR_GetCurrentThread() == mThread); |
michael@0 | 2473 | MOZ_ASSERT_IF(aWorkerPrivate, !mWorkerPrivate); |
michael@0 | 2474 | MOZ_ASSERT_IF(!aWorkerPrivate, mWorkerPrivate); |
michael@0 | 2475 | |
michael@0 | 2476 | // No need to lock here because mWorkerPrivate is only modified on mThread. |
michael@0 | 2477 | |
michael@0 | 2478 | if (mWorkerPrivate) { |
michael@0 | 2479 | MOZ_ASSERT(mObserver); |
michael@0 | 2480 | |
michael@0 | 2481 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(RemoveObserver(mObserver))); |
michael@0 | 2482 | |
michael@0 | 2483 | mObserver = nullptr; |
michael@0 | 2484 | mWorkerPrivate->SetThread(nullptr); |
michael@0 | 2485 | } |
michael@0 | 2486 | |
michael@0 | 2487 | mWorkerPrivate = aWorkerPrivate; |
michael@0 | 2488 | |
michael@0 | 2489 | if (mWorkerPrivate) { |
michael@0 | 2490 | mWorkerPrivate->SetThread(this); |
michael@0 | 2491 | |
michael@0 | 2492 | nsRefPtr<Observer> observer = new Observer(mWorkerPrivate); |
michael@0 | 2493 | |
michael@0 | 2494 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(AddObserver(observer))); |
michael@0 | 2495 | |
michael@0 | 2496 | mObserver.swap(observer); |
michael@0 | 2497 | } |
michael@0 | 2498 | } |
michael@0 | 2499 | |
michael@0 | 2500 | NS_IMPL_ISUPPORTS_INHERITED0(RuntimeService::WorkerThread, nsThread) |
michael@0 | 2501 | |
michael@0 | 2502 | NS_IMETHODIMP |
michael@0 | 2503 | RuntimeService::WorkerThread::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) |
michael@0 | 2504 | { |
michael@0 | 2505 | // May be called on any thread! |
michael@0 | 2506 | |
michael@0 | 2507 | #ifdef DEBUG |
michael@0 | 2508 | if (PR_GetCurrentThread() == mThread) { |
michael@0 | 2509 | MOZ_ASSERT(mWorkerPrivate); |
michael@0 | 2510 | mWorkerPrivate->AssertIsOnWorkerThread(); |
michael@0 | 2511 | } |
michael@0 | 2512 | else if (aRunnable && !IsAcceptingNonWorkerRunnables()) { |
michael@0 | 2513 | // Only enforce cancelable runnables after we've started the worker loop. |
michael@0 | 2514 | nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable); |
michael@0 | 2515 | MOZ_ASSERT(cancelable, |
michael@0 | 2516 | "Should have been wrapped by the worker's event target!"); |
michael@0 | 2517 | } |
michael@0 | 2518 | #endif |
michael@0 | 2519 | |
michael@0 | 2520 | // Workers only support asynchronous dispatch for now. |
michael@0 | 2521 | if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { |
michael@0 | 2522 | return NS_ERROR_UNEXPECTED; |
michael@0 | 2523 | } |
michael@0 | 2524 | |
michael@0 | 2525 | nsIRunnable* runnableToDispatch; |
michael@0 | 2526 | nsRefPtr<WorkerRunnable> workerRunnable; |
michael@0 | 2527 | |
michael@0 | 2528 | if (aRunnable && PR_GetCurrentThread() == mThread) { |
michael@0 | 2529 | // No need to lock here because mWorkerPrivate is only modified on mThread. |
michael@0 | 2530 | workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable); |
michael@0 | 2531 | runnableToDispatch = workerRunnable; |
michael@0 | 2532 | } |
michael@0 | 2533 | else { |
michael@0 | 2534 | runnableToDispatch = aRunnable; |
michael@0 | 2535 | } |
michael@0 | 2536 | |
michael@0 | 2537 | nsresult rv = nsThread::Dispatch(runnableToDispatch, NS_DISPATCH_NORMAL); |
michael@0 | 2538 | if (NS_WARN_IF(NS_FAILED(rv))) { |
michael@0 | 2539 | return rv; |
michael@0 | 2540 | } |
michael@0 | 2541 | |
michael@0 | 2542 | return NS_OK; |
michael@0 | 2543 | } |
michael@0 | 2544 | |
michael@0 | 2545 | NS_IMPL_ISUPPORTS(RuntimeService::WorkerThread::Observer, nsIThreadObserver) |
michael@0 | 2546 | |
michael@0 | 2547 | NS_IMETHODIMP |
michael@0 | 2548 | RuntimeService::WorkerThread::Observer::OnDispatchedEvent( |
michael@0 | 2549 | nsIThreadInternal* /*aThread */) |
michael@0 | 2550 | { |
michael@0 | 2551 | MOZ_ASSUME_UNREACHABLE("This should never be called!"); |
michael@0 | 2552 | } |
michael@0 | 2553 | |
michael@0 | 2554 | NS_IMETHODIMP |
michael@0 | 2555 | RuntimeService::WorkerThread::Observer::OnProcessNextEvent( |
michael@0 | 2556 | nsIThreadInternal* /* aThread */, |
michael@0 | 2557 | bool aMayWait, |
michael@0 | 2558 | uint32_t aRecursionDepth) |
michael@0 | 2559 | { |
michael@0 | 2560 | mWorkerPrivate->AssertIsOnWorkerThread(); |
michael@0 | 2561 | MOZ_ASSERT(!aMayWait); |
michael@0 | 2562 | |
michael@0 | 2563 | mWorkerPrivate->OnProcessNextEvent(aRecursionDepth); |
michael@0 | 2564 | return NS_OK; |
michael@0 | 2565 | } |
michael@0 | 2566 | |
michael@0 | 2567 | NS_IMETHODIMP |
michael@0 | 2568 | RuntimeService::WorkerThread::Observer::AfterProcessNextEvent( |
michael@0 | 2569 | nsIThreadInternal* /* aThread */, |
michael@0 | 2570 | uint32_t aRecursionDepth, |
michael@0 | 2571 | bool /* aEventWasProcessed */) |
michael@0 | 2572 | { |
michael@0 | 2573 | mWorkerPrivate->AssertIsOnWorkerThread(); |
michael@0 | 2574 | |
michael@0 | 2575 | mWorkerPrivate->AfterProcessNextEvent(aRecursionDepth); |
michael@0 | 2576 | return NS_OK; |
michael@0 | 2577 | } |
michael@0 | 2578 | |
michael@0 | 2579 | NS_IMPL_ISUPPORTS_INHERITED0(LogViolationDetailsRunnable, nsRunnable) |
michael@0 | 2580 | |
michael@0 | 2581 | NS_IMETHODIMP |
michael@0 | 2582 | LogViolationDetailsRunnable::Run() |
michael@0 | 2583 | { |
michael@0 | 2584 | AssertIsOnMainThread(); |
michael@0 | 2585 | |
michael@0 | 2586 | nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP(); |
michael@0 | 2587 | if (csp) { |
michael@0 | 2588 | NS_NAMED_LITERAL_STRING(scriptSample, |
michael@0 | 2589 | "Call to eval() or related function blocked by CSP."); |
michael@0 | 2590 | if (mWorkerPrivate->GetReportCSPViolations()) { |
michael@0 | 2591 | csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL, |
michael@0 | 2592 | mFileName, scriptSample, mLineNum, |
michael@0 | 2593 | EmptyString(), EmptyString()); |
michael@0 | 2594 | } |
michael@0 | 2595 | } |
michael@0 | 2596 | |
michael@0 | 2597 | nsRefPtr<MainThreadStopSyncLoopRunnable> response = |
michael@0 | 2598 | new MainThreadStopSyncLoopRunnable(mWorkerPrivate, mSyncLoopTarget.forget(), |
michael@0 | 2599 | true); |
michael@0 | 2600 | MOZ_ALWAYS_TRUE(response->Dispatch(nullptr)); |
michael@0 | 2601 | |
michael@0 | 2602 | return NS_OK; |
michael@0 | 2603 | } |
michael@0 | 2604 | |
michael@0 | 2605 | NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable, nsRunnable) |
michael@0 | 2606 | |
michael@0 | 2607 | NS_IMETHODIMP |
michael@0 | 2608 | WorkerThreadPrimaryRunnable::Run() |
michael@0 | 2609 | { |
michael@0 | 2610 | #ifdef MOZ_NUWA_PROCESS |
michael@0 | 2611 | if (IsNuwaProcess()) { |
michael@0 | 2612 | NS_ASSERTION(NuwaMarkCurrentThread != nullptr, |
michael@0 | 2613 | "NuwaMarkCurrentThread is undefined!"); |
michael@0 | 2614 | NuwaMarkCurrentThread(nullptr, nullptr); |
michael@0 | 2615 | NuwaFreezeCurrentThread(); |
michael@0 | 2616 | } |
michael@0 | 2617 | #endif |
michael@0 | 2618 | |
michael@0 | 2619 | char stackBaseGuess; |
michael@0 | 2620 | |
michael@0 | 2621 | nsAutoCString threadName; |
michael@0 | 2622 | threadName.AssignLiteral("WebWorker '"); |
michael@0 | 2623 | threadName.Append(NS_LossyConvertUTF16toASCII(mWorkerPrivate->ScriptURL())); |
michael@0 | 2624 | threadName.Append('\''); |
michael@0 | 2625 | |
michael@0 | 2626 | profiler_register_thread(threadName.get(), &stackBaseGuess); |
michael@0 | 2627 | |
michael@0 | 2628 | mThread->SetWorker(mWorkerPrivate); |
michael@0 | 2629 | |
michael@0 | 2630 | mWorkerPrivate->AssertIsOnWorkerThread(); |
michael@0 | 2631 | |
michael@0 | 2632 | { |
michael@0 | 2633 | nsCycleCollector_startup(); |
michael@0 | 2634 | |
michael@0 | 2635 | WorkerJSRuntime runtime(mParentRuntime, mWorkerPrivate); |
michael@0 | 2636 | JSRuntime* rt = runtime.Runtime(); |
michael@0 | 2637 | |
michael@0 | 2638 | JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt); |
michael@0 | 2639 | if (!cx) { |
michael@0 | 2640 | // XXX need to fire an error at parent. |
michael@0 | 2641 | NS_ERROR("Failed to create runtime and context!"); |
michael@0 | 2642 | return NS_ERROR_FAILURE; |
michael@0 | 2643 | } |
michael@0 | 2644 | |
michael@0 | 2645 | { |
michael@0 | 2646 | #ifdef MOZ_ENABLE_PROFILER_SPS |
michael@0 | 2647 | PseudoStack* stack = mozilla_get_pseudo_stack(); |
michael@0 | 2648 | if (stack) { |
michael@0 | 2649 | stack->sampleRuntime(rt); |
michael@0 | 2650 | } |
michael@0 | 2651 | #endif |
michael@0 | 2652 | |
michael@0 | 2653 | { |
michael@0 | 2654 | JSAutoRequest ar(cx); |
michael@0 | 2655 | |
michael@0 | 2656 | mWorkerPrivate->DoRunLoop(cx); |
michael@0 | 2657 | |
michael@0 | 2658 | JS_ReportPendingException(cx); |
michael@0 | 2659 | } |
michael@0 | 2660 | |
michael@0 | 2661 | #ifdef MOZ_ENABLE_PROFILER_SPS |
michael@0 | 2662 | if (stack) { |
michael@0 | 2663 | stack->sampleRuntime(nullptr); |
michael@0 | 2664 | } |
michael@0 | 2665 | #endif |
michael@0 | 2666 | } |
michael@0 | 2667 | |
michael@0 | 2668 | // Destroy the main context. This will unroot the main worker global and |
michael@0 | 2669 | // GC. This is not the last JSContext (WorkerJSRuntime maintains an |
michael@0 | 2670 | // internal JSContext). |
michael@0 | 2671 | JS_DestroyContext(cx); |
michael@0 | 2672 | |
michael@0 | 2673 | // Now WorkerJSRuntime goes out of scope and its destructor will shut |
michael@0 | 2674 | // down the cycle collector and destroy the final JSContext. This |
michael@0 | 2675 | // breaks any remaining cycles and collects the C++ and JS objects |
michael@0 | 2676 | // participating. |
michael@0 | 2677 | } |
michael@0 | 2678 | |
michael@0 | 2679 | mThread->SetWorker(nullptr); |
michael@0 | 2680 | |
michael@0 | 2681 | mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan); |
michael@0 | 2682 | |
michael@0 | 2683 | // It is no longer safe to touch mWorkerPrivate. |
michael@0 | 2684 | mWorkerPrivate = nullptr; |
michael@0 | 2685 | |
michael@0 | 2686 | // Now recycle this thread. |
michael@0 | 2687 | nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); |
michael@0 | 2688 | MOZ_ASSERT(mainThread); |
michael@0 | 2689 | |
michael@0 | 2690 | nsRefPtr<FinishedRunnable> finishedRunnable = |
michael@0 | 2691 | new FinishedRunnable(mThread.forget()); |
michael@0 | 2692 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mainThread->Dispatch(finishedRunnable, |
michael@0 | 2693 | NS_DISPATCH_NORMAL))); |
michael@0 | 2694 | |
michael@0 | 2695 | profiler_unregister_thread(); |
michael@0 | 2696 | return NS_OK; |
michael@0 | 2697 | } |
michael@0 | 2698 | |
michael@0 | 2699 | NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable::FinishedRunnable, |
michael@0 | 2700 | nsRunnable) |
michael@0 | 2701 | |
michael@0 | 2702 | NS_IMETHODIMP |
michael@0 | 2703 | WorkerThreadPrimaryRunnable::FinishedRunnable::Run() |
michael@0 | 2704 | { |
michael@0 | 2705 | AssertIsOnMainThread(); |
michael@0 | 2706 | |
michael@0 | 2707 | nsRefPtr<RuntimeService::WorkerThread> thread; |
michael@0 | 2708 | mThread.swap(thread); |
michael@0 | 2709 | |
michael@0 | 2710 | RuntimeService* rts = RuntimeService::GetService(); |
michael@0 | 2711 | if (rts) { |
michael@0 | 2712 | rts->NoteIdleThread(thread); |
michael@0 | 2713 | } |
michael@0 | 2714 | else if (thread->ShutdownRequired()) { |
michael@0 | 2715 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Shutdown())); |
michael@0 | 2716 | } |
michael@0 | 2717 | |
michael@0 | 2718 | return NS_OK; |
michael@0 | 2719 | } |