tor-browser

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

ConcurrentDelazification.h (6267B)


      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 #ifndef vm_ConcurrentDelazification_h
      8 #define vm_ConcurrentDelazification_h
      9 
     10 #include "mozilla/Maybe.h"            // mozilla::Maybe
     11 #include "mozilla/MemoryReporting.h"  // mozilla::MallocSizeOf
     12 
     13 #include <stddef.h>  // size_t
     14 #include <utility>   // std::pair
     15 
     16 #include "frontend/CompilationStencil.h"  // frontend::{InitialStencilAndDelazifications, CompilationStencil, ScriptStencilRef, CompilationStencilMerger}
     17 #include "frontend/ScriptIndex.h"         // frontend::ScriptIndex
     18 #include "js/AllocPolicy.h"               // SystemAllocPolicy
     19 #include "js/CompileOptions.h"  // JS::PrefableCompileOptions, JS::ReadOnlyCompileOptions
     20 #include "js/experimental/JSStencil.h"  // RefPtrTraits for InitialStencilAndDelazifications
     21 #include "js/UniquePtr.h"               // UniquePtr
     22 #include "js/Vector.h"                  // Vector
     23 
     24 namespace js {
     25 
     26 class FrontendContext;
     27 
     28 // Base class for implementing the various strategies to iterate over the
     29 // functions to be delazified, or to decide when to stop doing any
     30 // delazification.
     31 //
     32 // When created, the `add` function should be called with the top-level
     33 // ScriptIndex.
     34 struct DelazifyStrategy {
     35  using ScriptStencilRef = frontend::ScriptStencilRef;
     36  using ScriptIndex = frontend::ScriptIndex;
     37  using InitialStencilAndDelazifications =
     38      frontend::InitialStencilAndDelazifications;
     39 
     40  virtual ~DelazifyStrategy() = default;
     41 
     42  // Returns true if no more functions should be delazified. Note, this does not
     43  // imply that every function got delazified.
     44  virtual bool done() const = 0;
     45 
     46  // Return a function identifier which represent the next function to be
     47  // delazified. If no more function should be delazified, then return 0.
     48  virtual ScriptStencilRef next() = 0;
     49 
     50  // Empty the list of functions to be processed next. done() should return true
     51  // after this call.
     52  virtual void clear() = 0;
     53 
     54  // Insert an index in the container of the delazification strategy. A strategy
     55  // can choose to ignore the insertion of an index in its queue of function to
     56  // delazify. Return false only in case of errors while inserting, and true
     57  // otherwise.
     58  [[nodiscard]] virtual bool insert(ScriptStencilRef& ref) = 0;
     59 
     60  // Add the inner functions of a delazified function. This function should only
     61  // be called with a function which has some bytecode associated with it, and
     62  // register functions which parent are already delazified.
     63  //
     64  // This function is called with the script index of:
     65  //  - top-level script, when starting the off-thread delazification.
     66  //  - functions added by `add` and delazified by `DelazificationContext`.
     67  [[nodiscard]] bool add(FrontendContext* fc, ScriptStencilRef& ref);
     68 };
     69 
     70 // Delazify all functions using a Depth First traversal of the function-tree
     71 // ordered, where each functions is visited in source-order.
     72 //
     73 // When `add` is called with the top-level ScriptIndex. This will push all inner
     74 // functions to a stack such that they are popped in source order. Each
     75 // function, once delazified, would be used to schedule their inner functions
     76 // the same way.
     77 //
     78 // Hypothesis: This strategy parses all functions in source order, with the
     79 // expectation that calls will follow the same order, and that helper thread
     80 // would always be ahead of the execution.
     81 struct DepthFirstDelazification final : public DelazifyStrategy {
     82  Vector<frontend::ScriptStencilRef, 0, SystemAllocPolicy> stack;
     83 
     84  bool done() const override { return stack.empty(); }
     85  ScriptStencilRef next() override { return stack.popCopy(); }
     86  void clear() override { return stack.clear(); }
     87  bool insert(frontend::ScriptStencilRef& ref) override {
     88    return stack.append(ref);
     89  }
     90 };
     91 
     92 // Delazify all functions using a traversal which select the largest function
     93 // first. The intent being that if the main thread races with the helper thread,
     94 // then the main thread should only have to parse small functions instead of the
     95 // large ones which would be prioritized by this delazification strategy.
     96 struct LargeFirstDelazification final : public DelazifyStrategy {
     97  using SourceSize = uint32_t;
     98  Vector<std::pair<SourceSize, ScriptStencilRef>, 0, SystemAllocPolicy> heap;
     99 
    100  bool done() const override { return heap.empty(); }
    101  ScriptStencilRef next() override;
    102  void clear() override { return heap.clear(); }
    103  bool insert(frontend::ScriptStencilRef&) override;
    104 };
    105 
    106 class DelazificationContext {
    107  const JS::PrefableCompileOptions initialPrefableOptions_;
    108  using Stencils = frontend::InitialStencilAndDelazifications;
    109 
    110  // Queue of functions to be processed while delazifying.
    111  UniquePtr<DelazifyStrategy> strategy_;
    112 
    113  RefPtr<Stencils> stencils_;
    114  mozilla::Maybe<Stencils::RelativeIndexesGuard> indexesGuard_;
    115 
    116  // Record any errors happening while parsing or generating bytecode.
    117  FrontendContext fc_;
    118 
    119  size_t stackQuota_;
    120 
    121  bool isInterrupted_ = false;
    122 
    123 public:
    124  explicit DelazificationContext(
    125      const JS::PrefableCompileOptions& initialPrefableOptions,
    126      size_t stackQuota)
    127      : initialPrefableOptions_(initialPrefableOptions),
    128        stackQuota_(stackQuota) {}
    129 
    130  bool init(const JS::ReadOnlyCompileOptions& options, Stencils* stencils);
    131  bool delazify();
    132 
    133  // This function is called by `delazify` function to know whether the
    134  // delazification should be interrupted.
    135  //
    136  // The `delazify` function holds on a thread until all functions iterated
    137  // over by the strategy. However, as a `delazify` function iterates over
    138  // multiple functions, it can easily be interrupted at function boundaries.
    139  //
    140  // TODO: (Bug 1773683) Plug this with the mozilla::Task::RequestInterrupt
    141  // function which is wrapping HelperThreads tasks within Mozilla.
    142  bool isInterrupted() const { return isInterrupted_; }
    143  void interrupt() { isInterrupted_ = true; }
    144 
    145  bool done() const;
    146 
    147  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
    148 };
    149 
    150 } /* namespace js */
    151 
    152 #endif /* vm_ConcurrentDelazification_h */