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