Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  OffThreadPromiseRuntimeState.h

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#ifndef vm_OffThreadPromiseRuntimeState_h
#define vm_OffThreadPromiseRuntimeState_h

#include <stddef.h>  // size_t

#include "jstypes.h"  // JS_PUBLIC_API

#include "ds/Fifo.h"           // js::Fifo
#include "ds/PriorityQueue.h"  // js::PriorityQueue
#include "js/AllocPolicy.h"    // js::SystemAllocPolicy
#include "js/HashTable.h"      // js::DefaultHasher, js::HashSet
#include "js/Promise.h"  // JS::Dispatchable, JS::Dispatchable::MaybeShuttingDown,
                         // JS::DispatchToEventLoopCallback,
                         // JS::DelayedDispatchToEventLoopCallback
#include "js/RootingAPI.h"                // JS::Handle, JS::PersistentRooted
#include "threading/ConditionVariable.h"  // js::ConditionVariable
#include "vm/PromiseObject.h"             // js::PromiseObject

struct JS_PUBLIC_API JSContext;
struct JS_PUBLIC_API JSRuntime;

namespace js {

class AutoLockHelperThreadState;
class OffThreadPromiseRuntimeState;

// [SMDOC] OffThreadPromiseTask: an off-main-thread task that resolves a promise
//
// An OffThreadPromiseTask is an abstract base class holding a JavaScript
// promise that will be resolved (fulfilled or rejected) with the results of a
// task possibly performed by some other thread. OffThreadPromiseTasks can be
// undispatched (meaning that they can be dropped if the JSContext owning the
// promise shuts down before the tasks resolves) or dispatched (meaning that
// shutdown should wait for the task to join).
//
// An OffThreadPromiseTask's lifecycle is as follows:
//
// - Some JavaScript native wishes to return a promise of the result of some
//   computation that might be performed by other threads (say, helper threads
//   or the embedding's I/O threads), so it creates a PromiseObject to represent
//   the result, and an OffThreadPromiseTask referring to it. After handing the
//   OffThreadPromiseTask to the code doing the actual work, the native is free
//   to return the PromiseObject to its caller.
//
// - When the computation is done, successfully or otherwise, it populates the
//   OffThreadPromiseTask—which is actually an instance of some concrete
//   subclass specific to the task—with the information needed to resolve the
//   promise, and calls OffThreadPromiseTask::dispatchResolveAndDestroy. This
//   enqueues a runnable on the JavaScript thread to which the promise belongs.
//
// - When it gets around to the runnable, the JavaScript thread calls the
//   OffThreadPromiseTask's `resolve` method, which the concrete subclass has
//   overriden to resolve the promise appropriately. This probably enqueues a
//   promise reaction job.
//
// - The JavaScript thread then deletes the OffThreadPromiseTask.
//
// During shutdown, the process is slightly different. Enqueuing runnables to
// the JavaScript thread begins to fail. Undispatched tasks are immediately
// destroyed. JSRuntime shutdown waits for all outstanding dispatched tasks
// to call their run methods, and then deletes them on the main thread,
// without calling `resolve`.
//
// For example, the JavaScript function WebAssembly.compile uses
// OffThreadPromiseTask to manage the result of a helper thread task, accepting
// binary WebAssembly code and returning a promise of a compiled
// WebAssembly.Module. It would like to do this compilation work on a helper
// thread. When called by JavaScript, WebAssembly.compile creates a promise,
// builds a CompileBufferTask (the OffThreadPromiseTask concrete subclass) to
// keep track of it, and then hands that to a helper thread. When the helper
// thread is done, successfully or otherwise, it calls the CompileBufferTask's
// dispatchResolveAndDestroy method, which enqueues a runnable to the JavaScript
// thread to resolve the promise and delete the CompileBufferTask.
// (CompileBufferTask actually implements PromiseHelperTask, which implements
// OffThreadPromiseTask; PromiseHelperTask is what our helper thread scheduler
// requires.)
//
// OffThreadPromiseTasks are not limited to use with helper threads. For
// example, a function returning a promise of the result of a network operation
// could provide the code collecting the incoming data with an
// OffThreadPromiseTask for the promise, and let the embedding's network I/O
// threads call dispatchResolveAndDestroy.
//
// OffThreadPromiseTask may also be used purely on the main thread, as a way to
// "queue a task" in HTML terms. Note that a "task" is not the same as a
// "microtask" and there are separate queues for tasks and microtasks that are
// drained at separate times in the browser. The task queue is implemented by
// the browser's main event loop. The microtask queue is implemented
// by JS::JobQueue, used for promises and gets drained before returning to
// the event loop. Thus OffThreadPromiseTask can only be used when the spec
// says "queue a task", as the WebAssembly APIs do. In some cases, like
// Atomics.waitAsync, the choice between queuing a task or a microtask depends
// on whether the promise is being resolved from the owning thread or another
// thread. In such cases, ExtractAndForget can be used from the owning thread to
// cancel the task and return the underlying promise, which can then be resolved
// the normal way.
//
// An OffThreadPromiseTask has a JSContext, and must be constructed and have its
// 'init' method called on that JSContext's thread. Once
// initialized, its dispatchResolveAndDestroy method may be called from any
// thread. Other than calling `ExtractAndForget`, or `DestroyUndispatchedTask`
// during shutdown, this is the only safe way to destruct an
// OffThreadPromiseTask; doing so ensures the OffThreadPromiseTask's destructor
// will run on the JSContext's thread, either from the event loop or during
// shutdown.
//
// OffThreadPromiseTask::dispatchResolveAndDestroy uses the
// JS::DispatchToEventLoopCallback provided by the embedding to enqueue
// runnables on the JavaScript thread. See the comments for
// DispatchToEventLoopCallback for details.

class OffThreadPromiseTask : public JS::Dispatchable {
  friend class OffThreadPromiseRuntimeState;

