Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "RuntimeService.h"
8
9
#include "nsContentSecurityUtils.h"
10
#include "nsIContentSecurityPolicy.h"
11
#include "mozilla/dom/Document.h"
12
#include "nsIObserverService.h"
13
#include "nsIScriptContext.h"
14
#include "nsIStreamTransportService.h"
15
#include "nsISupportsPriority.h"
16
#include "nsITimer.h"
17
#include "nsIURI.h"
18
#include "nsIXULRuntime.h"
19
#include "nsPIDOMWindow.h"
20
21
#include <algorithm>
22
#include "mozilla/ipc/BackgroundChild.h"
23
#include "GeckoProfiler.h"
24
#include "jsfriendapi.h"
25
#include "js/ContextOptions.h"
26
#include "js/LocaleSensitive.h"
27
#include "mozilla/AbstractThread.h"
28
#include "mozilla/AntiTrackingCommon.h"
29
#include "mozilla/ArrayUtils.h"
30
#include "mozilla/Atomics.h"
31
#include "mozilla/Attributes.h"
32
#include "mozilla/CycleCollectedJSContext.h"
33
#include "mozilla/CycleCollectedJSRuntime.h"
34
#include "mozilla/Telemetry.h"
35
#include "mozilla/TimeStamp.h"
36
#include "mozilla/dom/AtomList.h"
37
#include "mozilla/dom/BindingUtils.h"
38
#include "mozilla/dom/ErrorEventBinding.h"
39
#include "mozilla/dom/EventTargetBinding.h"
40
#include "mozilla/dom/FetchUtil.h"
41
#include "mozilla/dom/MessageChannel.h"
42
#include "mozilla/dom/MessageEventBinding.h"
43
#include "mozilla/dom/PerformanceService.h"
44
#include "mozilla/dom/RemoteWorkerChild.h"
45
#include "mozilla/dom/WorkerBinding.h"
46
#include "mozilla/dom/ScriptSettings.h"
47
#include "mozilla/dom/IndexedDatabaseManager.h"
48
#include "mozilla/ipc/BackgroundChild.h"
49
#include "mozilla/DebugOnly.h"
50
#include "mozilla/Preferences.h"
51
#include "mozilla/dom/Navigator.h"
52
#include "mozilla/Monitor.h"
53
#include "nsContentUtils.h"
54
#include "nsCycleCollector.h"
55
#include "nsDOMJSUtils.h"
56
#include "nsISupportsImpl.h"
57
#include "nsLayoutStatics.h"
58
#include "nsNetUtil.h"
59
#include "nsServiceManagerUtils.h"
60
#include "nsThreadUtils.h"
61
#include "nsXPCOM.h"
62
#include "nsXPCOMPrivate.h"
63
#include "OSFileConstants.h"
64
#include "xpcpublic.h"
65
66
#if defined(XP_MACOSX)
67
# include "nsMacUtilsImpl.h"
68
#endif
69
70
#include "Principal.h"
71
#include "WorkerDebuggerManager.h"
72
#include "WorkerError.h"
73
#include "WorkerLoadInfo.h"
74
#include "WorkerPrivate.h"
75
#include "WorkerRunnable.h"
76
#include "WorkerScope.h"
77
#include "WorkerThread.h"
78
#include "prsystem.h"
79
80
#define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
81
82
namespace mozilla {
83
84
using namespace ipc;
85
86
namespace dom {
87
88
using namespace workerinternals;
89
90
namespace workerinternals {
91
92
// The size of the worker runtime heaps in bytes. May be changed via pref.
93
#define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
94
95
// The size of the worker JS allocation threshold in MB. May be changed via
96
// pref.
97
#define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30
98
99
// Half the size of the actual C stack, to be safe.
100
#define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
101
102
// The maximum number of hardware concurrency, overridable via pref.
103
#define MAX_HARDWARE_CONCURRENCY 8
104
105
// The maximum number of threads to use for workers, overridable via pref.
106
#define MAX_WORKERS_PER_DOMAIN 512
107
108
static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
109
"We should allow at least one worker per domain.");
110
111
// The default number of seconds that close handlers will be allowed to run for
112
// content workers.
113
#define MAX_SCRIPT_RUN_TIME_SEC 10
114
115
// The number of seconds that idle threads can hang around before being killed.
116
#define IDLE_THREAD_TIMEOUT_SEC 30
117
118
// The maximum number of threads that can be idle at one time.
119
#define MAX_IDLE_THREADS 20
120
121
#define PREF_WORKERS_PREFIX "dom.workers."
122
#define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
123
#define PREF_WORKERS_MAX_HARDWARE_CONCURRENCY "dom.maxHardwareConcurrency"
124
125
#define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
126
#define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
127
128
#define GC_REQUEST_OBSERVER_TOPIC "child-gc-request"
129
#define CC_REQUEST_OBSERVER_TOPIC "child-cc-request"
130
#define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
131
#define LOW_MEMORY_DATA "low-memory"
132
#define LOW_MEMORY_ONGOING_DATA "low-memory-ongoing"
133
#define MEMORY_PRESSURE_STOP_OBSERVER_TOPIC "memory-pressure-stop"
134
135
#define BROADCAST_ALL_WORKERS(_func, ...) \
136
PR_BEGIN_MACRO \
137
AssertIsOnMainThread(); \
138
\
139
AutoTArray<WorkerPrivate*, 100> workers; \
140
{ \
141
MutexAutoLock lock(mMutex); \
142
\
143
AddAllTopLevelWorkersToArray(workers); \
144
} \
145
\
146
if (!workers.IsEmpty()) { \
147
for (uint32_t index = 0; index < workers.Length(); index++) { \
148
workers[index]->_func(__VA_ARGS__); \
149
} \
150
} \
151
PR_END_MACRO
152
153
// Prefixes for observing preference changes.
154
#define PREF_JS_OPTIONS_PREFIX "javascript.options."
155
#define PREF_WORKERS_OPTIONS_PREFIX PREF_WORKERS_PREFIX "options."
156
#define PREF_MEM_OPTIONS_PREFIX "mem."
157
#define PREF_GCZEAL "gcZeal"
158
159
static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
160
161
namespace {
162
163
const uint32_t kNoIndex = uint32_t(-1);
164
165
uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN;
166
uint32_t gMaxHardwareConcurrency = MAX_HARDWARE_CONCURRENCY;
167
168
// Does not hold an owning reference.
169
RuntimeService* gRuntimeService = nullptr;
170
171
// Only true during the call to Init.
172
bool gRuntimeServiceDuringInit = false;
173
174
class LiteralRebindingCString : public nsDependentCString {
175
public:
176
template <int N>
177
void RebindLiteral(const char (&aStr)[N]) {
178
Rebind(aStr, N - 1);
179
}
180
};
181
182
template <typename T>
183
struct PrefTraits;
184
185
template <>
186
struct PrefTraits<bool> {
187
typedef bool PrefValueType;
188
189
static const PrefValueType kDefaultValue = false;
190
191
static inline PrefValueType Get(const char* aPref) {
192
AssertIsOnMainThread();
193
return Preferences::GetBool(aPref);
194
}
195
196
static inline bool Exists(const char* aPref) {
197
AssertIsOnMainThread();
198
return Preferences::GetType(aPref) == nsIPrefBranch::PREF_BOOL;
199
}
200
};
201
202
template <>
203
struct PrefTraits<int32_t> {
204
typedef int32_t PrefValueType;
205
206
static inline PrefValueType Get(const char* aPref) {
207
AssertIsOnMainThread();
208
return Preferences::GetInt(aPref);
209
}
210
211
static inline bool Exists(const char* aPref) {
212
AssertIsOnMainThread();
213
return Preferences::GetType(aPref) == nsIPrefBranch::PREF_INT;
214
}
215
};
216
217
template <typename T>
218
T GetWorkerPref(const nsACString& aPref,
219
const T aDefault = PrefTraits<T>::kDefaultValue) {
220
AssertIsOnMainThread();
221
222
typedef PrefTraits<T> PrefHelper;
223
224
T result;
225
226
nsAutoCString prefName;
227
prefName.AssignLiteral(PREF_WORKERS_OPTIONS_PREFIX);
228
prefName.Append(aPref);
229
230
if (PrefHelper::Exists(prefName.get())) {
231
result = PrefHelper::Get(prefName.get());
232
} else {
233
prefName.AssignLiteral(PREF_JS_OPTIONS_PREFIX);
234
prefName.Append(aPref);
235
236
if (PrefHelper::Exists(prefName.get())) {
237
result = PrefHelper::Get(prefName.get());
238
} else {
239
result = aDefault;
240
}
241
}
242
243
return result;
244
}
245
246
void LoadContextOptions(const char* aPrefName, void* /* aClosure */) {
247
AssertIsOnMainThread();
248
249
RuntimeService* rts = RuntimeService::GetService();
250
if (!rts) {
251
// May be shutting down, just bail.
252
return;
253
}
254
255
const nsDependentCString prefName(aPrefName);
256
257
// Several other pref branches will get included here so bail out if there is
258
// another callback that will handle this change.
259
if (StringBeginsWith(
260
prefName,
261
NS_LITERAL_CSTRING(PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
262
StringBeginsWith(
263
prefName, NS_LITERAL_CSTRING(
264
PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
265
return;
266
}
267
268
#ifdef JS_GC_ZEAL
269
if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) ||
270
prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) {
271
return;
272
}
273
#endif
274
275
// Context options.
276
JS::ContextOptions contextOptions;
277
contextOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
278
.setWasm(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm")))
279
.setWasmForTrustedPrinciples(
280
GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_trustedprincipals")))
281
.setWasmBaseline(
282
GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_baselinejit")))
283
.setWasmIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_ionjit")))
284
#ifdef ENABLE_WASM_CRANELIFT
285
.setWasmCranelift(
286
GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_cranelift")))
287
#endif
288
#ifdef ENABLE_WASM_REFTYPES
289
.setWasmGc(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_gc")))
290
#endif
291
.setWasmVerbose(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_verbose")))
292
.setThrowOnAsmJSValidationFailure(GetWorkerPref<bool>(
293
NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure")))
294
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
295
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
296
#ifdef FUZZING
297
.setFuzzing(GetWorkerPref<bool>(NS_LITERAL_CSTRING("fuzzing.enabled")))
298
#endif
299
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")));
300
301
nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
302
if (xr) {
303
bool safeMode = false;
304
xr->GetInSafeMode(&safeMode);
305
if (safeMode) {
306
contextOptions.disableOptionsForSafeMode();
307
}
308
}
309
310
RuntimeService::SetDefaultContextOptions(contextOptions);
311
312
if (rts) {
313
rts->UpdateAllWorkerContextOptions();
314
}
315
}
316
317
#ifdef JS_GC_ZEAL
318
void LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) {
319
AssertIsOnMainThread();
320
321
RuntimeService* rts = RuntimeService::GetService();
322
if (!rts) {
323
// May be shutting down, just bail.
324
return;
325
}
326
327
int32_t gczeal = GetWorkerPref<int32_t>(NS_LITERAL_CSTRING(PREF_GCZEAL), -1);
328
if (gczeal < 0) {
329
gczeal = 0;
330
}
331
332
int32_t frequency =
333
GetWorkerPref<int32_t>(NS_LITERAL_CSTRING("gcZeal.frequency"), -1);
334
if (frequency < 0) {
335
frequency = JS_DEFAULT_ZEAL_FREQ;
336
}
337
338
RuntimeService::SetDefaultGCZeal(uint8_t(gczeal), uint32_t(frequency));
339
340
if (rts) {
341
rts->UpdateAllWorkerGCZeal();
342
}
343
}
344
#endif
345
346
void UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService,
347
const nsACString& aPrefName,
348
JSGCParamKey aKey) {
349
AssertIsOnMainThread();
350
NS_ASSERTION(!aPrefName.IsEmpty(), "Empty pref name!");
351
352
int32_t prefValue = GetWorkerPref(aPrefName, -1);
353
uint32_t value =
354
(prefValue < 0 || prefValue >= 10000) ? 0 : uint32_t(prefValue);
355
356
RuntimeService::SetDefaultJSGCSettings(aKey, value);
357
358
if (aRuntimeService) {
359
aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, value);
360
}
361
}
362
363
void UpdateOtherJSGCMemoryOption(RuntimeService* aRuntimeService,
364
JSGCParamKey aKey, uint32_t aValue) {
365
AssertIsOnMainThread();
366
367
RuntimeService::SetDefaultJSGCSettings(aKey, aValue);
368
369
if (aRuntimeService) {
370
aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, aValue);
371
}
372
}
373
374
void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) {
375
AssertIsOnMainThread();
376
377
RuntimeService* rts = RuntimeService::GetService();
378
379
if (!rts) {
380
// May be shutting down, just bail.
381
return;
382
}
383
384
NS_NAMED_LITERAL_CSTRING(jsPrefix, PREF_JS_OPTIONS_PREFIX);
385
NS_NAMED_LITERAL_CSTRING(workersPrefix, PREF_WORKERS_OPTIONS_PREFIX);
386
387
const nsDependentCString fullPrefName(aPrefName);
388
389
// Pull out the string that actually distinguishes the parameter we need to
390
// change.
391
nsDependentCSubstring memPrefName;
392
if (StringBeginsWith(fullPrefName, jsPrefix)) {
393
memPrefName.Rebind(fullPrefName, jsPrefix.Length());
394
} else if (StringBeginsWith(fullPrefName, workersPrefix)) {
395
memPrefName.Rebind(fullPrefName, workersPrefix.Length());
396
} else {
397
NS_ERROR("Unknown pref name!");
398
return;
399
}
400
401
#ifdef DEBUG
402
// During Init() we get called back with a branch string here, so there should
403
// be no just a "mem." pref here.
404
if (!rts) {
405
NS_ASSERTION(memPrefName.EqualsLiteral(PREF_MEM_OPTIONS_PREFIX), "Huh?!");
406
}
407
#endif
408
409
// If we're running in Init() then do this for every pref we care about.
410
// Otherwise we just want to update the parameter that changed.
411
for (uint32_t index = !gRuntimeServiceDuringInit
412
? JSSettings::kGCSettingsArraySize - 1
413
: 0;
414
index < JSSettings::kGCSettingsArraySize; index++) {
415
LiteralRebindingCString matchName;
416
417
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "max");
418
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 0)) {
419
int32_t prefValue = GetWorkerPref(matchName, -1);
420
uint32_t value = (prefValue <= 0 || prefValue >= 0x1000)
421
? uint32_t(-1)
422
: uint32_t(prefValue) * 1024 * 1024;
423
UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value);
424
continue;
425
}
426
427
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
428
"gc_high_frequency_time_limit_ms");
429
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 2)) {
430
UpdateCommonJSGCMemoryOption(rts, matchName,
431
JSGC_HIGH_FREQUENCY_TIME_LIMIT);
432
continue;
433
}
434
435
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
436
"gc_low_frequency_heap_growth");
437
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 3)) {
438
UpdateCommonJSGCMemoryOption(rts, matchName,
439
JSGC_LOW_FREQUENCY_HEAP_GROWTH);
440
continue;
441
}
442
443
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
444
"gc_high_frequency_heap_growth_min");
445
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 4)) {
446
UpdateCommonJSGCMemoryOption(rts, matchName,
447
JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
448
continue;
449
}
450
451
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
452
"gc_high_frequency_heap_growth_max");
453
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 5)) {
454
UpdateCommonJSGCMemoryOption(rts, matchName,
455
JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
456
continue;
457
}
458
459
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
460
"gc_high_frequency_low_limit_mb");
461
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 6)) {
462
UpdateCommonJSGCMemoryOption(rts, matchName,
463
JSGC_HIGH_FREQUENCY_LOW_LIMIT);
464
continue;
465
}
466
467
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
468
"gc_high_frequency_high_limit_mb");
469
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 7)) {
470
UpdateCommonJSGCMemoryOption(rts, matchName,
471
JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
472
continue;
473
}
474
475
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
476
"gc_allocation_threshold_mb");
477
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 8)) {
478
UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD);
479
continue;
480
}
481
482
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms");
483
if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 9)) {
484
int32_t prefValue = GetWorkerPref(matchName, -1);
485
uint32_t value =
486
(prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue);
487
UpdateOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET_MS, value);
488
continue;
489
}
490
491
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth");
492
if (memPrefName == matchName ||
493
(gRuntimeServiceDuringInit && index == 10)) {
494
bool prefValue = GetWorkerPref(matchName, false);
495
UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH,
496
prefValue ? 0 : 1);
497
continue;
498
}
499
500
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice");
501
if (memPrefName == matchName ||
502
(gRuntimeServiceDuringInit && index == 11)) {
503
bool prefValue = GetWorkerPref(matchName, false);
504
UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE,
505
prefValue ? 0 : 1);
506
continue;
507
}
508
509
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_min_empty_chunk_count");
510
if (memPrefName == matchName ||
511
(gRuntimeServiceDuringInit && index == 12)) {
512
UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MIN_EMPTY_CHUNK_COUNT);
513
continue;
514
}
515
516
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_max_empty_chunk_count");
517
if (memPrefName == matchName ||
518
(gRuntimeServiceDuringInit && index == 13)) {
519
UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MAX_EMPTY_CHUNK_COUNT);
520
continue;
521
}
522
523
matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_compacting");
524
if (memPrefName == matchName ||
525
(gRuntimeServiceDuringInit && index == 14)) {
526
bool prefValue = GetWorkerPref(matchName, false);
527
UpdateOtherJSGCMemoryOption(rts, JSGC_COMPACTING_ENABLED,
528
prefValue ? 0 : 1);
529
continue;
530
}
531
532
#ifdef DEBUG
533
nsAutoCString message("Workers don't support the 'mem.");
534
message.Append(memPrefName);
535
message.AppendLiteral("' preference!");
536
NS_WARNING(message.get());
537
#endif
538
}
539
}
540
541
bool InterruptCallback(JSContext* aCx) {
542
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
543
MOZ_ASSERT(worker);
544
545
// As with the main thread, the interrupt callback is triggered
546
// non-deterministically when recording/replaying, so return early to avoid
547
// performing any recorded events.
548
if (recordreplay::IsRecordingOrReplaying()) {
549
return true;
550
}
551
552
// Now is a good time to turn on profiling if it's pending.
553
PROFILER_JS_INTERRUPT_CALLBACK();
554
555
return worker->InterruptCallback(aCx);
556
}
557
558
class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable {
559
nsString mFileName;
560
uint32_t mLineNum;
561
uint32_t mColumnNum;
562
nsString mScriptSample;
563
564
public:
565
LogViolationDetailsRunnable(WorkerPrivate* aWorker, const nsString& aFileName,
566
uint32_t aLineNum, uint32_t aColumnNum,
567
const nsAString& aScriptSample)
568
: WorkerMainThreadRunnable(
569
aWorker,
570
NS_LITERAL_CSTRING("RuntimeService :: LogViolationDetails")),
571
mFileName(aFileName),
572
mLineNum(aLineNum),
573
mColumnNum(aColumnNum),
574
mScriptSample(aScriptSample) {
575
MOZ_ASSERT(aWorker);
576
}
577
578
virtual bool MainThreadRun() override;
579
580
private:
581
~LogViolationDetailsRunnable() {}
582
};
583
584
bool ContentSecurityPolicyAllows(JSContext* aCx, JS::HandleValue aValue) {
585
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
586
worker->AssertIsOnWorkerThread();
587
588
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, aValue));
589
if (NS_WARN_IF(!jsString)) {
590
JS_ClearPendingException(aCx);
591
return false;
592
}
593
594
nsAutoJSString scriptSample;
595
if (NS_WARN_IF(!scriptSample.init(aCx, jsString))) {
596
JS_ClearPendingException(aCx);
597
return false;
598
}
599
600
if (!nsContentSecurityUtils::IsEvalAllowed(aCx, worker->UsesSystemPrincipal(),
601
scriptSample)) {
602
return false;
603
}
604
605
if (worker->GetReportCSPViolations()) {
606
nsString fileName;
607
uint32_t lineNum = 0;
608
uint32_t columnNum = 0;
609
610
JS::AutoFilename file;
611
if (JS::DescribeScriptedCaller(aCx, &file, &lineNum, &columnNum) &&
612
file.get()) {
613
fileName = NS_ConvertUTF8toUTF16(file.get());
614
} else {
615
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
616
}
617
618
RefPtr<LogViolationDetailsRunnable> runnable =
619
new LogViolationDetailsRunnable(worker, fileName, lineNum, columnNum,
620
scriptSample);
621
622
ErrorResult rv;
623
runnable->Dispatch(Killing, rv);
624
if (NS_WARN_IF(rv.Failed())) {
625
rv.SuppressException();
626
}
627
}
628
629
return worker->IsEvalAllowed();
630
}
631
632
void CTypesActivityCallback(JSContext* aCx, js::CTypesActivityType aType) {
633
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
634
worker->AssertIsOnWorkerThread();
635
636
switch (aType) {
637
case js::CTYPES_CALL_BEGIN:
638
worker->BeginCTypesCall();
639
break;
640
641
case js::CTYPES_CALL_END:
642
worker->EndCTypesCall();
643
break;
644
645
case js::CTYPES_CALLBACK_BEGIN:
646
worker->BeginCTypesCallback();
647
break;
648
649
case js::CTYPES_CALLBACK_END:
650
worker->EndCTypesCallback();
651
break;
652
653
default:
654
MOZ_CRASH("Unknown type flag!");
655
}
656
}
657
658
// JSDispatchableRunnables are WorkerRunnables used to dispatch JS::Dispatchable
659
// back to their worker thread. A WorkerRunnable is used for two reasons:
660
//
661
// 1. The JS::Dispatchable::run() callback may run JS so we cannot use a control
662
// runnable since they use async interrupts and break JS run-to-completion.
663
//
664
// 2. The DispatchToEventLoopCallback interface is *required* to fail during
665
// shutdown (see jsapi.h) which is exactly what WorkerRunnable::Dispatch() will
666
// do. Moreover, JS_DestroyContext() does *not* block on JS::Dispatchable::run
667
// being called, DispatchToEventLoopCallback failure is expected to happen
668
// during shutdown.
669
class JSDispatchableRunnable final : public WorkerRunnable {
670
JS::Dispatchable* mDispatchable;
671
672
~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable); }
673
674
// Disable the usual pre/post-dispatch thread assertions since we are
675
// dispatching from some random JS engine internal thread:
676
677
bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
678
679
void PostDispatch(WorkerPrivate* aWorkerPrivate,
680
bool aDispatchResult) override {
681
// For the benefit of the destructor assert.
682
if (!aDispatchResult) {
683
mDispatchable = nullptr;
684
}
685
}
686
687
public:
688
JSDispatchableRunnable(WorkerPrivate* aWorkerPrivate,
689
JS::Dispatchable* aDispatchable)
690
: WorkerRunnable(aWorkerPrivate,
691
WorkerRunnable::WorkerThreadUnchangedBusyCount),
692
mDispatchable(aDispatchable) {
693
MOZ_ASSERT(mDispatchable);
694
}
695
696
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
697
MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
698
MOZ_ASSERT(aCx == mWorkerPrivate->GetJSContext());
699
MOZ_ASSERT(mDispatchable);
700
701
AutoJSAPI jsapi;
702
jsapi.Init();
703
704
mDispatchable->run(mWorkerPrivate->GetJSContext(),
705
JS::Dispatchable::NotShuttingDown);
706
mDispatchable = nullptr; // mDispatchable may delete itself
707
708
return true;
709
}
710
711
nsresult Cancel() override {
712
MOZ_ASSERT(mDispatchable);
713
714
AutoJSAPI jsapi;
715
jsapi.Init();
716
717
mDispatchable->run(mWorkerPrivate->GetJSContext(),
718
JS::Dispatchable::ShuttingDown);
719
mDispatchable = nullptr; // mDispatchable may delete itself
720
721
return WorkerRunnable::Cancel();
722
}
723
};
724
725
static bool DispatchToEventLoop(void* aClosure,
726
JS::Dispatchable* aDispatchable) {
727
// This callback may execute either on the worker thread or a random
728
// JS-internal helper thread.
729
730
// See comment at JS::InitDispatchToEventLoop() below for how we know the
731
// WorkerPrivate is alive.
732
WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
733
734
// Dispatch is expected to fail during shutdown for the reasons outlined in
735
// the JSDispatchableRunnable comment above.
736
RefPtr<JSDispatchableRunnable> r =
737
new JSDispatchableRunnable(workerPrivate, aDispatchable);
738
return r->Dispatch();
739
}
740
741
static bool ConsumeStream(JSContext* aCx, JS::HandleObject aObj,
742
JS::MimeType aMimeType,
743
JS::StreamConsumer* aConsumer) {
744
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
745
if (!worker) {
746
JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
747
JSMSG_ERROR_CONSUMING_RESPONSE);
748
return false;
749
}
750
751
return FetchUtil::StreamResponseToJS(aCx, aObj, aMimeType, aConsumer, worker);
752
}
753
754
bool InitJSContextForWorker(WorkerPrivate* aWorkerPrivate,
755
JSContext* aWorkerCx) {
756
aWorkerPrivate->AssertIsOnWorkerThread();
757
NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
758
759
JSSettings settings;
760
aWorkerPrivate->CopyJSSettings(settings);
761
762
JS::ContextOptionsRef(aWorkerCx) = settings.contextOptions;
763
764
JSSettings::JSGCSettingsArray& gcSettings = settings.gcSettings;
765
766
// This is the real place where we set the max memory for the runtime.
767
for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) {
768
const JSSettings::JSGCSetting& setting = gcSettings[index];
769
if (setting.key.isSome()) {
770
NS_ASSERTION(setting.value, "Can't handle 0 values!");
771
JS_SetGCParameter(aWorkerCx, *setting.key, setting.value);
772
}
773
}
774
775
JS_SetNativeStackQuota(aWorkerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
776
777
// Security policy:
778
static const JSSecurityCallbacks securityCallbacks = {
779
ContentSecurityPolicyAllows};
780
JS_SetSecurityCallbacks(aWorkerCx, &securityCallbacks);
781
782
// A WorkerPrivate lives strictly longer than its JSRuntime so we can safely
783
// store a raw pointer as the callback's closure argument on the JSRuntime.
784
JS::InitDispatchToEventLoop(aWorkerCx, DispatchToEventLoop,
785
(void*)aWorkerPrivate);
786
787
JS::InitConsumeStreamCallback(aWorkerCx, ConsumeStream,
788
FetchUtil::ReportJSStreamError);
789
790
if (!JS::InitSelfHostedCode(aWorkerCx)) {
791
NS_WARNING("Could not init self-hosted code!");
792
return false;
793
}
794
795
JS_AddInterruptCallback(aWorkerCx, InterruptCallback);
796
797
js::SetCTypesActivityCallback(aWorkerCx, CTypesActivityCallback);
798
799
#ifdef JS_GC_ZEAL
800
JS_SetGCZeal(aWorkerCx, settings.gcZeal, settings.gcZealFrequency);
801
#endif
802
803
return true;
804
}
805
806
static bool PreserveWrapper(JSContext* cx, JS::HandleObject obj) {
807
MOZ_ASSERT(cx);
808
MOZ_ASSERT(obj);
809
MOZ_ASSERT(mozilla::dom::IsDOMObject(obj));
810
811
return mozilla::dom::TryPreserveWrapper(obj);
812
}
813
814
static bool IsWorkerDebuggerGlobalOrSandbox(JS::HandleObject aGlobal) {
815
return IsWorkerDebuggerGlobal(aGlobal) || IsWorkerDebuggerSandbox(aGlobal);
816
}
817
818
JSObject* Wrap(JSContext* cx, JS::HandleObject existing, JS::HandleObject obj) {
819
JS::RootedObject targetGlobal(cx, JS::CurrentGlobalOrNull(cx));
820
821
// Note: the JS engine unwraps CCWs before calling this callback.
822
JS::RootedObject originGlobal(cx, JS::GetNonCCWObjectGlobal(obj));
823
824
const js::Wrapper* wrapper = nullptr;
825
if (IsWorkerDebuggerGlobalOrSandbox(targetGlobal) &&
826
IsWorkerDebuggerGlobalOrSandbox(originGlobal)) {
827
wrapper = &js::CrossCompartmentWrapper::singleton;
828
} else {
829
wrapper = &js::OpaqueCrossCompartmentWrapper::singleton;
830
}
831
832
if (existing) {
833
js::Wrapper::Renew(existing, obj, wrapper);
834
}
835
return js::Wrapper::New(cx, obj, wrapper);
836
}
837
838
static const JSWrapObjectCallbacks WrapObjectCallbacks = {
839
Wrap,
840
nullptr,
841
};
842
843
class WorkerJSRuntime final : public mozilla::CycleCollectedJSRuntime {
844
public:
845
// The heap size passed here doesn't matter, we will change it later in the
846
// call to JS_SetGCParameter inside InitJSContextForWorker.
847
explicit WorkerJSRuntime(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
848
: CycleCollectedJSRuntime(aCx), mWorkerPrivate(aWorkerPrivate) {
849
MOZ_COUNT_CTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
850
MOZ_ASSERT(aWorkerPrivate);
851
852
{
853
JS::UniqueChars defaultLocale = aWorkerPrivate->AdoptDefaultLocale();
854
MOZ_ASSERT(defaultLocale,
855
"failure of a WorkerPrivate to have a default locale should "
856
"have made the worker fail to spawn");
857
858
if (!JS_SetDefaultLocale(Runtime(), defaultLocale.get())) {
859
NS_WARNING("failed to set workerCx's default locale");
860
}
861
}
862
}
863
864
void Shutdown(JSContext* cx) override {
865
// The CC is shut down, and the superclass destructor will GC, so make sure
866
// we don't try to CC again.
867
mWorkerPrivate = nullptr;
868
869
CycleCollectedJSRuntime::Shutdown(cx);
870
}
871
872
~WorkerJSRuntime() {
873
MOZ_COUNT_DTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
874
}
875
876
virtual void PrepareForForgetSkippable() override {}
877
878
virtual void BeginCycleCollectionCallback() override {}
879
880
virtual void EndCycleCollectionCallback(
881
CycleCollectorResults& aResults) override {}
882
883
void DispatchDeferredDeletion(bool aContinuation, bool aPurge) override {
884
MOZ_ASSERT(!aContinuation);
885
886
// Do it immediately, no need for asynchronous behavior here.
887
nsCycleCollector_doDeferredDeletion();
888
}
889
890
virtual void CustomGCCallback(JSGCStatus aStatus) override {
891
if (!mWorkerPrivate) {
892
// We're shutting down, no need to do anything.
893
return;
894
}
895
896
mWorkerPrivate->AssertIsOnWorkerThread();
897
898
if (aStatus == JSGC_END) {
899
nsCycleCollector_collect(nullptr);
900
}
901
}
902
903
private:
904
WorkerPrivate* mWorkerPrivate;
905
};
906
907
} // anonymous namespace
908
909
} // namespace workerinternals
910
911
class WorkerJSContext final : public mozilla::CycleCollectedJSContext {
912
public:
913
// The heap size passed here doesn't matter, we will change it later in the
914
// call to JS_SetGCParameter inside InitJSContextForWorker.
915
explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate)
916
: mWorkerPrivate(aWorkerPrivate) {
917
MOZ_COUNT_CTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
918
MOZ_ASSERT(aWorkerPrivate);
919
// Magical number 2. Workers have the base recursion depth 1, and normal
920
// runnables run at level 2, and we don't want to process microtasks
921
// at any other level.
922
SetTargetedMicroTaskRecursionDepth(2);
923
}
924
925
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because otherwise we have to annotate the
926
// SpiderMonkey JS::JobQueue's destructor as MOZ_CAN_RUN_SCRIPT, which is a
927
// bit of a pain.
928
MOZ_CAN_RUN_SCRIPT_BOUNDARY ~WorkerJSContext() {
929
MOZ_COUNT_DTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
930
JSContext* cx = MaybeContext();
931
if (!cx) {
932
return; // Initialize() must have failed
933
}
934
935
// The worker global should be unrooted and the shutdown cycle collection
936
// should break all remaining cycles. The superclass destructor will run
937
// the GC one final time and finalize any JSObjects that were participating
938
// in cycles that were broken during CC shutdown.
939
nsCycleCollector_shutdown();
940
941
// The CC is shut down, and the superclass destructor will GC, so make sure
942
// we don't try to CC again.
943
mWorkerPrivate = nullptr;
944
}
945
946
WorkerJSContext* GetAsWorkerJSContext() override { return this; }
947
948
CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) override {
949
return new WorkerJSRuntime(aCx, mWorkerPrivate);
950
}
951
952
nsresult Initialize(JSRuntime* aParentRuntime) {
953
nsresult rv = CycleCollectedJSContext::Initialize(
954
aParentRuntime, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
955
if (NS_WARN_IF(NS_FAILED(rv))) {
956
return rv;
957
}
958
959
JSContext* cx = Context();
960
961
js::SetPreserveWrapperCallback(cx, PreserveWrapper);
962
JS_InitDestroyPrincipalsCallback(cx, WorkerPrincipal::Destroy);
963
JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
964
if (mWorkerPrivate->IsDedicatedWorker()) {
965
JS_SetFutexCanWait(cx);
966
}
967
968
return NS_OK;
969
}
970
971
virtual void DispatchToMicroTask(
972
already_AddRefed<MicroTaskRunnable> aRunnable) override {
973
RefPtr<MicroTaskRunnable> runnable(aRunnable);
974
975
MOZ_ASSERT(!NS_IsMainThread());
976
MOZ_ASSERT(runnable);
977
978
std::queue<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
979
980
JSContext* cx = GetCurrentWorkerThreadJSContext();
981
NS_ASSERTION(cx, "This should never be null!");
982
983
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
984
NS_ASSERTION(global, "This should never be null!");
985
986
// On worker threads, if the current global is the worker global, we use the
987
// main micro task queue. Otherwise, the current global must be
988
// either the debugger global or a debugger sandbox, and we use the debugger
989
// micro task queue instead.
990
if (IsWorkerGlobal(global)) {
991
microTaskQueue = &GetMicroTaskQueue();
992
} else {
993
MOZ_ASSERT(IsWorkerDebuggerGlobal(global) ||
994
IsWorkerDebuggerSandbox(global));
995
996
microTaskQueue = &GetDebuggerMicroTaskQueue();
997
}
998
999
JS::JobQueueMayNotBeEmpty(cx);
1000
microTaskQueue->push(runnable.forget());
1001
}
1002
1003
bool IsSystemCaller() const override {
1004
return mWorkerPrivate->UsesSystemPrincipal();
1005
}
1006
1007
void ReportError(JSErrorReport* aReport,
1008
JS::ConstUTF8CharsZ aToStringResult) override {
1009
mWorkerPrivate->ReportError(Context(), aToStringResult, aReport);
1010
}
1011
1012
WorkerPrivate* GetWorkerPrivate() const { return mWorkerPrivate; }
1013
1014
private:
1015
WorkerPrivate* mWorkerPrivate;
1016
};
1017
1018
namespace workerinternals {
1019
1020
namespace {
1021
1022
class WorkerThreadPrimaryRunnable final : public Runnable {
1023
WorkerPrivate* mWorkerPrivate;
1024
RefPtr<WorkerThread> mThread;
1025
JSRuntime* mParentRuntime;
1026
1027
class FinishedRunnable final : public Runnable {
1028
RefPtr<WorkerThread> mThread;
1029
1030
public:
1031
explicit FinishedRunnable(already_AddRefed<WorkerThread> aThread)
1032
: Runnable("WorkerThreadPrimaryRunnable::FinishedRunnable"),
1033
mThread(aThread) {
1034
MOZ_ASSERT(mThread);
1035
}
1036
1037
NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishedRunnable, Runnable)
1038
1039
private:
1040
~FinishedRunnable() {}
1041
1042
NS_DECL_NSIRUNNABLE
1043
};
1044
1045
public:
1046
WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
1047
WorkerThread* aThread, JSRuntime* aParentRuntime)
1048
: mozilla::Runnable("WorkerThreadPrimaryRunnable"),
1049
mWorkerPrivate(aWorkerPrivate),
1050
mThread(aThread),
1051
mParentRuntime(aParentRuntime) {
1052
MOZ_ASSERT(aWorkerPrivate);
1053
MOZ_ASSERT(aThread);
1054
}
1055
1056
NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerThreadPrimaryRunnable, Runnable)
1057
1058
private:
1059
~WorkerThreadPrimaryRunnable() {}
1060
1061
NS_DECL_NSIRUNNABLE
1062
};
1063
1064
void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
1065
AssertIsOnMainThread();
1066
1067
nsTArray<nsString> languages;
1068
Navigator::GetAcceptLanguages(languages);
1069
1070
RuntimeService* runtime = RuntimeService::GetService();
1071
if (runtime) {
1072
runtime->UpdateAllWorkerLanguages(languages);
1073
}
1074
}
1075
1076
void AppNameOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) {
1077
AssertIsOnMainThread();
1078
1079
nsAutoString override;
1080
Preferences::GetString("general.appname.override", override);
1081
1082
RuntimeService* runtime = RuntimeService::GetService();
1083
if (runtime) {
1084
runtime->UpdateAppNameOverridePreference(override);
1085
}
1086
}
1087
1088
void AppVersionOverrideChanged(const char* /* aPrefName */,
1089
void* /* aClosure */) {
1090
AssertIsOnMainThread();
1091
1092
nsAutoString override;
1093
Preferences::GetString("general.appversion.override", override);
1094
1095
RuntimeService* runtime = RuntimeService::GetService();
1096
if (runtime) {
1097
runtime->UpdateAppVersionOverridePreference(override);
1098
}
1099
}
1100
1101
void PlatformOverrideChanged(const char* /* aPrefName */,
1102
void* /* aClosure */) {
1103
AssertIsOnMainThread();
1104
1105
nsAutoString override;
1106
Preferences::GetString("general.platform.override", override);
1107
1108
RuntimeService* runtime = RuntimeService::GetService();
1109
if (runtime) {
1110
runtime->UpdatePlatformOverridePreference(override);
1111
}
1112
}
1113
1114
} /* anonymous namespace */
1115
1116
struct RuntimeService::IdleThreadInfo {
1117
RefPtr<WorkerThread> mThread;
1118
mozilla::TimeStamp mExpirationTime;
1119
};
1120
1121
// This is only touched on the main thread. Initialized in Init() below.
1122
JSSettings RuntimeService::sDefaultJSSettings;
1123
1124
RuntimeService::RuntimeService()
1125
: mMutex("RuntimeService::mMutex"),
1126
mObserved(false),
1127
mShuttingDown(false),
1128
mNavigatorPropertiesLoaded(false) {
1129
AssertIsOnMainThread();
1130
NS_ASSERTION(!gRuntimeService, "More than one service!");
1131
}
1132
1133
RuntimeService::~RuntimeService() {
1134
AssertIsOnMainThread();
1135
1136
// gRuntimeService can be null if Init() fails.
1137
NS_ASSERTION(!gRuntimeService || gRuntimeService == this,
1138
"More than one service!");
1139
1140
gRuntimeService = nullptr;
1141
}
1142
1143
// static
1144
RuntimeService* RuntimeService::GetOrCreateService() {
1145
AssertIsOnMainThread();
1146
1147
if (!gRuntimeService) {
1148
// The observer service now owns us until shutdown.
1149
gRuntimeService = new RuntimeService();
1150
if (NS_FAILED(gRuntimeService->Init())) {
1151
NS_WARNING("Failed to initialize!");
1152
gRuntimeService->Cleanup();
1153
gRuntimeService = nullptr;
1154
return nullptr;
1155
}
1156
}
1157
1158
return gRuntimeService;
1159
}
1160
1161
// static
1162
RuntimeService* RuntimeService::GetService() { return gRuntimeService; }
1163
1164
bool RuntimeService::RegisterWorker(WorkerPrivate* aWorkerPrivate) {
1165
aWorkerPrivate->AssertIsOnParentThread();
1166
1167
WorkerPrivate* parent = aWorkerPrivate->GetParent();
1168
if (!parent) {
1169
AssertIsOnMainThread();
1170
1171
if (mShuttingDown) {
1172
return false;
1173
}
1174
}
1175
1176
const bool isServiceWorker = aWorkerPrivate->IsServiceWorker();
1177
const bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
1178
const bool isDedicatedWorker = aWorkerPrivate->IsDedicatedWorker();
1179
if (isServiceWorker) {
1180
AssertIsOnMainThread();
1181
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_ATTEMPTS, 1);
1182
}
1183
1184
nsCString sharedWorkerScriptSpec;
1185
if (isSharedWorker) {
1186
AssertIsOnMainThread();
1187
1188
nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI();
1189
NS_ASSERTION(scriptURI, "Null script URI!");
1190
1191
nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
1192
if (NS_FAILED(rv)) {
1193
NS_WARNING("GetSpec failed?!");
1194
return false;
1195
}
1196
1197
NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
1198
}
1199
1200
bool exemptFromPerDomainMax = false;
1201
if (isServiceWorker) {
1202
AssertIsOnMainThread();
1203
exemptFromPerDomainMax = Preferences::GetBool(
1204
"dom.serviceWorkers.exemptFromPerDomainMax", false);
1205
}
1206
1207
const nsCString& domain = aWorkerPrivate->Domain();
1208
1209
bool queued = false;
1210
{
1211
MutexAutoLock lock(mMutex);
1212
1213
const auto& domainInfo =
1214
mDomainMap.LookupForAdd(domain).OrInsert([&domain, parent]() {
1215
NS_ASSERTION(!parent, "Shouldn't have a parent here!");
1216
Unused
1217
<< parent; // silence clang -Wunused-lambda-capture in opt builds
1218
WorkerDomainInfo* wdi = new WorkerDomainInfo();
1219
wdi->mDomain = domain;
1220
return wdi;
1221
});
1222
1223
queued = gMaxWorkersPerDomain &&
1224
domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
1225
!domain.IsEmpty() && !exemptFromPerDomainMax;
1226
1227
if (queued) {
1228
domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
1229
1230
// Worker spawn gets queued due to hitting max workers per domain
1231
// limit so let's log a warning.
1232
WorkerPrivate::ReportErrorToConsole("HittingMaxWorkersPerDomain2");
1233
1234
if (isServiceWorker) {
1235
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_GETS_QUEUED, 1);
1236
} else if (isSharedWorker) {
1237
Telemetry::Accumulate(Telemetry::SHARED_WORKER_SPAWN_GETS_QUEUED, 1);
1238
} else if (isDedicatedWorker) {
1239
Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_SPAWN_GETS_QUEUED, 1);
1240
}
1241
} else if (parent) {
1242
domainInfo->mChildWorkerCount++;
1243
} else if (isServiceWorker) {
1244
domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate);
1245
} else {
1246
domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
1247
}
1248
}
1249
1250
// From here on out we must call UnregisterWorker if something fails!
1251
if (parent) {
1252
if (!parent->AddChildWorker(aWorkerPrivate)) {
1253
UnregisterWorker(aWorkerPrivate);
1254
return false;
1255
}
1256
} else {
1257
if (!mNavigatorPropertiesLoaded) {
1258
Navigator::AppName(mNavigatorProperties.mAppName,
1259
aWorkerPrivate->GetPrincipal(),
1260
false /* aUsePrefOverriddenValue */);
1261
if (NS_FAILED(Navigator::GetAppVersion(
1262
mNavigatorProperties.mAppVersion, aWorkerPrivate->GetPrincipal(),
1263
false /* aUsePrefOverriddenValue */)) ||
1264
NS_FAILED(Navigator::GetPlatform(
1265
mNavigatorProperties.mPlatform, aWorkerPrivate->GetPrincipal(),
1266
false /* aUsePrefOverriddenValue */))) {
1267
UnregisterWorker(aWorkerPrivate);
1268
return false;
1269
}
1270
1271
// The navigator overridden properties should have already been read.
1272
1273
Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages);
1274
mNavigatorPropertiesLoaded = true;
1275
}
1276
1277
nsPIDOMWindowInner* window = aWorkerPrivate->GetWindow();
1278
1279
if (!isServiceWorker) {
1280
// Service workers are excluded since their lifetime is separate from
1281
// that of dom windows.
1282
const auto& windowArray = mWindowMap.LookupForAdd(window).OrInsert(
1283
[]() { return new nsTArray<WorkerPrivate*>(1); });
1284
if (!windowArray->Contains(aWorkerPrivate)) {
1285
windowArray->AppendElement(aWorkerPrivate);
1286
} else {
1287
MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
1288
}
1289
}
1290
}
1291
1292
if (!queued && !ScheduleWorker(aWorkerPrivate)) {
1293
return false;
1294
}
1295
1296
if (isServiceWorker) {
1297
AssertIsOnMainThread();
1298
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_WAS_SPAWNED, 1);
1299
}
1300
return true;
1301
}
1302
1303
void RuntimeService::UnregisterWorker(WorkerPrivate* aWorkerPrivate) {
1304
aWorkerPrivate->AssertIsOnParentThread();
1305
1306
WorkerPrivate* parent = aWorkerPrivate->GetParent();
1307
if (!parent) {
1308
AssertIsOnMainThread();
1309
}
1310
1311
const nsCString& domain = aWorkerPrivate->Domain();
1312
1313
WorkerPrivate* queuedWorker = nullptr;
1314
{
1315
MutexAutoLock lock(mMutex);
1316
1317
WorkerDomainInfo* domainInfo;
1318
if (!mDomainMap.Get(domain, &domainInfo)) {
1319
NS_ERROR("Don't have an entry for this domain!");
1320
}
1321
1322
// Remove old worker from everywhere.
1323
uint32_t index = domainInfo->mQueuedWorkers.IndexOf(aWorkerPrivate);
1324
if (index != kNoIndex) {
1325
// Was queued, remove from the list.
1326
domainInfo->mQueuedWorkers.RemoveElementAt(index);
1327
} else if (parent) {
1328
MOZ_ASSERT(domainInfo->mChildWorkerCount, "Must be non-zero!");
1329
domainInfo->mChildWorkerCount--;
1330
} else if (aWorkerPrivate->IsServiceWorker()) {
1331
MOZ_ASSERT(domainInfo->mActiveServiceWorkers.Contains(aWorkerPrivate),
1332
"Don't know about this worker!");
1333
domainInfo->mActiveServiceWorkers.RemoveElement(aWorkerPrivate);
1334
} else {
1335
MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(aWorkerPrivate),
1336
"Don't know about this worker!");
1337
domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
1338
}
1339
1340
// See if there's a queued worker we can schedule.
1341
if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
1342
!domainInfo->mQueuedWorkers.IsEmpty()) {
1343
queuedWorker = domainInfo->mQueuedWorkers[0];
1344
domainInfo->mQueuedWorkers.RemoveElementAt(0);
1345
1346
if (queuedWorker->GetParent()) {
1347
domainInfo->mChildWorkerCount++;
1348
} else if (queuedWorker->IsServiceWorker()) {
1349
domainInfo->mActiveServiceWorkers.AppendElement(queuedWorker);
1350
} else {
1351
domainInfo->mActiveWorkers.AppendElement(queuedWorker);
1352
}
1353
}
1354
1355
if (domainInfo->HasNoWorkers()) {
1356
MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
1357
mDomainMap.Remove(domain);
1358
}
1359
}
1360
1361
if (aWorkerPrivate->IsServiceWorker()) {
1362
AssertIsOnMainThread();
1363
Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LIFE_TIME,
1364
aWorkerPrivate->CreationTimeStamp());
1365
}
1366
1367
// NB: For Shared Workers we used to call ShutdownOnMainThread on the
1368
// RemoteWorkerController; however, that was redundant because
1369
// RemoteWorkerChild uses a WeakWorkerRef which notifies at about the
1370
// same time as us calling into the code here and would race with us.
1371
1372
if (parent) {
1373
parent->RemoveChildWorker(aWorkerPrivate);
1374
} else if (aWorkerPrivate->IsSharedWorker()) {
1375
AssertIsOnMainThread();
1376
1377
for (auto iter = mWindowMap.Iter(); !iter.Done(); iter.Next()) {
1378
const auto& workers = iter.Data();
1379
MOZ_ASSERT(workers);
1380
1381
if (workers->RemoveElement(aWorkerPrivate)) {
1382
MOZ_ASSERT(!workers->Contains(aWorkerPrivate),
1383
"Added worker more than once!");
1384
1385
if (workers->IsEmpty()) {
1386
iter.Remove();
1387
}
1388
}
1389
}
1390
} else if (aWorkerPrivate->IsDedicatedWorker()) {
1391
// May be null.
1392
nsPIDOMWindowInner* window = aWorkerPrivate->GetWindow();
1393
if (auto entry = mWindowMap.Lookup(window)) {
1394
MOZ_ALWAYS_TRUE(entry.Data()->RemoveElement(aWorkerPrivate));
1395
if (entry.Data()->IsEmpty()) {
1396
entry.Remove();
1397
}
1398
} else {
1399
MOZ_ASSERT_UNREACHABLE("window is not in mWindowMap");
1400
}
1401
}
1402
1403
if (queuedWorker && !ScheduleWorker(queuedWorker)) {
1404
UnregisterWorker(queuedWorker);
1405
}
1406
}
1407
1408
bool RuntimeService::ScheduleWorker(WorkerPrivate* aWorkerPrivate) {
1409
if (!aWorkerPrivate->Start()) {
1410
// This is ok, means that we didn't need to make a thread for this worker.
1411
return true;
1412
}
1413
1414
RefPtr<WorkerThread> thread;
1415
{
1416
MutexAutoLock lock(mMutex);
1417
if (!mIdleThreadArray.IsEmpty()) {
1418
uint32_t index = mIdleThreadArray.Length() - 1;
1419
mIdleThreadArray[index].mThread.swap(thread);
1420
mIdleThreadArray.RemoveElementAt(index);
1421
}
1422
}
1423
1424
const WorkerThreadFriendKey friendKey;
1425
1426
if (!thread) {
1427
thread = WorkerThread::Create(friendKey);
1428
if (!thread) {
1429
UnregisterWorker(aWorkerPrivate);
1430
return false;
1431
}
1432
}
1433
1434
if (NS_FAILED(thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL))) {
1435
NS_WARNING("Could not set the thread's priority!");
1436
}
1437
1438
aWorkerPrivate->SetThread(thread);
1439
JSContext* cx = CycleCollectedJSContext::Get()->Context();
1440
nsCOMPtr<nsIRunnable> runnable = new WorkerThreadPrimaryRunnable(
1441
aWorkerPrivate, thread, JS_GetParentRuntime(cx));
1442
if (NS_FAILED(
1443
thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) {
1444
UnregisterWorker(aWorkerPrivate);
1445
return false;
1446
}
1447
1448
return true;
1449
}
1450
1451
// static
1452
void RuntimeService::ShutdownIdleThreads(nsITimer* aTimer,
1453
void* /* aClosure */) {
1454
AssertIsOnMainThread();
1455
1456
RuntimeService* runtime = RuntimeService::GetService();
1457
NS_ASSERTION(runtime, "This should never be null!");
1458
1459
NS_ASSERTION(aTimer == runtime->mIdleThreadTimer, "Wrong timer!");
1460
1461
// Cheat a little and grab all threads that expire within one second of now.
1462
TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromSeconds(1);
1463
1464
TimeStamp nextExpiration;
1465
1466
AutoTArray<RefPtr<WorkerThread>, 20> expiredThreads;
1467
{
1468
MutexAutoLock lock(runtime->mMutex);
1469
1470
for (uint32_t index = 0; index < runtime->mIdleThreadArray.Length();
1471
index++) {
1472
IdleThreadInfo& info = runtime->mIdleThreadArray[index];
1473
if (info.mExpirationTime > now) {
1474
nextExpiration = info.mExpirationTime;
1475
break;
1476
}
1477
1478
RefPtr<WorkerThread>* thread = expiredThreads.AppendElement();
1479
thread->swap(info.mThread);
1480
}
1481
1482
if (!expiredThreads.IsEmpty()) {
1483
runtime->mIdleThreadArray.RemoveElementsAt(0, expiredThreads.Length());
1484
}
1485
}
1486
1487
if (!nextExpiration.IsNull()) {
1488
TimeDuration delta = nextExpiration - TimeStamp::NowLoRes();
1489
uint32_t delay(delta > TimeDuration(0) ? delta.ToMilliseconds() : 0);
1490
1491
// Reschedule the timer.
1492
MOZ_ALWAYS_SUCCEEDS(aTimer->InitWithNamedFuncCallback(
1493
ShutdownIdleThreads, nullptr, delay, nsITimer::TYPE_ONE_SHOT,
1494
"RuntimeService::ShutdownIdleThreads"));
1495
}
1496
1497
for (uint32_t index = 0; index < expiredThreads.Length(); index++) {
1498
if (NS_FAILED(expiredThreads[index]->Shutdown())) {
1499
NS_WARNING("Failed to shutdown thread!");
1500
}
1501
}
1502
}
1503
1504
nsresult RuntimeService::Init() {
1505
AssertIsOnMainThread();
1506
1507
nsLayoutStatics::AddRef();
1508
1509
// Initialize JSSettings.
1510
if (sDefaultJSSettings.gcSettings[0].key.isNothing()) {
1511
sDefaultJSSettings.contextOptions = JS::ContextOptions();
1512
sDefaultJSSettings.chrome.maxScriptRuntime = -1;
1513
sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
1514
#ifdef JS_GC_ZEAL
1515
sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
1516
sDefaultJSSettings.gcZeal = 0;
1517
#endif
1518
SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
1519
SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
1520
WORKER_DEFAULT_ALLOCATION_THRESHOLD);
1521
}
1522
1523
// nsIStreamTransportService is thread-safe but it must be initialized on the
1524
// main-thread. FileReader needs it, so, let's initialize it now.
1525
nsresult rv;
1526
nsCOMPtr<nsIStreamTransportService> sts =
1527
do_GetService(kStreamTransportServiceCID, &rv);
1528
NS_ENSURE_TRUE(sts, NS_ERROR_FAILURE);
1529
1530
mIdleThreadTimer = NS_NewTimer();
1531
NS_ENSURE_STATE(mIdleThreadTimer);
1532
1533
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1534
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
1535
1536
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
1537
NS_ENSURE_SUCCESS(rv, rv);
1538
1539
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
1540
NS_ENSURE_SUCCESS(rv, rv);
1541
1542
mObserved = true;
1543
1544
if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) {
1545
NS_WARNING("Failed to register for GC request notifications!");
1546
}
1547
1548
if (NS_FAILED(obs->AddObserver(this, CC_REQUEST_OBSERVER_TOPIC, false))) {
1549
NS_WARNING("Failed to register for CC request notifications!");
1550
}
1551
1552
if (NS_FAILED(
1553
obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC, false))) {
1554
NS_WARNING("Failed to register for memory pressure notifications!");
1555
}
1556
1557
if (NS_FAILED(
1558
obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) {
1559
NS_WARNING("Failed to register for offline notification event!");
1560
}
1561
1562
MOZ_ASSERT(!gRuntimeServiceDuringInit, "This should be false!");
1563
gRuntimeServiceDuringInit = true;
1564
1565
if (NS_FAILED(Preferences::RegisterPrefixCallback(
1566
LoadJSGCMemoryOptions,
1567
PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1568
NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
1569
LoadJSGCMemoryOptions,
1570
PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1571
#ifdef JS_GC_ZEAL
1572
NS_FAILED(Preferences::RegisterCallback(
1573
LoadGCZealOptions, PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) ||
1574
#endif
1575
1576
#define WORKER_PREF(name, callback) \
1577
NS_FAILED(Preferences::RegisterCallbackAndCall(callback, name)) ||
1578
WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF(
1579
"general.appname.override", AppNameOverrideChanged)
1580
WORKER_PREF("general.appversion.override", AppVersionOverrideChanged)
1581
WORKER_PREF("general.platform.override", PlatformOverrideChanged)
1582
#ifdef JS_GC_ZEAL
1583
WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
1584
#endif
1585
#undef WORKER_PREF
1586
1587
NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
1588
LoadContextOptions, PREF_WORKERS_OPTIONS_PREFIX)) ||
1589
NS_FAILED(Preferences::RegisterPrefixCallback(LoadContextOptions,
1590
PREF_JS_OPTIONS_PREFIX))) {
1591
NS_WARNING("Failed to register pref callbacks!");
1592
}
1593
1594
MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!");
1595
gRuntimeServiceDuringInit = false;
1596
1597
// We assume atomic 32bit reads/writes. If this assumption doesn't hold on
1598
// some wacky platform then the worst that could happen is that the close
1599
// handler will run for a slightly different amount of time.
1600
Preferences::AddIntVarCache(&sDefaultJSSettings.content.maxScriptRuntime,
1601
PREF_MAX_SCRIPT_RUN_TIME_CONTENT,
1602
MAX_SCRIPT_RUN_TIME_SEC);
1603
Preferences::AddIntVarCache(&sDefaultJSSettings.chrome.maxScriptRuntime,
1604
PREF_MAX_SCRIPT_RUN_TIME_CHROME, -1);
1605
1606
int32_t maxPerDomain =
1607
Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, MAX_WORKERS_PER_DOMAIN);
1608
gMaxWorkersPerDomain = std::max(0, maxPerDomain);
1609
1610
int32_t maxHardwareConcurrency = Preferences::GetInt(
1611
PREF_WORKERS_MAX_HARDWARE_CONCURRENCY, MAX_HARDWARE_CONCURRENCY);
1612
gMaxHardwareConcurrency = std::max(0, maxHardwareConcurrency);
1613
1614
RefPtr<OSFileConstantsService> osFileConstantsService =
1615
OSFileConstantsService::GetOrCreate();
1616
if (NS_WARN_IF(!osFileConstantsService)) {
1617
return NS_ERROR_FAILURE;
1618
}
1619
1620
if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
1621
return NS_ERROR_UNEXPECTED;
1622
}
1623
1624
// PerformanceService must be initialized on the main-thread.
1625
PerformanceService::GetOrCreate();
1626
1627
return NS_OK;
1628
}
1629
1630
void RuntimeService::Shutdown() {
1631
AssertIsOnMainThread();
1632
1633
MOZ_ASSERT(!mShuttingDown);
1634
// That's it, no more workers.
1635
mShuttingDown = true;
1636
1637
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1638
NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
1639
1640
// Tell anyone that cares that they're about to lose worker support.
1641
if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC,
1642
nullptr))) {
1643
NS_WARNING("NotifyObservers failed!");
1644
}
1645
1646
{
1647
MutexAutoLock lock(mMutex);
1648
1649
AutoTArray<WorkerPrivate*, 100> workers;
1650
AddAllTopLevelWorkersToArray(workers);
1651
1652
if (!workers.IsEmpty()) {
1653
// Cancel all top-level workers.
1654
{
1655
MutexAutoUnlock unlock(mMutex);
1656
1657
for (uint32_t index = 0; index < workers.Length(); index++) {
1658
if (!workers[index]->Cancel()) {
1659
NS_WARNING("Failed to cancel worker!");
1660
}
1661
}
1662
}
1663
}
1664
}
1665
}
1666
1667
namespace {
1668
1669
class CrashIfHangingRunnable : public WorkerControlRunnable {
1670
public:
1671
explicit CrashIfHangingRunnable(WorkerPrivate* aWorkerPrivate)
1672
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1673
mMonitor("CrashIfHangingRunnable::mMonitor") {}
1674
1675
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1676
aWorkerPrivate->DumpCrashInformation(mMsg);
1677
1678
MonitorAutoLock lock(mMonitor);
1679
lock.Notify();
1680
return true;
1681
}
1682
1683
nsresult Cancel() override {
1684
mMsg.Assign("Canceled");
1685
1686
MonitorAutoLock lock(mMonitor);
1687
lock.Notify();
1688
1689
return NS_OK;
1690
}
1691
1692
void DispatchAndWait() {
1693
MonitorAutoLock lock(mMonitor);
1694
1695
if (!Dispatch()) {
1696
mMsg.Assign("Dispatch Error");
1697
return;
1698
}
1699
1700
lock.Wait();
1701
}
1702
1703
const nsCString& MsgData() const { return mMsg; }
1704
1705
private:
1706
bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
1707
1708
void PostDispatch(WorkerPrivate* aWorkerPrivate,
1709
bool aDispatchResult) override {}
1710
1711
Monitor mMonitor;
1712
nsCString mMsg;
1713
};
1714
1715
} // namespace
1716
1717
void RuntimeService::CrashIfHanging() {
1718
MutexAutoLock lock(mMutex);
1719
1720
if (mDomainMap.IsEmpty()) {
1721
return;
1722
}
1723
1724
uint32_t activeWorkers = 0;
1725
uint32_t activeServiceWorkers = 0;
1726
uint32_t inactiveWorkers = 0;
1727
1728
nsTArray<WorkerPrivate*> workers;
1729
1730
for (auto iter = mDomainMap.Iter(); !iter.Done(); iter.Next()) {
1731
WorkerDomainInfo* aData = iter.UserData();
1732
1733
activeWorkers += aData->mActiveWorkers.Length();
1734
activeServiceWorkers += aData->mActiveServiceWorkers.Length();
1735
1736
workers.AppendElements(aData->mActiveWorkers);
1737
workers.AppendElements(aData->mActiveServiceWorkers);
1738
1739
// These might not be top-level workers...
1740
for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) {
1741
WorkerPrivate* worker = aData->mQueuedWorkers[index];
1742
if (!worker->GetParent()) {
1743
++inactiveWorkers;
1744
}
1745
}
1746
}
1747
1748
// We must have something pending...
1749
MOZ_DIAGNOSTIC_ASSERT(activeWorkers + activeServiceWorkers + inactiveWorkers);
1750
1751
nsCString msg;
1752
1753
// A: active Workers | S: active ServiceWorkers | Q: queued Workers
1754
msg.AppendPrintf("Workers Hanging - %d|A:%d|S:%d|Q:%d", mShuttingDown ? 1 : 0,
1755
activeWorkers, activeServiceWorkers, inactiveWorkers);
1756
1757
// For each thread, let's print some data to know what is going wrong.
1758
for (uint32_t i = 0; i < workers.Length(); ++i) {
1759
WorkerPrivate* workerPrivate = workers[i];
1760
1761
// BC: Busy Count
1762
msg.AppendPrintf("-BC:%d", workerPrivate->BusyCount());
1763
1764
RefPtr<CrashIfHangingRunnable> runnable =
1765
new CrashIfHangingRunnable(workerPrivate);
1766
runnable->DispatchAndWait();
1767
1768
msg.Append(runnable->MsgData());
1769
}
1770
1771
// This string will be leaked.
1772
MOZ_CRASH_UNSAFE(strdup(msg.BeginReading()));
1773
}
1774
1775
// This spins the event loop until all workers are finished and their threads
1776
// have been joined.
1777
void RuntimeService::Cleanup() {
1778
AssertIsOnMainThread();
1779
1780
if (!mShuttingDown) {
1781
Shutdown();
1782
}
1783
1784
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1785
NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
1786
1787
if (mIdleThreadTimer) {
1788
if (NS_FAILED(mIdleThreadTimer->Cancel())) {
1789
NS_WARNING("Failed to cancel idle timer!");
1790
}
1791
mIdleThreadTimer = nullptr;
1792
}
1793
1794
{
1795
MutexAutoLock lock(mMutex);
1796
1797
AutoTArray<WorkerPrivate*, 100> workers;
1798
AddAllTopLevelWorkersToArray(workers);
1799
1800
if (!workers.IsEmpty()) {
1801
nsIThread* currentThread = NS_GetCurrentThread();
1802
NS_ASSERTION(currentThread, "This should never be null!");
1803
1804
// Shut down any idle threads.
1805
if (!mIdleThreadArray.IsEmpty()) {
1806
AutoTArray<RefPtr<WorkerThread>, 20> idleThreads;
1807
1808
uint32_t idleThreadCount = mIdleThreadArray.Length();
1809
idleThreads.SetLength(idleThreadCount);
1810
1811
for (uint32_t index = 0; index < idleThreadCount; index++) {
1812
NS_ASSERTION(mIdleThreadArray[index].mThread, "Null thread!");
1813
idleThreads[index].swap(mIdleThreadArray[index].mThread);
1814
}
1815
1816
mIdleThreadArray.Clear();
1817
1818
MutexAutoUnlock unlock(mMutex);
1819
1820
for (uint32_t index = 0; index < idleThreadCount; index++) {
1821
if (NS_FAILED(idleThreads[index]->Shutdown())) {
1822
NS_WARNING("Failed to shutdown thread!");
1823
}
1824
}
1825
}
1826
1827
// And make sure all their final messages have run and all their threads
1828
// have joined.
1829
while (mDomainMap.Count()) {
1830
MutexAutoUnlock unlock(mMutex);
1831
1832
if (!NS_ProcessNextEvent(currentThread)) {
1833
NS_WARNING("Something bad happened!");
1834
break;
1835
}
1836
}
1837
}
1838
}
1839
1840
NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!");
1841
1842
if (mObserved) {
1843
if (NS_FAILED(Preferences::UnregisterPrefixCallback(
1844
LoadContextOptions, PREF_JS_OPTIONS_PREFIX)) ||
1845
NS_FAILED(Preferences::UnregisterPrefixCallback(
1846
LoadContextOptions, PREF_WORKERS_OPTIONS_PREFIX)) ||
1847
#define WORKER_PREF(name, callback) \
1848
NS_FAILED(Preferences::UnregisterCallback(callback, name)) ||
1849
WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF(
1850
"general.appname.override",
1851
AppNameOverrideChanged) WORKER_PREF("general.appversion.override",
1852
AppVersionOverrideChanged)
1853
WORKER_PREF("general.platform.override", PlatformOverrideChanged)
1854
#ifdef JS_GC_ZEAL
1855
WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
1856
#endif
1857
#undef WORKER_PREF
1858
1859
#ifdef JS_GC_ZEAL
1860
NS_FAILED(Preferences::UnregisterCallback(
1861
LoadGCZealOptions,
1862
PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) ||
1863
#endif
1864
NS_FAILED(Preferences::UnregisterPrefixCallback(
1865
LoadJSGCMemoryOptions,
1866
PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1867
NS_FAILED(Preferences::UnregisterPrefixCallback(
1868
LoadJSGCMemoryOptions,
1869
PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
1870
NS_WARNING("Failed to unregister pref callbacks!");
1871
}
1872
1873
if (obs) {
1874
if (NS_F