tor-browser

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

MicroTask.h (7575B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
      2 * vim: set ts=8 sts=4 et sw=4 tw=99:
      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 js_friend_MicroTask_h
      8 #define js_friend_MicroTask_h
      9 
     10 #include "jstypes.h"
     11 
     12 #include "js/GCPolicyAPI.h"
     13 #include "js/RootingAPI.h"
     14 #include "js/TypeDecls.h"
     15 #include "js/UniquePtr.h"
     16 #include "js/Value.h"
     17 #include "js/ValueArray.h"
     18 
     19 namespace JS {
     20 
     21 // [SMDOC] MicroTasks in SpiderMonkey
     22 //
     23 // To enable higher performance, this header allows an embedding to work with
     24 // a MicroTask queue stored inside the JS engine. This allows optimization of
     25 // tasks by avoiding allocations in important cases.
     26 //
     27 // To make this work, we need some cooperation with the embedding.
     28 //
     29 // The high level thrust of this is that rather than managing the JobQueue
     30 // themselves, embeddings assume that there's a JobQueue available to them
     31 // inside the engine. When 'runJobs' happens, the embedding is responsible
     32 // for pulling jobs of the queue, doing any setup required, then calling
     33 // them.
     34 //
     35 // Embedding jobs are trivially supportable, since a MicroTask job is
     36 // represented as a JS::Value, and thus an embedding job may be put on
     37 // the queue by wrapping it in a JS::Value (e.g. using Private to store
     38 // C++ pointers).
     39 //
     40 // The major requirement is that if a MicroTask identifies as a "JS"
     41 // MicroTask, by passing the IsJSMicrotask predicate, the job must be
     42 // run by calling RunJSMicroTask, while in the realm specified by the
     43 // global returned by GetExecutionGlobalFromJSMicroTask, e.g
     44 //
     45 //    JSObject* global = JS::GetExecutionGlobalFromJSMicroTask(job);
     46 //    if (global) {
     47 //      AutoRealm ar(cx, global);
     48 //      if (!JS::RunJSMicroTask(cx, job)) {
     49 //        ...
     50 //      }
     51 //    }
     52 
     53 // A MicroTask is a JS::Value. Using this MicroTask system allows
     54 // embedders to put whatever pointer they would like into the queue.
     55 // The task will be dequeued unchanged.
     56 //
     57 // The major requirement here is that if the MicroTask is a JS
     58 // MicroTask (as determined by IsJSMicroTask), it must be run
     59 // by calling RunJSMicroTask, while in the realm specified by
     60 // GetExecutionGlobalFromJSMicroTask.
     61 //
     62 // An embedding is free to do with non-JS MicroTasks as it
     63 // sees fit.
     64 using GenericMicroTask = JS::Value;
     65 using JSMicroTask = JSObject;
     66 
     67 JS_PUBLIC_API bool IsJSMicroTask(const JS::GenericMicroTask& hv);
     68 JS_PUBLIC_API JSMicroTask* ToUnwrappedJSMicroTask(
     69    const JS::GenericMicroTask& genericMicroTask);
     70 JS_PUBLIC_API JSMicroTask* ToMaybeWrappedJSMicroTask(
     71    const JS::GenericMicroTask& genericMicroTask);
     72 
     73 // Run a MicroTask that is known to be a JS MicroTask. This will crash
     74 // if provided an invalid task kind.
     75 //
     76 // This will return false if an exception is thrown while processing.
     77 JS_PUBLIC_API bool RunJSMicroTask(JSContext* cx,
     78                                  Handle<JS::JSMicroTask*> entry);
     79 
     80 // Queue Management. This is done per-JSContext.
     81 //
     82 // Internally we maintain two queues, one for 'debugger' microtasks. These
     83 // are expected in normal operation to either be popped off the queue first,
     84 // or processed separately.
     85 //
     86 // Non-debugger MicroTasks are "regular" microtasks, and go to the regular
     87 // microtask queue.
     88 //
     89 // In general, we highly recommend that most embeddings use only the regular
     90 // microtask queue. The debugger microtask queue mostly exists to support
     91 // patterns used by Gecko.
     92 //
     93 // These methods only fail for OOM.
     94 JS_PUBLIC_API bool EnqueueMicroTask(JSContext* cx,
     95                                    const GenericMicroTask& entry);
     96 JS_PUBLIC_API bool EnqueueDebugMicroTask(JSContext* cx,
     97                                         const GenericMicroTask& entry);
     98 JS_PUBLIC_API bool PrependMicroTask(JSContext* cx,
     99                                    const GenericMicroTask& entry);
    100 
    101 // Dequeue the next MicroTask. If there are no MicroTasks of the appropriate
    102 // kind, each of the below API returns JS::NullValue().
    103 //
    104 // The generic DequeueNext will always pull a debugger microtask first,
    105 // if one exists, then a regular microtask if one exists.
    106 // - DequeueNextDebuggerMicroTask only pulls from the debugger queue.
    107 // - DequeueNextRegularMicroTask only pulls from the regular queue.
    108 //
    109 // Internally, these basically do
    110 //
    111 //    if (HasXMicroTask()) { return X.popFront(); } return NullValue()
    112 //
    113 // so checking for emptiness before calling these is not required, and is
    114 // very slightly less efficient.
    115 JS_PUBLIC_API GenericMicroTask DequeueNextMicroTask(JSContext* cx);
    116 JS_PUBLIC_API GenericMicroTask DequeueNextDebuggerMicroTask(JSContext* cx);
    117 JS_PUBLIC_API GenericMicroTask DequeueNextRegularMicroTask(JSContext* cx);
    118 
    119 // Returns true if there are -any- microtasks pending in the queue.
    120 JS_PUBLIC_API bool HasAnyMicroTasks(JSContext* cx);
    121 
    122 // Returns true if there are any debugger microtasks pending in the queue.
    123 JS_PUBLIC_API bool HasDebuggerMicroTasks(JSContext* cx);
    124 
    125 // Returns true if there are any regular (non-debugger) microtasks pending in
    126 // the queue.
    127 JS_PUBLIC_API bool HasRegularMicroTasks(JSContext* cx);
    128 
    129 // Returns the length of the regular microtask queue.
    130 JS_PUBLIC_API size_t GetRegularMicroTaskCount(JSContext* cx);
    131 
    132 // This is the global associated with the realm RunJSMicroTask expects to be
    133 // in.  Returns nullptr if a dead wrapper is found.
    134 JS_PUBLIC_API JSObject* GetExecutionGlobalFromJSMicroTask(JSMicroTask* entry);
    135 
    136 // To handle cases where the queue needs to be set aside for some reason
    137 // (mostly the Debugger API), we provide a Save and Restore API.
    138 //
    139 // When restoring the saved queue, the JSContext microtask queue must be
    140 // empty -- you cannot drop items by restoring over a non-empty queue
    141 // (so HasAnyMicroTasks must be false).
    142 class SavedMicroTaskQueue {
    143 public:
    144  SavedMicroTaskQueue() = default;
    145  virtual ~SavedMicroTaskQueue() = default;
    146  SavedMicroTaskQueue(const SavedMicroTaskQueue&) = delete;
    147  SavedMicroTaskQueue& operator=(const SavedMicroTaskQueue&) = delete;
    148 };
    149 
    150 // This will return nullptr (and set OutOfMemory) if the save operation
    151 // fails.
    152 JS_PUBLIC_API js::UniquePtr<SavedMicroTaskQueue> SaveMicroTaskQueue(
    153    JSContext* cx);
    154 JS_PUBLIC_API void RestoreMicroTaskQueue(
    155    JSContext* cx, js::UniquePtr<SavedMicroTaskQueue> savedQueue);
    156 
    157 // Via the following API functions various host defined data is exposed to the
    158 // embedder (see JobQueue::getHostDefinedData).
    159 //
    160 // These return true on success and false on failure. They return false if
    161 // there are any unwrapping issues (e.g., dead wrappers), and true with nullptr
    162 // if there just isn't any data.
    163 //
    164 // This disambiguates between no-data and the dead wrapper case
    165 JS_PUBLIC_API bool MaybeGetHostDefinedDataFromJSMicroTask(
    166    JSMicroTask* entry, MutableHandleObject out);
    167 JS_PUBLIC_API bool MaybeGetAllocationSiteFromJSMicroTask(
    168    JSMicroTask* entry, MutableHandleObject out);
    169 
    170 // In some circumstances an entry may not have host defined data but may
    171 // still have a host defined global;
    172 JS_PUBLIC_API JSObject* MaybeGetHostDefinedGlobalFromJSMicroTask(
    173    JSMicroTask* entry);
    174 
    175 JS_PUBLIC_API JSObject* MaybeGetPromiseFromJSMicroTask(JSMicroTask* entry);
    176 
    177 // Get the flow ID from a JS microtask for profiler markers.
    178 // This only returns false if entry has become a dead wrapper,
    179 // in which case the microtask doesn't run anyhow.
    180 JS_PUBLIC_API bool GetFlowIdFromJSMicroTask(JSMicroTask* entry, uint64_t* uid);
    181 
    182 }  // namespace JS
    183 
    184 #endif /* js_friend_MicroTask_h */