  JSRuntime* runtime_;
  JS::PersistentRooted<PromiseObject*> promise_;

  // Indicates that this is an undispatched cancellable task, which is a member
  // of the Cancellable list.  If cancellable is set to false, we can no longer
  // terminate the task early, this means it is no longer tracked by the
  // Cancellable list, and a dispatch has been attempted.
  bool cancellable_;

  void unregister(OffThreadPromiseRuntimeState& state);
  // Used when we want to reuse a lock for unregistration and deletion.
  void unregister(OffThreadPromiseRuntimeState& state,
                  const AutoLockHelperThreadState& lock);

 protected:
  OffThreadPromiseTask(JSContext* cx, JS::Handle<PromiseObject*> promise);

  // To be called by OffThreadPromiseTask and implemented by the derived class.
  virtual bool resolve(JSContext* cx, JS::Handle<PromiseObject*> promise) {
    MOZ_CRASH("Tasks should override resolve");
  };

  // JS::Dispatchable override implementation.
  // Runs the task, and ends with 'js_delete(this)'.
  void run(JSContext* cx, MaybeShuttingDown maybeShuttingDown) final;

  // JS::Dispatchable override implementation, moves ownership to
  // OffThreadPromiseRuntimeState's failed_ list, for cleanup on shutdown.
  void transferToRuntime() final;

  // To be called by `destroy` during shutdown and implemented by the derived
  // class (for undispatched tasks only). Gives the task a chance to clean up
  // before being deleted.
  virtual void prepareForCancel() {
    MOZ_CRASH("Undispatched tasks should override prepareForCancel");
  }

 public:
  ~OffThreadPromiseTask() override;

  void operator=(const OffThreadPromiseTask&) = delete;
  OffThreadPromiseTask(const OffThreadPromiseTask&) = delete;

  static void DestroyUndispatchedTask(OffThreadPromiseTask* task,
                                      OffThreadPromiseRuntimeState& state,
                                      const AutoLockHelperThreadState& lock);

  JSRuntime* runtime() { return runtime_; }

  // Calling `init` on an OffThreadPromiseTask informs the runtime that it must
  // wait on shutdown for this task to rejoin the active JSContext by calling
  // dispatchResolveAndDestroy().
  bool init(JSContext* cx);
  bool init(JSContext* cx, const AutoLockHelperThreadState& lock);

  // Cancellable initialization track the task in case it needs to be aborted
  // before it is dispatched. Cancellable tasks are owned by the cancellable_
  // hashMap until they are dispatched.
  static bool InitCancellable(JSContext* cx,
                              js::UniquePtr<OffThreadPromiseTask>&& task);
  static bool InitCancellable(JSContext* cx,
                              const AutoLockHelperThreadState& lock,
                              js::UniquePtr<OffThreadPromiseTask>&& task);

  // Remove the cancellable task from the runtime cancellable list and
  // call DispatchResolveAndDestroy with a newly created UniquePtr,
  // so that ownership moves to the embedding.
  //
  // If a task is never removed from the cancellable list, it is deleted on
  // shutdown without running.
  void removeFromCancellableListAndDispatch();
  void removeFromCancellableListAndDispatch(
      const AutoLockHelperThreadState& lock);

