v8  8.4.371 (node 14.19.3)
V8 is Google's open source JavaScript engine
v8-platform.h
Go to the documentation of this file.
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_V8_PLATFORM_H_
6 #define V8_V8_PLATFORM_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <stdlib.h> // For abort.
11 #include <memory>
12 #include <string>
13 
14 #include "v8config.h" // NOLINT(build/include_directory)
15 
16 namespace v8 {
17 
18 class Isolate;
19 
20 // Valid priorities supported by the task scheduling infrastructure.
21 enum class TaskPriority : uint8_t {
22  /**
23  * Best effort tasks are not critical for performance of the application. The
24  * platform implementation should preempt such tasks if higher priority tasks
25  * arrive.
26  */
28  /**
29  * User visible tasks are long running background tasks that will
30  * improve performance and memory usage of the application upon completion.
31  * Example: background compilation and garbage collection.
32  */
34  /**
35  * User blocking tasks are highest priority tasks that block the execution
36  * thread (e.g. major garbage collection). They must be finished as soon as
37  * possible.
38  */
40 };
41 
42 /**
43  * A Task represents a unit of work.
44  */
45 class Task {
46  public:
47  virtual ~Task() = default;
48 
49  virtual void Run() = 0;
50 };
51 
52 /**
53  * An IdleTask represents a unit of work to be performed in idle time.
54  * The Run method is invoked with an argument that specifies the deadline in
55  * seconds returned by MonotonicallyIncreasingTime().
56  * The idle task is expected to complete by this deadline.
57  */
58 class IdleTask {
59  public:
60  virtual ~IdleTask() = default;
61  virtual void Run(double deadline_in_seconds) = 0;
62 };
63 
64 /**
65  * A TaskRunner allows scheduling of tasks. The TaskRunner may still be used to
66  * post tasks after the isolate gets destructed, but these tasks may not get
67  * executed anymore. All tasks posted to a given TaskRunner will be invoked in
68  * sequence. Tasks can be posted from any thread.
69  */
70 class TaskRunner {
71  public:
72  /**
73  * Schedules a task to be invoked by this TaskRunner. The TaskRunner
74  * implementation takes ownership of |task|.
75  */
76  virtual void PostTask(std::unique_ptr<Task> task) = 0;
77 
78  /**
79  * Schedules a task to be invoked by this TaskRunner. The TaskRunner
80  * implementation takes ownership of |task|. The |task| cannot be nested
81  * within other task executions.
82  *
83  * Requires that |TaskRunner::NonNestableTasksEnabled()| is true.
84  */
85  virtual void PostNonNestableTask(std::unique_ptr<Task> task) {}
86 
87  /**
88  * Schedules a task to be invoked by this TaskRunner. The task is scheduled
89  * after the given number of seconds |delay_in_seconds|. The TaskRunner
90  * implementation takes ownership of |task|.
91  */
92  virtual void PostDelayedTask(std::unique_ptr<Task> task,
93  double delay_in_seconds) = 0;
94 
95  /**
96  * Schedules a task to be invoked by this TaskRunner. The task is scheduled
97  * after the given number of seconds |delay_in_seconds|. The TaskRunner
98  * implementation takes ownership of |task|. The |task| cannot be nested
99  * within other task executions.
100  *
101  * Requires that |TaskRunner::NonNestableDelayedTasksEnabled()| is true.
102  */
103  virtual void PostNonNestableDelayedTask(std::unique_ptr<Task> task,
104  double delay_in_seconds) {}
105 
106  /**
107  * Schedules an idle task to be invoked by this TaskRunner. The task is
108  * scheduled when the embedder is idle. Requires that
109  * |TaskRunner::IdleTasksEnabled()| is true. Idle tasks may be reordered
110  * relative to other task types and may be starved for an arbitrarily long
111  * time if no idle time is available. The TaskRunner implementation takes
112  * ownership of |task|.
113  */
114  virtual void PostIdleTask(std::unique_ptr<IdleTask> task) = 0;
115 
116  /**
117  * Returns true if idle tasks are enabled for this TaskRunner.
118  */
119  virtual bool IdleTasksEnabled() = 0;
120 
121  /**
122  * Returns true if non-nestable tasks are enabled for this TaskRunner.
123  */
124  virtual bool NonNestableTasksEnabled() const { return false; }
125 
126  /**
127  * Returns true if non-nestable delayed tasks are enabled for this TaskRunner.
128  */
129  virtual bool NonNestableDelayedTasksEnabled() const { return false; }
130 
131  TaskRunner() = default;
132  virtual ~TaskRunner() = default;
133 
134  TaskRunner(const TaskRunner&) = delete;
135  TaskRunner& operator=(const TaskRunner&) = delete;
136 };
137 
138 /**
139  * Delegate that's passed to Job's worker task, providing an entry point to
140  * communicate with the scheduler.
141  */
142 class JobDelegate {
143  public:
144  /**
145  * Returns true if this thread should return from the worker task on the
146  * current thread ASAP. Workers should periodically invoke ShouldYield (or
147  * YieldIfNeeded()) as often as is reasonable.
148  */
149  virtual bool ShouldYield() = 0;
150 
151  /**
152  * Notifies the scheduler that max concurrency was increased, and the number
153  * of worker should be adjusted accordingly. See Platform::PostJob() for more
154  * details.
155  */
156  virtual void NotifyConcurrencyIncrease() = 0;
157 };
158 
159 /**
160  * Handle returned when posting a Job. Provides methods to control execution of
161  * the posted Job.
162  */
163 class JobHandle {
164  public:
165  virtual ~JobHandle() = default;
166 
167  /**
168  * Notifies the scheduler that max concurrency was increased, and the number
169  * of worker should be adjusted accordingly. See Platform::PostJob() for more
170  * details.
171  */
172  virtual void NotifyConcurrencyIncrease() = 0;
173 
174  /**
175  * Contributes to the job on this thread. Doesn't return until all tasks have
176  * completed and max concurrency becomes 0. When Join() is called and max
177  * concurrency reaches 0, it should not increase again. This also promotes
178  * this Job's priority to be at least as high as the calling thread's
179  * priority.
180  */
181  virtual void Join() = 0;
182 
183  /**
184  * Forces all existing workers to yield ASAP. Waits until they have all
185  * returned from the Job's callback before returning.
186  */
187  virtual void Cancel() = 0;
188 
189  /**
190  * Returns true if associated with a Job and other methods may be called.
191  * Returns false after Join() or Cancel() was called.
192  */
193  virtual bool IsRunning() = 0;
194 };
195 
196 /**
197  * A JobTask represents work to run in parallel from Platform::PostJob().
198  */
199 class JobTask {
200  public:
201  virtual ~JobTask() = default;
202 
203  virtual void Run(JobDelegate* delegate) = 0;
204 
205  /**
206  * Controls the maximum number of threads calling Run() concurrently. Run() is
207  * only invoked if the number of threads previously running Run() was less
208  * than the value returned. Since GetMaxConcurrency() is a leaf function, it
209  * must not call back any JobHandle methods.
210  */
211  virtual size_t GetMaxConcurrency() const = 0;
212 };
213 
214 /**
215  * The interface represents complex arguments to trace events.
216  */
218  public:
219  virtual ~ConvertableToTraceFormat() = default;
220 
221  /**
222  * Append the class info to the provided |out| string. The appended
223  * data must be a valid JSON object. Strings must be properly quoted, and
224  * escaped. There is no processing applied to the content after it is
225  * appended.
226  */
227  virtual void AppendAsTraceFormat(std::string* out) const = 0;
228 };
229 
230 /**
231  * V8 Tracing controller.
232  *
233  * Can be implemented by an embedder to record trace events from V8.
234  */
236  public:
237  virtual ~TracingController() = default;
238 
239  // In Perfetto mode, trace events are written using Perfetto's Track Event
240  // API directly without going through the embedder. However, it is still
241  // possible to observe tracing being enabled and disabled.
242 #if !defined(V8_USE_PERFETTO)
243  /**
244  * Called by TRACE_EVENT* macros, don't call this directly.
245  * The name parameter is a category group for example:
246  * TRACE_EVENT0("v8,parse", "V8.Parse")
247  * The pointer returned points to a value with zero or more of the bits
248  * defined in CategoryGroupEnabledFlags.
249  **/
250  virtual const uint8_t* GetCategoryGroupEnabled(const char* name) {
251  static uint8_t no = 0;
252  return &no;
253  }
254 
255  /**
256  * Adds a trace event to the platform tracing system. These function calls are
257  * usually the result of a TRACE_* macro from trace_event_common.h when
258  * tracing and the category of the particular trace are enabled. It is not
259  * advisable to call these functions on their own; they are really only meant
260  * to be used by the trace macros. The returned handle can be used by
261  * UpdateTraceEventDuration to update the duration of COMPLETE events.
262  */
263  virtual uint64_t AddTraceEvent(
264  char phase, const uint8_t* category_enabled_flag, const char* name,
265  const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
266  const char** arg_names, const uint8_t* arg_types,
267  const uint64_t* arg_values,
268  std::unique_ptr<ConvertableToTraceFormat>* arg_convertables,
269  unsigned int flags) {
270  return 0;
271  }
272  virtual uint64_t AddTraceEventWithTimestamp(
273  char phase, const uint8_t* category_enabled_flag, const char* name,
274  const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args,
275  const char** arg_names, const uint8_t* arg_types,
276  const uint64_t* arg_values,
277  std::unique_ptr<ConvertableToTraceFormat>* arg_convertables,
278  unsigned int flags, int64_t timestamp) {
279  return 0;
280  }
281 
282  /**
283  * Sets the duration field of a COMPLETE trace event. It must be called with
284  * the handle returned from AddTraceEvent().
285  **/
286  virtual void UpdateTraceEventDuration(const uint8_t* category_enabled_flag,
287  const char* name, uint64_t handle) {}
288 #endif // !defined(V8_USE_PERFETTO)
289 
291  public:
292  virtual ~TraceStateObserver() = default;
293  virtual void OnTraceEnabled() = 0;
294  virtual void OnTraceDisabled() = 0;
295  };
296 
297  /** Adds tracing state change observer. */
299 
300  /** Removes tracing state change observer. */
302 };
303 
304 /**
305  * A V8 memory page allocator.
306  *
307  * Can be implemented by an embedder to manage large host OS allocations.
308  */
310  public:
311  virtual ~PageAllocator() = default;
312 
313  /**
314  * Gets the page granularity for AllocatePages and FreePages. Addresses and
315  * lengths for those calls should be multiples of AllocatePageSize().
316  */
317  virtual size_t AllocatePageSize() = 0;
318 
319  /**
320  * Gets the page granularity for SetPermissions and ReleasePages. Addresses
321  * and lengths for those calls should be multiples of CommitPageSize().
322  */
323  virtual size_t CommitPageSize() = 0;
324 
325  /**
326  * Sets the random seed so that GetRandomMmapAddr() will generate repeatable
327  * sequences of random mmap addresses.
328  */
329  virtual void SetRandomMmapSeed(int64_t seed) = 0;
330 
331  /**
332  * Returns a randomized address, suitable for memory allocation under ASLR.
333  * The address will be aligned to AllocatePageSize.
334  */
335  virtual void* GetRandomMmapAddr() = 0;
336 
337  /**
338  * Memory permissions.
339  */
340  enum Permission {
344  // TODO(hpayer): Remove this flag. Memory should never be rwx.
347  };
348 
349  /**
350  * Allocates memory in range with the given alignment and permission.
351  */
352  virtual void* AllocatePages(void* address, size_t length, size_t alignment,
353  Permission permissions) = 0;
354 
355  /**
356  * Frees memory in a range that was allocated by a call to AllocatePages.
357  */
358  virtual bool FreePages(void* address, size_t length) = 0;
359 
360  /**
361  * Releases memory in a range that was allocated by a call to AllocatePages.
362  */
363  virtual bool ReleasePages(void* address, size_t length,
364  size_t new_length) = 0;
365 
366  /**
367  * Sets permissions on pages in an allocated range.
368  */
369  virtual bool SetPermissions(void* address, size_t length,
370  Permission permissions) = 0;
371 
372  /**
373  * Frees memory in the given [address, address + size) range. address and size
374  * should be operating system page-aligned. The next write to this
375  * memory area brings the memory transparently back.
376  */
377  virtual bool DiscardSystemPages(void* address, size_t size) { return true; }
378 };
379 
380 /**
381  * V8 Platform abstraction layer.
382  *
383  * The embedder has to provide an implementation of this interface before
384  * initializing the rest of V8.
385  */
386 class Platform {
387  public:
388  virtual ~Platform() = default;
389 
390  /**
391  * Allows the embedder to manage memory page allocations.
392  */
394  // TODO(bbudge) Make this abstract after all embedders implement this.
395  return nullptr;
396  }
397 
398  /**
399  * Enables the embedder to respond in cases where V8 can't allocate large
400  * blocks of memory. V8 retries the failed allocation once after calling this
401  * method. On success, execution continues; otherwise V8 exits with a fatal
402  * error.
403  * Embedder overrides of this function must NOT call back into V8.
404  */
405  virtual void OnCriticalMemoryPressure() {
406  // TODO(bbudge) Remove this when embedders override the following method.
407  // See crbug.com/634547.
408  }
409 
410  /**
411  * Enables the embedder to respond in cases where V8 can't allocate large
412  * memory regions. The |length| parameter is the amount of memory needed.
413  * Returns true if memory is now available. Returns false if no memory could
414  * be made available. V8 will retry allocations until this method returns
415  * false.
416  *
417  * Embedder overrides of this function must NOT call back into V8.
418  */
419  virtual bool OnCriticalMemoryPressure(size_t length) { return false; }
420 
421  /**
422  * Gets the number of worker threads used by
423  * Call(BlockingTask)OnWorkerThread(). This can be used to estimate the number
424  * of tasks a work package should be split into. A return value of 0 means
425  * that there are no worker threads available. Note that a value of 0 won't
426  * prohibit V8 from posting tasks using |CallOnWorkerThread|.
427  */
428  virtual int NumberOfWorkerThreads() = 0;
429 
430  /**
431  * Returns a TaskRunner which can be used to post a task on the foreground.
432  * The TaskRunner's NonNestableTasksEnabled() must be true. This function
433  * should only be called from a foreground thread.
434  */
435  virtual std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner(
436  Isolate* isolate) = 0;
437 
438  /**
439  * Schedules a task to be invoked on a worker thread.
440  */
441  virtual void CallOnWorkerThread(std::unique_ptr<Task> task) = 0;
442 
443  /**
444  * Schedules a task that blocks the main thread to be invoked with
445  * high-priority on a worker thread.
446  */
447  virtual void CallBlockingTaskOnWorkerThread(std::unique_ptr<Task> task) {
448  // Embedders may optionally override this to process these tasks in a high
449  // priority pool.
450  CallOnWorkerThread(std::move(task));
451  }
452 
453  /**
454  * Schedules a task to be invoked with low-priority on a worker thread.
455  */
456  virtual void CallLowPriorityTaskOnWorkerThread(std::unique_ptr<Task> task) {
457  // Embedders may optionally override this to process these tasks in a low
458  // priority pool.
459  CallOnWorkerThread(std::move(task));
460  }
461 
462  /**
463  * Schedules a task to be invoked on a worker thread after |delay_in_seconds|
464  * expires.
465  */
466  virtual void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
467  double delay_in_seconds) = 0;
468 
469  /**
470  * Returns true if idle tasks are enabled for the given |isolate|.
471  */
472  virtual bool IdleTasksEnabled(Isolate* isolate) { return false; }
473 
474  /**
475  * Posts |job_task| to run in parallel. Returns a JobHandle associated with
476  * the Job, which can be joined or canceled.
477  * This avoids degenerate cases:
478  * - Calling CallOnWorkerThread() for each work item, causing significant
479  * overhead.
480  * - Fixed number of CallOnWorkerThread() calls that split the work and might
481  * run for a long time. This is problematic when many components post
482  * "num cores" tasks and all expect to use all the cores. In these cases,
483  * the scheduler lacks context to be fair to multiple same-priority requests
484  * and/or ability to request lower priority work to yield when high priority
485  * work comes in.
486  * A canonical implementation of |job_task| looks like:
487  * class MyJobTask : public JobTask {
488  * public:
489  * MyJobTask(...) : worker_queue_(...) {}
490  * // JobTask:
491  * void Run(JobDelegate* delegate) override {
492  * while (!delegate->ShouldYield()) {
493  * // Smallest unit of work.
494  * auto work_item = worker_queue_.TakeWorkItem(); // Thread safe.
495  * if (!work_item) return;
496  * ProcessWork(work_item);
497  * }
498  * }
499  *
500  * size_t GetMaxConcurrency() const override {
501  * return worker_queue_.GetSize(); // Thread safe.
502  * }
503  * };
504  * auto handle = PostJob(TaskPriority::kUserVisible,
505  * std::make_unique<MyJobTask>(...));
506  * handle->Join();
507  *
508  * PostJob() and methods of the returned JobHandle/JobDelegate, must never be
509  * called while holding a lock that could be acquired by JobTask::Run or
510  * JobTask::GetMaxConcurrency -- that could result in a deadlock. This is
511  * because [1] JobTask::GetMaxConcurrency may be invoked while holding
512  * internal lock (A), hence JobTask::GetMaxConcurrency can only use a lock (B)
513  * if that lock is *never* held while calling back into JobHandle from any
514  * thread (A=>B/B=>A deadlock) and [2] JobTask::Run or
515  * JobTask::GetMaxConcurrency may be invoked synchronously from JobHandle
516  * (B=>JobHandle::foo=>B deadlock).
517  *
518  * A sufficient PostJob() implementation that uses the default Job provided in
519  * libplatform looks like:
520  * std::unique_ptr<JobHandle> PostJob(
521  * TaskPriority priority, std::unique_ptr<JobTask> job_task) override {
522  * return std::make_unique<DefaultJobHandle>(
523  * std::make_shared<DefaultJobState>(
524  * this, std::move(job_task), kNumThreads));
525  * }
526  */
527  virtual std::unique_ptr<JobHandle> PostJob(
528  TaskPriority priority, std::unique_ptr<JobTask> job_task) {
529  return nullptr;
530  }
531 
532  /**
533  * Monotonically increasing time in seconds from an arbitrary fixed point in
534  * the past. This function is expected to return at least
535  * millisecond-precision values. For this reason,
536  * it is recommended that the fixed point be no further in the past than
537  * the epoch.
538  **/
539  virtual double MonotonicallyIncreasingTime() = 0;
540 
541  /**
542  * Current wall-clock time in milliseconds since epoch.
543  * This function is expected to return at least millisecond-precision values.
544  */
545  virtual double CurrentClockTimeMillis() = 0;
546 
547  typedef void (*StackTracePrinter)();
548 
549  /**
550  * Returns a function pointer that print a stack trace of the current stack
551  * on invocation. Disables printing of the stack trace if nullptr.
552  */
553  virtual StackTracePrinter GetStackTracePrinter() { return nullptr; }
554 
555  /**
556  * Returns an instance of a v8::TracingController. This must be non-nullptr.
557  */
559 
560  /**
561  * Tells the embedder to generate and upload a crashdump during an unexpected
562  * but non-critical scenario.
563  */
564  virtual void DumpWithoutCrashing() {}
565 
566  protected:
567  /**
568  * Default implementation of current wall-clock time in milliseconds
569  * since epoch. Useful for implementing |CurrentClockTimeMillis| if
570  * nothing special needed.
571  */
573 };
574 
575 } // namespace v8
576 
577 #endif // V8_V8_PLATFORM_H_