1. 程式人生 > >深入V8引擎-預設Platform之mac篇(1)

深入V8引擎-預設Platform之mac篇(1)

  又到了常規的堆砌程式碼湊文章字數環節,很多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的兩個屬性,官方的解釋依次如下。

1、Size of a page in bytes.  Must not be less than 1.
2、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的非同步原理相差無幾,但是深入原始碼的每一步還是挺噁心的,下一篇再來