又到了常规的堆砌代码凑文章字数环节, 很多 API 我就直接贴官方的英文释义, 个人翻译其实有时候并不是很准确, 搞错了甚至会误导, 还是尽量自己去理解.
首先看看入口方法.
- std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
- // int 类型 thread_pool_size => 0
- // 枚举值 idle_task_support => kDisabled
- // 枚举值 in_process_stack_dumping => kDisabled
- // 类 tracing_controller => NULL
- std::unique_ptr<v8::Platform> NewDefaultPlatform(
- int thread_pool_size, IdleTaskSupport idle_task_support,
- InProcessStackDumping in_process_stack_dumping,
- std::unique_ptr<v8::TracingController> tracing_controller) {
- // 不会进这里
- if (in_process_stack_dumping == InProcessStackDumping::kEnabled) {
- v8::base::debug::EnableInProcessStackDumping();
- }
- // 1
- std::unique_ptr<DefaultPlatform> platform(new DefaultPlatform(idle_task_support, std::move(tracing_controller)));
- // 2
- platform->SetThreadPoolSize(thread_pool_size);
- // 3
- platform->EnsureBackgroundTaskRunnerInitialized();
- return std::move(platform);
- }
这里比较头疼的是我没找到 NewDefaultPlatform 方法是在哪里定义的, 所以默认参数不知道是什么, 只能打断点调试看变量值, 已经在注释标注了.
默认参数情况下, 那个 if 分支是不会进去的, 所以无视, 后面的三个语句都各自负责了一部门功能, 分别是初始化默认 Platform 对象, 设置线程池大小, 启动工作线程, 分块来看各部分源码.
第一条语句直接用 new 构造了一个 DefaultPlatform 对象, 如下, 加了一些标注.
- // 1
- DefaultPlatform::DefaultPlatform(
- IdleTaskSupport idle_task_support,
- std::unique_ptr<v8::TracingController> tracing_controller)
- : thread_pool_size_(0),
- idle_task_support_(idle_task_support),
- tracing_controller_(std::move(tracing_controller)),
- // 1-1
- page_allocator_(new v8::base::PageAllocator()),
- time_function_for_testing_(nullptr) {
- if (!tracing_controller_) {
- // 1-2
- tracing::TracingController* controller = new tracing::TracingController();
- // 1-3
- controller->Initialize(nullptr);
- // 智能指针替换
- tracing_controller_.reset(controller);
- }
- }
这是接受两个参数的构造函数, 而且 DefaultPlatform 只有这一个构造函数. 除了用给定的 2 个参数初始化属性, 一些其他属性也用默认的参数初始化了, 对于 0,nullptr 这种就不用管, 其中比较特殊的是那个 page_allocator 初始化, 上一篇给出的类声明是基类, 在 V8 的命名空间有一个同名的实现类.
- class PageAllocator : public ::v8::PageAllocator {
- // ...
- private:
- const size_t allocate_page_size_;
- const size_t commit_page_size_;
- }
- PageAllocator::PageAllocator()
- : allocate_page_size_(base::OS::AllocatePageSize()),
- commit_page_size_(base::OS::CommitPageSize()) {}
我也是服了 V8, 弄了个同名的类, 第一次看楞了好久. 构造函数调用的是 OS 命名空间的方法, 这个命名空间是用来取一些系统参数, 调用 Mac 系统上 < unistd.h > 头文件的一些 API, 看一下 allocate_page_size 的初始化就明白了.
- size_t OS::AllocatePageSize() {
- return static_cast<size_t>(sysconf(_SC_PAGESIZE));
- }
这里用了一个 sysconf 方法, 在其他的很多地方也有使用, 官方解释如下.
get configuration information at run time
当成 NODE_ENV 来理解就差不多了, 也就是一个获取系统配置参数的 API, 对于 PageAllocator 的两个属性, 官方的解释依次如下.
- ,Size of a page in bytes. Must not be Less than 1.
- ,memory page size
两个其实都是 page size(内存页大小, 关于 Linux 的内存模型我不太懂, 后面有空再去了解), 我本地测试了一下, 都返回的 4096.
对属性初始化完后, 构造函数会继续走代码块里的语句, 由于外部传进来的 tracing_controller 是个 NULL, 所以这里还需要手动 new 一个.
TracingController::TracingController() = default;
然而这个构造函数没啥好讲的, 因为是默认构造函数, 所以直接跳过了, 后面的两步也没什么讲的, reset 是智能指针的 API, 替换管理内容.
下面是第二条语句, 从命名直接能看出来了, 就是设置线程池的大小, 方法也比较简单暴力了.
- // 2
- void DefaultPlatform::SetThreadPoolSize(int thread_pool_size) {
- base::MutexGuard guard(&lock_);
- DCHECK_GE(thread_pool_size, 0);
- if (thread_pool_size <1) {
- // The number of processors currently online (available) => 4
- thread_pool_size = base::SysInfo::NumberOfProcessors() - 1;
- }
- // max(min(8, size), 1)
- thread_pool_size_ = std::max(std::min(thread_pool_size, kMaxThreadPoolSize), 1);
- }
这里实际上也是调用了一个类似于上面的 sysconf 来获取系统参数, 返回的系统处理器数量, 由于其中一个要用来作为主线程, 所以可用的线程池数量要减一, 简单处理一下返回一个 size.
第三条语句内容相当的麻烦, 看得我脑子疼, 语义上理解就是保证后台线程 runner 的初始化运行.
说得简单, 由于之前的操作只是初始化了一个空白 platform 类, 算了算线程池的大小, 所以剩下的所有实际操作都在这里. 大概包含了初始化线程池, 生成线程同时分配任务, 管理 task 队列, 启动线程等一系列操作, 其中的思想倒并不复杂, 实际上跟 libuv 的异步原理相差无几, 但是深入源码的每一步还是挺恶心的, 下一篇再来搞.
来源: https://www.cnblogs.com/QH-Jimmy/p/10968788.html