tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

testGCFinalizeCallback.cpp (6439B)


      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/. */
      4 
      5 #include "js/GlobalObject.h"
      6 #include "jsapi-tests/tests.h"
      7 
      8 using namespace js;
      9 using namespace JS;
     10 
     11 static const unsigned BufSize = 20;
     12 static unsigned FinalizeCalls = 0;
     13 static JSFinalizeStatus StatusBuffer[BufSize];
     14 
     15 BEGIN_TEST(testGCFinalizeCallback) {
     16  AutoGCParameter param1(cx, JSGC_INCREMENTAL_GC_ENABLED, true);
     17  AutoGCParameter param2(cx, JSGC_PER_ZONE_GC_ENABLED, true);
     18 
     19  /* Full GC, non-incremental. */
     20  FinalizeCalls = 0;
     21  JS_GC(cx);
     22  CHECK(cx->runtime()->gc.isFullGc());
     23  CHECK(checkSingleGroup());
     24  CHECK(checkFinalizeStatus());
     25 
     26  /* Full GC, incremental. */
     27  FinalizeCalls = 0;
     28  JS::PrepareForFullGC(cx);
     29  SliceBudget startBudget(TimeBudget(1000000));
     30  JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API,
     31                         startBudget);
     32  while (cx->runtime()->gc.isIncrementalGCInProgress()) {
     33    JS::PrepareForFullGC(cx);
     34    SliceBudget budget(TimeBudget(1000000));
     35    JS::IncrementalGCSlice(cx, JS::GCReason::API, budget);
     36  }
     37  CHECK(!cx->runtime()->gc.isIncrementalGCInProgress());
     38  CHECK(cx->runtime()->gc.isFullGc());
     39  CHECK(checkMultipleGroups());
     40  CHECK(checkFinalizeStatus());
     41 
     42 #ifdef JS_GC_ZEAL
     43  // Bug 1377593 - the below tests want to control how many zones are GC'ing,
     44  // and some zeal modes will convert them into all-zones GCs.
     45  JS::SetGCZeal(cx, 0, 0);
     46 #endif
     47 
     48  JS::RootedObject global1(cx, createTestGlobal());
     49  JS::RootedObject global2(cx, createTestGlobal());
     50  JS::RootedObject global3(cx, createTestGlobal());
     51  CHECK(global1);
     52  CHECK(global2);
     53  CHECK(global3);
     54 
     55  /* Zone GC, non-incremental, single zone. */
     56  FinalizeCalls = 0;
     57  JS::PrepareZoneForGC(cx, global1->zone());
     58  JS::NonIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API);
     59  CHECK(!cx->runtime()->gc.isFullGc());
     60  CHECK(checkSingleGroup());
     61  CHECK(checkFinalizeStatus());
     62 
     63  /* Zone GC, non-incremental, multiple zones. */
     64  FinalizeCalls = 0;
     65  JS::PrepareZoneForGC(cx, global1->zone());
     66  JS::PrepareZoneForGC(cx, global2->zone());
     67  JS::PrepareZoneForGC(cx, global3->zone());
     68  JS::NonIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API);
     69  CHECK(!cx->runtime()->gc.isFullGc());
     70  CHECK(checkSingleGroup());
     71  CHECK(checkFinalizeStatus());
     72 
     73  /* Zone GC, incremental, single zone. */
     74  FinalizeCalls = 0;
     75  JS::PrepareZoneForGC(cx, global1->zone());
     76  SliceBudget budget(TimeBudget(1000000));
     77  JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API, budget);
     78  while (cx->runtime()->gc.isIncrementalGCInProgress()) {
     79    JS::PrepareZoneForGC(cx, global1->zone());
     80    budget = SliceBudget(TimeBudget(1000000));
     81    JS::IncrementalGCSlice(cx, JS::GCReason::API, budget);
     82  }
     83  CHECK(!cx->runtime()->gc.isIncrementalGCInProgress());
     84  CHECK(!cx->runtime()->gc.isFullGc());
     85  CHECK(checkSingleGroup());
     86  CHECK(checkFinalizeStatus());
     87 
     88  /* Zone GC, incremental, multiple zones. */
     89  FinalizeCalls = 0;
     90  JS::PrepareZoneForGC(cx, global1->zone());
     91  JS::PrepareZoneForGC(cx, global2->zone());
     92  JS::PrepareZoneForGC(cx, global3->zone());
     93  budget = SliceBudget(TimeBudget(1000000));
     94  JS::StartIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API, budget);
     95  while (cx->runtime()->gc.isIncrementalGCInProgress()) {
     96    JS::PrepareZoneForGC(cx, global1->zone());
     97    JS::PrepareZoneForGC(cx, global2->zone());
     98    JS::PrepareZoneForGC(cx, global3->zone());
     99    budget = SliceBudget(TimeBudget(1000000));
    100    JS::IncrementalGCSlice(cx, JS::GCReason::API, budget);
    101  }
    102  CHECK(!cx->runtime()->gc.isIncrementalGCInProgress());
    103  CHECK(!cx->runtime()->gc.isFullGc());
    104  CHECK(checkMultipleGroups());
    105  CHECK(checkFinalizeStatus());
    106 
    107 #ifdef JS_GC_ZEAL
    108 
    109  /* Full GC with reset due to new zone, becoming zone GC. */
    110 
    111  FinalizeCalls = 0;
    112  JS::SetGCZeal(cx, 9, 1000000);
    113  JS::PrepareForFullGC(cx);
    114  budget = SliceBudget(WorkBudget(1));
    115  cx->runtime()->gc.startDebugGC(JS::GCOptions::Normal, budget);
    116  CHECK(cx->runtime()->gc.state() == gc::State::Mark);
    117  CHECK(cx->runtime()->gc.isFullGc());
    118 
    119  JS::RootedObject global4(cx, createTestGlobal());
    120  budget = SliceBudget(WorkBudget(1));
    121  cx->runtime()->gc.debugGCSlice(budget);
    122  while (cx->runtime()->gc.isIncrementalGCInProgress()) {
    123    cx->runtime()->gc.debugGCSlice(budget);
    124  }
    125  CHECK(!cx->runtime()->gc.isIncrementalGCInProgress());
    126  CHECK(checkSingleGroup());
    127  CHECK(checkFinalizeStatus());
    128 
    129  JS::SetGCZeal(cx, 0, 0);
    130 
    131 #endif
    132 
    133  /*
    134   * Make some use of the globals here to ensure the compiler doesn't optimize
    135   * them away in release builds, causing the zones to be collected and
    136   * the test to fail.
    137   */
    138  CHECK(JS_IsGlobalObject(global1));
    139  CHECK(JS_IsGlobalObject(global2));
    140  CHECK(JS_IsGlobalObject(global3));
    141 
    142  return true;
    143 }
    144 
    145 JSObject* createTestGlobal() {
    146  JS::RealmOptions options;
    147  return JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
    148                            JS::FireOnNewGlobalHook, options);
    149 }
    150 
    151 virtual bool init() override {
    152  if (!JSAPIRuntimeTest::init()) {
    153    return false;
    154  }
    155 
    156  JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
    157  return true;
    158 }
    159 
    160 virtual void uninit() override {
    161  JS_RemoveFinalizeCallback(cx, FinalizeCallback);
    162  JSAPIRuntimeTest::uninit();
    163 }
    164 
    165 bool checkSingleGroup() {
    166  CHECK(FinalizeCalls < BufSize);
    167  CHECK(FinalizeCalls == 4);
    168  return true;
    169 }
    170 
    171 bool checkMultipleGroups() {
    172  CHECK(FinalizeCalls < BufSize);
    173  CHECK(FinalizeCalls % 3 == 1);
    174  CHECK((FinalizeCalls - 1) / 3 > 1);
    175  return true;
    176 }
    177 
    178 bool checkFinalizeStatus() {
    179  /*
    180   * The finalize callback should be called twice for each sweep group
    181   * finalized, with status JSFINALIZE_GROUP_START and JSFINALIZE_GROUP_END,
    182   * and then once more with JSFINALIZE_COLLECTION_END.
    183   */
    184 
    185  for (unsigned i = 0; i < FinalizeCalls - 1; i += 3) {
    186    CHECK(StatusBuffer[i] == JSFINALIZE_GROUP_PREPARE);
    187    CHECK(StatusBuffer[i + 1] == JSFINALIZE_GROUP_START);
    188    CHECK(StatusBuffer[i + 2] == JSFINALIZE_GROUP_END);
    189  }
    190 
    191  CHECK(StatusBuffer[FinalizeCalls - 1] == JSFINALIZE_COLLECTION_END);
    192 
    193  return true;
    194 }
    195 
    196 static void FinalizeCallback(JS::GCContext* gcx, JSFinalizeStatus status,
    197                             void* data) {
    198  if (FinalizeCalls < BufSize) {
    199    StatusBuffer[FinalizeCalls] = status;
    200  }
    201  ++FinalizeCalls;
    202 }
    203 END_TEST(testGCFinalizeCallback)