• 深入V8引擎-初始化默认Platform


      本来寻思着写一篇"'Hello' + ', World'"是怎么从JS代码编译然后输出的,然而compile过程的复杂性远超我的想象,强上怕会走火入魔,还是老老实实先回家种田,找点咸鱼方法先写着。虽然说是咸鱼方法,但是V8任何一块拿出来都不简单,之前讲的Time模块说实话大概是属于源码里面幼儿园级别的,这次试试难一点的。

      V8的源码在本地编译完成后,会提供一个hello-world.cc的sample,里面有新手用户标准的初始化流程,如下。

    int main(int argc, char* argv[]) {
      // Initialize V8.
      // 这个方法在mac不作为
      v8::V8::InitializeICUDefaultLocation(argv[0]);
      // 读取指定名称的配置文件 也不用鸟
      v8::V8::InitializeExternalStartupData(argv[0]);
      // 生成一个默认的platform对象
      std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
      // 初始化刚才的platform
      v8::V8::InitializePlatform(platform.get());
      // V8的初始化
      v8::V8::Initialize();
    
      // ...
    }

      前两步不用去管,在入门阶段用不上。

      第三步是主要内容,探究生成的默认platform对象(当然也可以选择自己定制一个platform对象),这个类主要负责管理线程池、调用栈、事件队列等一些杂活。

      这一篇不会去深入方法一步一步走,里面内容太过于杂乱,跳来跳去的,先整体介绍一下所有涉及的类,有一个初步的印象(建议深入阅读所有基类的英文注释,解释的很明白)。

    Platform

      首先当然是核心类Platform,但这是一个基类,里面的大部分方法都是虚函数。

    /**
     * V8 Platform abstraction layer.
     *
     * The embedder has to provide an implementation of this interface before
     * initializing the rest of V8.
     */
    class Platform {};

      如果需要定制platform来初始化V8,需要继承这个类并实现那些方法。一般情况下当然可以V8默认提供的类,即DefaultPlatform。

    class DefaultPlatform : public Platform {
      public:
        // 接受一个枚举值、一个TracingController类的构造函数
        explicit DefaultPlatform(
          IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled,
          std::unique_ptr<v8::TracingController> tracing_controller = {});
        ~DefaultPlatform() override;
        // 设置线程池大小
        void SetThreadPoolSize(int thread_pool_size);
        // 初始化线程池、管理线程任务相关的方法
        void EnsureBackgroundTaskRunnerInitialized();
      private:
        // 最大线程池数量 默认为8
        static const int kMaxThreadPoolSize;
    
        int thread_pool_size_;
        IdleTaskSupport idle_task_support_;
        // 线程任务启动器
        std::shared_ptr<DefaultWorkerThreadsTaskRunner> worker_threads_task_runner_;
        // 工具类
        std::unique_ptr<TracingController> tracing_controller_;
        std::unique_ptr<PageAllocator> page_allocator_;
        // 计数方法 用的是之前介绍的Time模块
        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 {};

      只选了一些初始化相关的方法,其实内容远比这个要多。其中还定义了两个类似于Platform的基类变量,一个负责调用栈追踪,一个负责内存管理。

    TaskRunner/Thread

      接下来是任务执行者、线程,因为这两者基本上成对出现,所以放一起来看。

    // 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();
        // ...
    };

      这是最基础的Thread,其中定义并实现了Start等常规方法,也有一些虚函数需要继承去重新实现,除此之外还有一些静态方法。默认情况下,V8实现了一个类继承于Thread,位置十分的隐蔽,在默认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() override;
        private:
          DefaultWorkerThreadsTaskRunner* runner_;
        };
        // 获取下一个task
        std::unique_ptr<Task> GetNext();
    
        bool terminated_ = false;
        // task队列
        DelayedTaskQueue queue_;
        // 线程池
        std::vector<std::unique_ptr<WorkerThread>> thread_pool_;
        // 计数方法
        TimeFunction time_function_;
        std::atomic_int single_worker_thread_id_{0};
        uint32_t thread_pool_size_;
    };

      这里顺便把TaskRunner相关的内容也一并放出来,大部分内容可以看命名。内部类的初始化参数类型是外部类,V8完全把Thread、TaskRunner两个类绑起来了。

    Task

      这个只是一个简单基类,用来继承实现任务的。

    /**
     * A Task represents a unit of work.
     */
    class Task {
     public:
      virtual ~Task() = default;
      // 所有的task需要继承这个类并实现Run方法
      virtual void Run() = 0;
    };

      由于HelloWorld的sample并没有用到多线程,所以不存在Task类的实现,这里只能先关注概念。使用时,大概方法如下,写个伪代码演示下。

    class userTask : public Task {
      public:
        void Run() {
          // do something...
        };
    };
    
    void handleTask() {
      // 新建一个task
      auto task = new userTask();
      // 加入队列
      queue_.push_back(task);
      // 唤醒线程
      thread_.signal();
      // 线程处理task
      while(true) {
        if(queue_.empty()) break;
        auto task = queue_pop_back();
        task->Run();
      }
      // 线程等待唤醒
      thread_.wait();
    }

      过程跟其实libuv的异步操作差不多,感觉编程的套路也就那样,看多了源码或者有实际开发经验的都熟悉。

      

      这一篇就先介绍一些类(调用栈和内存管理先放着),了解后基本上V8中关于Platform的内容就差不多了。关于Thread、TaskRunner、Task三者的联系与运作,因为C++是速成的,没去了解这些东西的实际运用,所以暂时不在这里班门弄斧了。之前学Java的时候了解过线程,感觉无论是API的名字还是概念都差不多,有兴趣的可以自己去看看。

  • 相关阅读:
    每日算法
    每日算法
    搜索算法入门详解
    NLP
    每日算法
    每日算法
    Elasticsearch地理位置总结
    elasticsearch Geo Bounding Box Query
    elasticsearch Geo Distance Query
    Elasticsearch java API (23)查询 DSL Geo查询
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/10964223.html
Copyright © 2020-2023  润新知