Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 js_friend_MicroTask_h
#define js_friend_MicroTask_h
#include "mozilla/Attributes.h"
#include "jstypes.h"
#include "js/GCPolicyAPI.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
#include "js/UniquePtr.h"
#include "js/ValueArray.h"
namespace JS {
// [SMDOC] MicroTasks in SpiderMonkey
//
// To enable higher performance, this header allows an embedding to work with
// a MicroTask queue stored inside the JS engine. This allows optimization of
// tasks by avoiding allocations in important cases.
//
// To make this work, we need some cooperation with the embedding.
//
// The high level thrust of this is that rather than managing the JobQueue
// themselves, embeddings assume that there's a JobQueue available to them
// inside the engine. When 'runJobs' happens, the embedding is responsible
// for pulling jobs of the queue, doing any setup required, then calling
// them.
//
// Embedding jobs are trivially supportable, since a MicroTask job is
// represented as a JS::Value, and thus an embedding job may be put on
// the queue by wrapping it in a JS::Value (e.g. using Private to store
// C++ pointers).
//
// The major requirement is that if a MicroTask identifies as a "JS"
// MicroTask, by passing the IsJSMicrotask predicate, the job must be
// run by calling RunJSMicroTask, while in the realm specified by the
// global returned by GetExecutionGlobalFromJSMicroTask, e.g
//
// AutoRealm ar(cx, JS::GetExecutionGlobalFromJSMicroTask(job));
// if (!JS::RunJSMicroTask(cx, job)) {
// ...
// }
// A MicroTask is a JS::Value. Using this MicroTask system allows
// embedders to put whatever pointer they would like into the queue.
// The task will be dequeued unchanged.
//
// The major requirement here is that if the MicroTask is a JS
// MicroTask (as determined by IsJSMicroTask), it must be run
// by calling RunJSMicroTask, while in the realm specified by
// GetExecutionGlobalFromJSMicroTask.
//
// An embedding is free to do with non-JS MicroTasks as it
// sees fit.
using MicroTask = JS::Value;
JS_PUBLIC_API bool IsJSMicroTask(Handle<JS::Value> hv);
// Run a MicroTask that is known to be a JS MicroTask. This will crash
// if provided an invalid task kind.
//
// This will return true except on OOM.
JS_PUBLIC_API bool RunJSMicroTask(JSContext* cx, Handle<MicroTask> entry);
// Queue Management. This is done per-JSContext.
//
// Internally we maintain two queues, one for 'debugger' microtasks. These
// are expected in normal operation to either be popped off the queue first,
// or processed separately.
//
// Non-debugger MicroTasks are "regular" microtasks, and go to the regular
// microtask queue.
//
// In general, we highly recommend that most embeddings use only the regular
// microtask queue. The debugger microtask queue mostly exists to support
// patterns used by Gecko.
JS_PUBLIC_API bool EnqueueMicroTask(JSContext* cx, const MicroTask& entry);
JS_PUBLIC_API bool EnqueueDebugMicroTask(JSContext* cx, const MicroTask& entry);
JS_PUBLIC_API bool PrependMicroTask(JSContext* cx, const MicroTask& entry);
// Dequeue the next MicroTask. If there are no MicroTasks of the appropriate
// kind, each of the below API returns JS::NullValue().
//
// The generic DequeueNext will always pull a debugger microtask first,
// if one exists, then a regular microtask if one exists.
// - DequeueNextDebuggerMicroTask only pulls from the debugger queue.
// - DequeueNextRegularMicroTask only pulls from the regular queue.
//
// Internally, these basically do
//
// if (HasXMicroTask()) { return X.popFront(); } return NullValue()
//
// so checking for emptiness before calling these is not required, and is
// very slightly less efficient.
JS_PUBLIC_API MicroTask DequeueNextMicroTask(JSContext* cx);
JS_PUBLIC_API MicroTask DequeueNextDebuggerMicroTask(JSContext* cx);
JS_PUBLIC_API MicroTask DequeueNextRegularMicroTask(JSContext* cx);
// Returns true if there are -any- microtasks pending in the queue.
JS_PUBLIC_API bool HasAnyMicroTasks(JSContext* cx);
// Returns true if there are any debugger microtasks pending in the queue.
JS_PUBLIC_API bool HasDebuggerMicroTasks(JSContext* cx);
// Returns true if there are any regular (non-debugger) microtasks pending in
// the queue.
JS_PUBLIC_API bool HasRegularMicroTasks(JSContext* cx);
// Returns the length of the regular microtask queue.
JS_PUBLIC_API size_t GetRegularMicroTaskCount(JSContext* cx);
// This is the global associated with the realm RunJSMicroTask expects to be in.
JS_PUBLIC_API JSObject* GetExecutionGlobalFromJSMicroTask(
const MicroTask& entry);
// To handle cases where the queue needs to be set aside for some reason
// (mostly the Debugger API), we provide a Save and Restore API.
//
// When restoring the saved queue, the JSContext microtask queue must be
// empty -- you cannot drop items by restoring over a non-empty queue
// (so HasAnyMicroTasks must be false).
class SavedMicroTaskQueue {
public:
SavedMicroTaskQueue() = default;
virtual ~SavedMicroTaskQueue() = default;
SavedMicroTaskQueue(const SavedMicroTaskQueue&) = delete;
SavedMicroTaskQueue& operator=(const SavedMicroTaskQueue&) = delete;
};
// This will return nullptr (and set OutOfMemory) if the save operation
// fails.
JS_PUBLIC_API js::UniquePtr<SavedMicroTaskQueue> SaveMicroTaskQueue(
JSContext* cx);
JS_PUBLIC_API void RestoreMicroTaskQueue(
JSContext* cx, js::UniquePtr<SavedMicroTaskQueue> savedQueue);
// Via the following API functions various host defined data is exposed to the
// embedder (see JobQueue::getHostDefinedData).
//
// All of these may return null if there's no data, or if there's a
// security error.
JS_PUBLIC_API JSObject* MaybeGetHostDefinedDataFromJSMicroTask(
const MicroTask& entry);
JS_PUBLIC_API JSObject* MaybeGetAllocationSiteFromJSMicroTask(
const MicroTask& entry);
// In some circumstances an entry may not have host defined data but may
// still have a host defined global;
JS_PUBLIC_API JSObject* MaybeGetHostDefinedGlobalFromJSMicroTask(
const MicroTask& entry);
JS_PUBLIC_API JSObject* MaybeGetPromiseFromJSMicroTask(const MicroTask& entry);
} // namespace JS
#endif /* js_friend_MicroTask_h */