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