Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / js / src / jsapi-tests / testGCFinalizeCallback.cpp
blob290356c4212b832ccff5c09d1a21bc863a89bd53
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "js/GlobalObject.h"
6 #include "jsapi-tests/tests.h"
8 using namespace js;
10 static const unsigned BufSize = 20;
11 static unsigned FinalizeCalls = 0;
12 static JSFinalizeStatus StatusBuffer[BufSize];
14 BEGIN_TEST(testGCFinalizeCallback) {
15 AutoGCParameter param1(cx, JSGC_INCREMENTAL_GC_ENABLED, true);
16 AutoGCParameter param2(cx, JSGC_PER_ZONE_GC_ENABLED, true);
18 /* Full GC, non-incremental. */
19 FinalizeCalls = 0;
20 JS_GC(cx);
21 CHECK(cx->runtime()->gc.isFullGc());
22 CHECK(checkSingleGroup());
23 CHECK(checkFinalizeStatus());
25 /* Full GC, incremental. */
26 FinalizeCalls = 0;
27 JS::PrepareForFullGC(cx);
28 SliceBudget startBudget(TimeBudget(1000000));
29 JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API,
30 startBudget);
31 while (cx->runtime()->gc.isIncrementalGCInProgress()) {
32 JS::PrepareForFullGC(cx);
33 SliceBudget budget(TimeBudget(1000000));
34 JS::IncrementalGCSlice(cx, JS::GCReason::API, budget);
36 CHECK(!cx->runtime()->gc.isIncrementalGCInProgress());
37 CHECK(cx->runtime()->gc.isFullGc());
38 CHECK(checkMultipleGroups());
39 CHECK(checkFinalizeStatus());
41 #ifdef JS_GC_ZEAL
42 // Bug 1377593 - the below tests want to control how many zones are GC'ing,
43 // and some zeal modes will convert them into all-zones GCs.
44 JS_SetGCZeal(cx, 0, 0);
45 #endif
47 JS::RootedObject global1(cx, createTestGlobal());
48 JS::RootedObject global2(cx, createTestGlobal());
49 JS::RootedObject global3(cx, createTestGlobal());
50 CHECK(global1);
51 CHECK(global2);
52 CHECK(global3);
54 /* Zone GC, non-incremental, single zone. */
55 FinalizeCalls = 0;
56 JS::PrepareZoneForGC(cx, global1->zone());
57 JS::NonIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API);
58 CHECK(!cx->runtime()->gc.isFullGc());
59 CHECK(checkSingleGroup());
60 CHECK(checkFinalizeStatus());
62 /* Zone GC, non-incremental, multiple zones. */
63 FinalizeCalls = 0;
64 JS::PrepareZoneForGC(cx, global1->zone());
65 JS::PrepareZoneForGC(cx, global2->zone());
66 JS::PrepareZoneForGC(cx, global3->zone());
67 JS::NonIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API);
68 CHECK(!cx->runtime()->gc.isFullGc());
69 CHECK(checkSingleGroup());
70 CHECK(checkFinalizeStatus());
72 /* Zone GC, incremental, single zone. */
73 FinalizeCalls = 0;
74 JS::PrepareZoneForGC(cx, global1->zone());
75 SliceBudget budget(TimeBudget(1000000));
76 JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API, budget);
77 while (cx->runtime()->gc.isIncrementalGCInProgress()) {
78 JS::PrepareZoneForGC(cx, global1->zone());
79 budget = SliceBudget(TimeBudget(1000000));
80 JS::IncrementalGCSlice(cx, JS::GCReason::API, budget);
82 CHECK(!cx->runtime()->gc.isIncrementalGCInProgress());
83 CHECK(!cx->runtime()->gc.isFullGc());
84 CHECK(checkSingleGroup());
85 CHECK(checkFinalizeStatus());
87 /* Zone GC, incremental, multiple zones. */
88 FinalizeCalls = 0;
89 JS::PrepareZoneForGC(cx, global1->zone());
90 JS::PrepareZoneForGC(cx, global2->zone());
91 JS::PrepareZoneForGC(cx, global3->zone());
92 budget = SliceBudget(TimeBudget(1000000));
93 JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API, budget);
94 while (cx->runtime()->gc.isIncrementalGCInProgress()) {
95 JS::PrepareZoneForGC(cx, global1->zone());
96 JS::PrepareZoneForGC(cx, global2->zone());
97 JS::PrepareZoneForGC(cx, global3->zone());
98 budget = SliceBudget(TimeBudget(1000000));
99 JS::IncrementalGCSlice(cx, JS::GCReason::API, budget);
101 CHECK(!cx->runtime()->gc.isIncrementalGCInProgress());
102 CHECK(!cx->runtime()->gc.isFullGc());
103 CHECK(checkMultipleGroups());
104 CHECK(checkFinalizeStatus());
106 #ifdef JS_GC_ZEAL
108 /* Full GC with reset due to new zone, becoming zone GC. */
110 FinalizeCalls = 0;
111 JS_SetGCZeal(cx, 9, 1000000);
112 JS::PrepareForFullGC(cx);
113 budget = SliceBudget(WorkBudget(1));
114 cx->runtime()->gc.startDebugGC(JS::GCOptions::Normal, budget);
115 CHECK(cx->runtime()->gc.state() == gc::State::Mark);
116 CHECK(cx->runtime()->gc.isFullGc());
118 JS::RootedObject global4(cx, createTestGlobal());
119 budget = SliceBudget(WorkBudget(1));
120 cx->runtime()->gc.debugGCSlice(budget);
121 while (cx->runtime()->gc.isIncrementalGCInProgress()) {
122 cx->runtime()->gc.debugGCSlice(budget);
124 CHECK(!cx->runtime()->gc.isIncrementalGCInProgress());
125 CHECK(checkSingleGroup());
126 CHECK(checkFinalizeStatus());
128 JS_SetGCZeal(cx, 0, 0);
130 #endif
133 * Make some use of the globals here to ensure the compiler doesn't optimize
134 * them away in release builds, causing the zones to be collected and
135 * the test to fail.
137 CHECK(JS_IsGlobalObject(global1));
138 CHECK(JS_IsGlobalObject(global2));
139 CHECK(JS_IsGlobalObject(global3));
141 return true;
144 JSObject* createTestGlobal() {
145 JS::RealmOptions options;
146 return JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
147 JS::FireOnNewGlobalHook, options);
150 virtual bool init() override {
151 if (!JSAPIRuntimeTest::init()) {
152 return false;
155 JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
156 return true;
159 virtual void uninit() override {
160 JS_RemoveFinalizeCallback(cx, FinalizeCallback);
161 JSAPIRuntimeTest::uninit();
164 bool checkSingleGroup() {
165 CHECK(FinalizeCalls < BufSize);
166 CHECK(FinalizeCalls == 4);
167 return true;
170 bool checkMultipleGroups() {
171 CHECK(FinalizeCalls < BufSize);
172 CHECK(FinalizeCalls % 3 == 1);
173 CHECK((FinalizeCalls - 1) / 3 > 1);
174 return true;
177 bool checkFinalizeStatus() {
179 * The finalize callback should be called twice for each sweep group
180 * finalized, with status JSFINALIZE_GROUP_START and JSFINALIZE_GROUP_END,
181 * and then once more with JSFINALIZE_COLLECTION_END.
184 for (unsigned i = 0; i < FinalizeCalls - 1; i += 3) {
185 CHECK(StatusBuffer[i] == JSFINALIZE_GROUP_PREPARE);
186 CHECK(StatusBuffer[i + 1] == JSFINALIZE_GROUP_START);
187 CHECK(StatusBuffer[i + 2] == JSFINALIZE_GROUP_END);
190 CHECK(StatusBuffer[FinalizeCalls - 1] == JSFINALIZE_COLLECTION_END);
192 return true;
195 static void FinalizeCallback(JS::GCContext* gcx, JSFinalizeStatus status,
196 void* data) {
197 if (FinalizeCalls < BufSize) {
198 StatusBuffer[FinalizeCalls] = status;
200 ++FinalizeCalls;
202 END_TEST(testGCFinalizeCallback)