Originally thought to write a “‘Hello’ + ‘, World'” is how to compile from the JS code and then output, but compile process complexity is far more than I imagined, strong afraid will go into magic, or honest first home farming, find some salty fish method to write first. Although it is said that the salt fish method, but V8 any piece out is not simple, before the Time module to tell the truth is probably belong to the source of the kindergarten level, this try a bit more difficult.

After compiling V8 source code locally, there will be a sample of hello-world.cc, which has the standard initialization process for novice users, as follows.

int main(int argc, char* argv[]) {
  // Initialize V8.
  // This method does not work on MAC
  v8::V8::InitializeICUDefaultLocation(argv[0]);
  // Read the configuration file with the specified name without bird
  v8::V8::InitializeExternalStartupData(argv[0]);
  // Generate a default Platform object
  std: :unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
  // Initialize platform
  v8::V8::InitializePlatform(platform.get());
  // Initialization of V8
  v8::V8::Initialize();

  // ...
}Copy the code
Don’t bother with the first two steps. You don’t need them in the introductory stage.

The third step is the main content, exploring the generated default Platform object (you can also choose to customize one yourself), which is responsible for managing thread pools, call stacks, event queues, and other chores.

This article will not go into the method step by step, the content is too messy, jump to jump, first introduce the whole of all the classes involved, have a preliminary impression (it is recommended to read the English notes of all the base classes, the explanation is very clear).



Platform

The first, of course, is the core class Platform, but this is a base class where most of the methods are virtual functions.

/** * V8 Platform abstraction layer. * * The embedder has to provide an implementation of this interface before * initializing the rest of V8. */
class Platform {};Copy the code
If you need to customize the platform to initialize V8, you need to inherit this class and implement those methods. In general, of course, V8 provides the default class, which is DefaultPlatform.

class DefaultPlatform : public Platform {
  public:
    // Accepts an enumerated value, a TracingController class constructor
    explicit DefaultPlatform(
      IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled,
      std: :unique_ptr<v8::TracingController> tracing_controller = {});
    ~DefaultPlatform() override;
    // Set the thread pool size
    void SetThreadPoolSize(int thread_pool_size);
    // Initialize thread pool, manage thread task-related methods
    void EnsureBackgroundTaskRunnerInitialized(a);
  private:
    The default maximum number of thread pools is 8
    static const int kMaxThreadPoolSize;

    int thread_pool_size_;
    IdleTaskSupport idle_task_support_;
    // Thread task initiator
    std: :shared_ptr<DefaultWorkerThreadsTaskRunner> worker_threads_task_runner_;
    / / tools
    std: :unique_ptr<TracingController> tracing_controller_;
    std: :unique_ptr<PageAllocator> page_allocator_;
    // The count method uses the Time module introduced earlier
    TimeFunction time_function_for_testing_;
};

/** * V8 Tracing controller. * * Can be implemented by an embedder to record trace events from V8. */
class TracingController {};

/** * A V8 memory page allocator. * * Can be implemented by an embedder to manage large host OS allocations. */
class PageAllocator {};Copy the code
I just picked some initiality-related methods, but there’s more to it than that. Two base class variables, similar to Platform, are also defined, one for call stack tracing and one for memory management.



TaskRunner/Thread

Then there’s the tasker, and then there’s the thread, because the two are basically paired together.

// Thread
//
// Thread objects are used for creating and running threads. When the start()
// method is called the new thread starts running the run() method in the new
// thread. The Thread object should not be deallocated before the thread has
// terminated.

class V8_BASE_EXPORT Thread {
  public:
    // Start new thread by calling the Run() method on the new thread.
    void Start(a);
    // ...
};Copy the code
This is the most basic Thread, which defines and implements general methods such as Start, as well as some virtual functions that need to be inherited to be re-implemented, and some static methods. By default, V8 implements a class that inherits Thread, which is hidden from the default TaskRunner private.

/** * A TaskRunner allows scheduling of tasks. The TaskRunner may still be used to * post tasks after the isolate gets destructed, but these tasks may not get * executed anymore. All tasks posted to a given TaskRunner will be invoked in * sequence. Tasks can be posted from any thread. */
class TaskRunner {};

class DefaultWorkerThreadsTaskRunner : public TaskRunner {
  public:
    using TimeFunction = double(*) (); DefaultWorkerThreadsTaskRunner(uint32_t thread_pool_size, TimeFunction time_function);
  private:
    class WorkerThread : public Thread {
    public:
      explicit WorkerThread(DefaultWorkerThreadsTaskRunner* runner);
      ~WorkerThread() override;

      // This thread attempts to get tasks in a loop from |runner_| and run them.
      void Run(a) override;
    private:
      DefaultWorkerThreadsTaskRunner* runner_;
    };
    // Get the next task
    std: :unique_ptr<Task> GetNext();

    bool terminated_ = false;
    / / the task queue
    DelayedTaskQueue queue_;
    / / thread pool
    std: :vector<std: :unique_ptr<WorkerThread>> thread_pool_;
    // count methods
    TimeFunction time_function_;
    std: :atomic_int single_worker_thread_id_{0};
    uint32_t thread_pool_size_;
};Copy the code
TaskRunner content is also included here, most of which can be seen in the name. The inner class’s initialization parameter type is the outer class, and V8 completely ties Thread and TaskRunner together.



Task

This is just a simple base class that inherits implementation tasks.

/** * A Task represents a unit of work. */
class Task {
 public:
  virtual ~Task() = default;
  // All tasks need to inherit this class and implement the Run method
  virtual void Run(a) = 0;
};Copy the code
Since the HelloWorld sample does not use multithreading, there is no implementation of the Task class, so we can only focus on the concept. When used, the general method is as follows, write a pseudo-code to demonstrate.

class userTask : public Task {
  public:
    void Run(a) {
      // do something...
    };
};

void handleTask(a) {
  // Create a task
  auto task = new userTask();
  // Join the queue
  queue_.push_back(task);
  // Wake up the thread
  thread_.signal();
  // Threads process tasks
  while(true) {
    if(queue_.empty()) break;
    auto task = queue_pop_back();
    task->Run();
  }
  // The thread is waiting to wake up
  thread_.wait();
}Copy the code
The process is similar to the asynchronous operation of libuv in fact, the routine of programming feels like that, read more source code or have actual development experience are familiar with.



This article will cover a few classes (call stack and memory management for now), but pretty much everything V8 has to offer about Platform. As for the connection and operation of Thread, TaskRunner and Task, I have not learned how to use these things in practice because C++ is a quick learning process. I learned about threads when I was learning Java before. I feel that both the name and concept of API are similar. If you are interested, you can have a look by yourself.