  // These first two methods will wrap a pointer in a uniquePtr for the purpose
  // of passing it's ownership eventually to the embedding. These are used by
  // WASM and PromiseHelperTask.
  void dispatchResolveAndDestroy();
  void dispatchResolveAndDestroy(const AutoLockHelperThreadState& lock);

  // An initialized OffThreadPromiseTask can be dispatched to an active
  // JSContext of its Promise's JSRuntime from any thread. Normally, this will
  // lead to resolve() being called on JSContext thread, given the Promise.
  // However, if shutdown interrupts, resolve() may not be called, though the
  // OffThreadPromiseTask will be destroyed on a JSContext thread.
  static void DispatchResolveAndDestroy(
      js::UniquePtr<OffThreadPromiseTask>&& task);
  static void DispatchResolveAndDestroy(
      js::UniquePtr<OffThreadPromiseTask>&& task,
      const AutoLockHelperThreadState& lock);

  static PromiseObject* ExtractAndForget(OffThreadPromiseTask* task,
                                         const AutoLockHelperThreadState& lock);
};

using OffThreadPromiseTaskSet =
    HashSet<OffThreadPromiseTask*, DefaultHasher<OffThreadPromiseTask*>,
            SystemAllocPolicy>;

using DispatchableFifo =
    Fifo<js::UniquePtr<JS::Dispatchable>, 0, SystemAllocPolicy>;

class DelayedDispatchable {
  js::UniquePtr<JS::Dispatchable> dispatchable_;
  mozilla::TimeStamp endTime_;

 public:
  DelayedDispatchable(DelayedDispatchable&& other)
      : dispatchable_(other.dispatchable()), endTime_(other.endTime()) {}

  DelayedDispatchable(js::UniquePtr<JS::Dispatchable>&& dispatchable,
                      mozilla::TimeStamp endTime)
      : dispatchable_(std::move(dispatchable)), endTime_(endTime) {}

  void operator=(DelayedDispatchable&& other) {
    dispatchable_ = other.dispatchable();
    endTime_ = other.endTime();
  }
  js::UniquePtr<JS::Dispatchable> dispatchable() {
    return std::move(dispatchable_);
  }
  mozilla::TimeStamp endTime() const { return endTime_; }

  static bool higherPriority(const DelayedDispatchable& a,
                             const DelayedDispatchable& b) {
    return a.endTime_ < b.endTime_;
  }
};

using DelayedDispatchablePriorityQueue =
    PriorityQueue<DelayedDispatchable, DelayedDispatchable, 0,
                  SystemAllocPolicy>;

class OffThreadPromiseRuntimeState {
  friend class OffThreadPromiseTask;

  // These fields are initialized once before any off-thread usage and thus do
  // not require a lock.
  JS::DispatchToEventLoopCallback dispatchToEventLoopCallback_;
  JS::DelayedDispatchToEventLoopCallback delayedDispatchToEventLoopCallback_;
  JS::AsyncTaskStartedCallback asyncTaskStartedCallback_;
  JS::AsyncTaskFinishedCallback asyncTaskFinishedCallback_;
  void* dispatchToEventLoopClosure_;

#ifdef DEBUG
  // Set to true when the JS shell is force-quitting.
  // In this case the tasks won't be drained and the destructor cannot
  // assert anything.
  HelperThreadLockData<bool> forceQuitting_;
#endif

  // A set of all OffThreadPromiseTasks that have successfully called 'init'.
  // This set doesn't own tasks. OffThreadPromiseTask's destructor decrements
  // this counter.
  HelperThreadLockData<size_t> numRegistered_;

  // Currently, we have a subset of tasks which are not registered with
  // OffThreadPromiseRuntimeState (as they do not contain a promise), but
  // still depend on the internalDispatchQueue. These are JS::Dispatchables
  // that do not inherit from OffThreadPromiseTask, namely
  // WaitAsyncTimeoutTask. As a result, our assertions are broken. In order
  // to handle these non-registered tasks, we keep a separate counter for
  // dispatchables that are passed from the delayed dispatch queue to the
  // internal dispatch queue.
  //
  // This is a temporary fix that allows us to track these tasks.
  // TODO: remove this once we clean up this behavior.
  HelperThreadLockData<size_t> numDelayed_;

  // The cancellable hashmap tracks the registered, but thusfar
  // undispatched tasks. Not all undispatched tasks are cancellable, namely
  // webassembly helpers are held in an undispatched state, but are not
  // cancellable. Until the task is dispatched, this hashmap acts as the owner
  // of the task. In order to place something in the cancellable list, use
  // InitCancellable. Any task owned by the cancellable list cannot be
  // dispatched using DispatchResolveAndDestroy. Instead, use the method on
  // OffThreadPromiseTask removeFromCancellableAndDispatch.
  HelperThreadLockData<OffThreadPromiseTaskSet> cancellable_;

  // This list owns tasks that have failed to dispatch or failed to execute.
  // The list is cleared on shutdown.
  HelperThreadLockData<DispatchableFifo> failed_;

  // The allFailed_ condition is waited on and notified during engine
  // shutdown, communicating when all off-thread tasks in failed_ are safe to be
  // destroyed from the (shutting down) main thread. This condition is met when
  // numRegistered_ == failed().count(), where the collection of failed tasks
  // mean "the DispatchToEventLoopCallback failed after this task was dispatched
  // for execution".
  HelperThreadLockData<ConditionVariable> allFailed_;

  // The queue of JS::Dispatchables used by the DispatchToEventLoopCallback that
  // calling js::UseInternalJobQueues installs.
  HelperThreadLockData<DispatchableFifo> internalDispatchQueue_;
  HelperThreadLockData<ConditionVariable> internalDispatchQueueAppended_;
  HelperThreadLockData<bool> internalDispatchQueueClosed_;
  HelperThreadLockData<DelayedDispatchablePriorityQueue>
      internalDelayedDispatchPriorityQueue_;

  ConditionVariable& allFailed() { return allFailed_.ref(); }

  DispatchableFifo& failed() { return failed_.ref(); }
  OffThreadPromiseTaskSet& cancellable() { return cancellable_.ref(); }

  DispatchableFifo& internalDispatchQueue() {
    return internalDispatchQueue_.ref();
  }
  ConditionVariable& internalDispatchQueueAppended() {
    return internalDispatchQueueAppended_.ref();
  }
  DelayedDispatchablePriorityQueue& internalDelayedDispatchPriorityQueue() {
    return internalDelayedDispatchPriorityQueue_.ref();
  }

  void dispatchDelayedTasks();

  static bool internalDispatchToEventLoop(void*,
                                          js::UniquePtr<JS::Dispatchable>&&);
  static bool internalDelayedDispatchToEventLoop(
      void*, js::UniquePtr<JS::Dispatchable>&&, uint32_t);
  bool usingInternalDispatchQueue() const;

  // Used by OffThreadPromiseTask
  void registerTask(JSContext* cx, OffThreadPromiseTask* task);
  void unregisterTask(OffThreadPromiseTask* task);

 public:
  OffThreadPromiseRuntimeState();
  ~OffThreadPromiseRuntimeState();

  void operator=(const OffThreadPromiseRuntimeState&) = delete;
  OffThreadPromiseRuntimeState(const OffThreadPromiseRuntimeState&) = delete;

  void init(JS::DispatchToEventLoopCallback dispatchCallback,
            JS::DelayedDispatchToEventLoopCallback delayedDispatchCallback,
            JS::AsyncTaskStartedCallback asyncTaskStartedCallback,
            JS::AsyncTaskFinishedCallback asyncTaskFinishedCallback,
            void* closure);
  void initInternalDispatchQueue();
  bool initialized() const;

  // If initInternalDispatchQueue() was called, internalDrain() can be
  // called to periodically drain the dispatch queue before shutdown.
  void internalDrain(JSContext* cx);
  bool internalHasPending();
  bool internalHasPending(AutoLockHelperThreadState& lock);

  void stealFailedTask(JS::Dispatchable* dispatchable);

  bool dispatchToEventLoop(js::UniquePtr<JS::Dispatchable>&& dispatchable);
  bool delayedDispatchToEventLoop(
      js::UniquePtr<JS::Dispatchable>&& dispatchable, uint32_t delay);

  void cancelTasks(JSContext* cx);
  void cancelTasks(AutoLockHelperThreadState& lock, JSContext* cx);

  // shutdown() must be called by the JSRuntime while the JSRuntime is valid.
  void shutdown(JSContext* cx);

#ifdef DEBUG
  void setForceQuitting() { forceQuitting_ = true; }
#endif
};

}  // namespace js

#endif  // vm_OffThreadPromiseRuntimeState_h

Messung V0.5 in Prozent
C=84 H=88 G=85

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002