深入理解Isolate

大逗大人發表於2020-01-21

由於Dart是一種單執行緒模型語言,所以避免了多執行緒環境下產生的一系列降低執行效率的問題。但單執行緒模型卻有一個非常嚴重的問題,就是耗時任務的執行。當執行耗時任務時,會導致當前執行緒會被阻塞,從而無法繼續執行。這時候就需要非同步任務,而Dart提供了Isolate來執行非同步任務。

在剛開始學習Isolate時,以為它類似JavaC/CPP中的執行緒。但隨著學習的深入,發現Isolate遠比JavaC/CPP中的執行緒複雜。下面就來一窺究竟。

1、Isolate的使用

Dart中,Isolate的使用及通訊都較為複雜,主要是通過 Isolate.spawnIsolate.spawnUri來建立IsolateReceivePort來進行Isolate間通訊。下面就來看如何使用Isolate

1.1、Isolate單向通訊

先來看Isolate間的單向通訊,程式碼如下。

//在父Isolate中呼叫
Isolate isolate;
start() async {
  ReceivePort receivePort = ReceivePort();
  //建立子Isolate物件
  isolate = await Isolate.spawn(getMsg, receivePort.sendPort);
  //監聽子Isolate的返回資料
  receivePort.listen((data) {
    print('data:$data');
    receivePort.close();
    //關閉Isolate物件
    isolate?.kill(priority: Isolate.immediate);
    isolate = null;
  });
}
//子Isolate物件的入口函式,可以在該函式中做耗時操作
getMsg(sendPort) => sendPort.send("hello");
複製程式碼

執行程式碼後,就會輸出新建立Isolate物件返回的資料,如下。

深入理解Isolate

1.2、Isolate雙向通訊

再來看多個Isolate之間的通訊實現,程式碼如下。


//當前函式在父Isolate中
Future<dynamic> asyncFactoriali(n) async {
  //父Isolate對應的ReceivePort物件
  final response = ReceivePort();
  //建立一個子Isolate物件
  await Isolate.spawn(_isolate, response.sendPort);
  final sendPort = await response.first as SendPort;
  final answer = ReceivePort();
  //給子Isolate傳送資料
  sendPort.send([n, answer.sendPort]);
  return answer.first;
}

//子Isolate的入口函式,可以在該函式中做耗時操作
_isolate(SendPort initialReplyTo) async {
  //子Isolate對應的ReceivePort物件
  final port = ReceivePort();
  initialReplyTo.send(port.sendPort);
  final message = await port.first as List;
  final data = message[0] as int;
  final send = message[1] as SendPort;
  //給父Isolate的返回資料
  send.send(syncFactorial(data));
}

//執行程式碼
start() async {
  print("計算結果:${await asyncFactoriali(4)}");
}
start();
複製程式碼

通過在新建立的Isolate中計算並返回資料後,得到如下返回結果。

深入理解Isolate

通過上面程式碼,我們就可以能夠通過Isolate來執行非同步任務。下面再來看其具體實現原理。

2、isolate的建立與執行

先從下面的時序圖來看isolate是如何建立及執行的。

深入理解Isolate
還是比較複雜的,下面就從isolate的建立及執行兩方面來對上圖進行詳細介紹。

2.1、isolate的建立

首先來看isolate的建立,在上面例子中是通過Isolate.spawn來建立Isolate物件。

class Isolate {
  //宣告外部實現
  external static Future<Isolate> spawn<T>(
      void entryPoint(T message), T message,
      {bool paused: false,
      bool errorsAreFatal,
      SendPort onExit,
      SendPort onError,
      @Since("2.3") String debugName});
}
複製程式碼

這裡的external關鍵字主要是宣告spawn這個函式,具體實現由外部提供。在Dart中,該函式的具體實現是在isolate_patch.dart中。先來看spawn的具體實現。

@patch
class Isolate {
  @patch
  static Future<Isolate> spawn<T>(void entryPoint(T message), T message,
      {bool paused: false,
      bool errorsAreFatal,
      SendPort onExit,
      SendPort onError,
      String debugName}) async {
    // `paused` isn't handled yet.
    RawReceivePort readyPort;
    try {
      //該函式執行是非同步的
      _spawnFunction(
          readyPort.sendPort,
          script.toString(),
          entryPoint,
          message,
          paused,
          errorsAreFatal,
          onExit,
          onError,
          null,
          packageConfig,
          debugName);
      return await _spawnCommon(readyPort);
    } catch (e, st) {
      ...
    }
  }

  static Future<Isolate> _spawnCommon(RawReceivePort readyPort) {
    Completer completer = new Completer<Isolate>.sync();
    //監聽Isolate是否建立完畢,當子Isolate建立完畢後會通知父Isolate
    readyPort.handler = (readyMessage) {
      //關閉埠
      readyPort.close();
      if (readyMessage is List && readyMessage.length == 2) {//子Isolate建立成功
        SendPort controlPort = readyMessage[0];
        List capabilities = readyMessage[1];
        completer.complete(new Isolate(controlPort,
            pauseCapability: capabilities[0],
            terminateCapability: capabilities[1]));
      } else if (readyMessage is String) {...} else {...}
    };
    return completer.future;
  }
  ......
  //呼叫虛擬機器中的Isolate_spawnFunction函式
  static void _spawnFunction(
      SendPort readyPort,
      String uri,
      Function topLevelFunction,
      var message,
      bool paused,
      bool errorsAreFatal,
      SendPort onExit,
      SendPort onError,
      String packageRoot,
      String packageConfig,
      String debugName) native "Isolate_spawnFunction";

  ......
}
複製程式碼

這裡的_spawnFunction呼叫的是Dart VM中的Isolate_spawnFunction函式,該函式就是把Isolate物件的建立交給執行緒池執行,所以Isolate物件的建立是非同步的。這裡的執行緒池是在Dart VM初始化的時候建立的

[->third_party/dart/runtime/lib/isolate.cc]

DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 11) {
  ...
  if (closure.IsClosure()) {
    ...
      //非同步執行,thread_pool是一個執行緒池
      Dart::thread_pool()->Run<SpawnIsolateTask>(isolate, std::move(state));
      return Object::null();
    }
  }
  ...
  return Object::null();
}
複製程式碼

SpawnIsolateTask是一個類似Java中實現了Runable介面的類,在該類中主要是進行子Isolate物件的建立及執行,來看其具體實現。

[->third_party/dart/runtime/lib/isolate.cc]

//在子執行緒中執行
class SpawnIsolateTask : public ThreadPool::Task {
  void Run() override {
    ...
    // initialize_callback對應[->third_party/dart/runtime/bin/main.cc]中的OnIsolateInitialize函式
    // OnIsolateInitialize是在Dart VM初始化時設定的
    //[見小節2.2]
    Dart_InitializeIsolateCallback initialize_callback =
        Isolate::InitializeCallback();
    ...
    // Create a new isolate.
    char* error = nullptr;
    Isolate* isolate = nullptr;
    //在AOT編譯環境下,FLAG_enable_isolate_groups為true,否則為false
    //group及initialize_callback都是在虛擬機器初始化的時候設定的
    if (!FLAG_enable_isolate_groups || group == nullptr ||
        initialize_callback == nullptr) {
        ...
    } else {
      ...
      //先直接看AOT編譯下的Isolate建立
      isolate = CreateWithinExistingIsolateGroup(group, name, &error);
      ...
      void* child_isolate_data = nullptr;
      //將Isolate設定為可執行
      //[見2.2小節]
      bool success = initialize_callback(&child_isolate_data, &error);
    }
    //建立失敗
    if (isolate == nullptr) {
      FailedSpawn(error);
      free(error);
      return;
    }
    ...
    // isolate是否是可執行的
    // 是在OnIsolateInitialize中設定的
    if (isolate->is_runnable()) {
      //執行isolate
      //[見2.2小節]
      isolate->Run();
    }
  }
  
};
複製程式碼

AOT編譯下,在子執行緒中呼叫CreateWithinExistingIsolateGroup函式來建立Isolate物件。

[->third_party/dart/runtime/vm/dart_api_impl.cc]

Isolate* CreateWithinExistingIsolateGroup(IsolateGroup* group,
                                          const char* name,
                                          char** error) {
  //建立Isolate物件
  Isolate* isolate = reinterpret_cast<Isolate*>(
      CreateIsolate(group, name, /*isolate_data=*/nullptr, error));
  ...
  return isolate;
}
...
static Dart_Isolate CreateIsolate(IsolateGroup* group,
                                  const char* name,
                                  void* isolate_data,
                                  char** error) {

  auto source = group->source();
  Isolate* I = Dart::CreateIsolate(name, source->flags, group);
  ...

  Dart::ShutdownIsolate();
  return reinterpret_cast<Dart_Isolate>(NULL);
}
複製程式碼

經過一系列呼叫,最終呼叫dart.cc中的CreateIsolate函式,該函式很簡單,就是建立一個新的Isolate物件。

[->third_party/dart/runtime/vm/dart.cc]

Isolate* Dart::CreateIsolate(const char* name_prefix,
                             const Dart_IsolateFlags& api_flags,
                             IsolateGroup* isolate_group) {
  // Create a new isolate.
  Isolate* isolate =
      Isolate::InitIsolate(name_prefix, isolate_group, api_flags);
  return isolate;
}
複製程式碼

[->third_party/dart/runtime/vm/isolate.cc]

//初始化Isolate
Isolate* Isolate::InitIsolate(const char* name_prefix,
                              IsolateGroup* isolate_group,
                              const Dart_IsolateFlags& api_flags,
                              bool is_vm_isolate) {
  //1、建立一個Isolate物件
  Isolate* result = new Isolate(isolate_group, api_flags);
  ...
  //2、建立Isolate對應的堆空間,在該堆空間中,存在物件的分配,垃圾回收等。
  Heap::Init(result,
             is_vm_isolate
                 ? 0  // New gen size 0; VM isolate should only allocate in old.
                 : FLAG_new_gen_semi_max_size * MBInWords,//MBInWords值是128kb,
             (is_service_or_kernel_isolate ? kDefaultMaxOldGenHeapSize
                                           : FLAG_old_gen_heap_size) *
                 MBInWords);
  //3、將Isolate與Thread相關聯
  if (!Thread::EnterIsolate(result)) {
    // We failed to enter the isolate, it is possible the VM is shutting down,
    // return back a NULL so that CreateIsolate reports back an error.
    if (KernelIsolate::IsKernelIsolate(result)) {
      KernelIsolate::SetKernelIsolate(nullptr);
    }
    if (ServiceIsolate::IsServiceIsolate(result)) {
      ServiceIsolate::SetServiceIsolate(nullptr);
    }
    delete result;
    return nullptr;
  }

  // Setup the isolate message handler.
  //4、設定isolate的訊息處理器
  MessageHandler* handler = new IsolateMessageHandler(result);
  result->set_message_handler(handler);

  // Setup the Dart API state.
  //5、啟動Dart API狀態
  ApiState* state = new ApiState();
  result->set_api_state(state);
  
  //6、設定主埠
  result->set_main_port(PortMap::CreatePort(result->message_handler()));

  // Add to isolate list. Shutdown and delete the isolate on failure.
  //7、將當前的Isolate新增到連結串列中(一個單連結串列)
  if (!AddIsolateToList(result)) {
    //新增失敗,銷燬該Isolate
    result->LowLevelShutdown();
    //取消執行緒與Isolate的關聯
    Thread::ExitIsolate();
    //如果是虛擬機器內部的Isolate
    if (KernelIsolate::IsKernelIsolate(result)) {
      KernelIsolate::SetKernelIsolate(nullptr);
    }
    //如果是Service Isolate
    if (ServiceIsolate::IsServiceIsolate(result)) {
      ServiceIsolate::SetServiceIsolate(nullptr);
    }
    //刪除當前Isolate物件
    delete result;
    return nullptr;
  }

  return result;
}
複製程式碼

InitIsolate函式比較重要,主要做了以下事情。

  1. 建立Isolate物件
  2. 建立Isolate中的堆空間,在Isolate僅有一塊堆空間。存在堆空間也就會存在物件分配、垃圾回收等。
  3. Isolate物件與一個執行緒進行關聯,也就是可以說一個執行緒對應著一個Isolate物件。
  4. 設定訊息處理器(IsolateMessageHandler),主要是對於Isolate中的訊息處理。子Isolate可以通過埠向父IsolateMessageHandler中新增訊息,反之亦然。這也是Isolate間的通訊的實現。
  5. 設定api state,暫時沒搞懂這個是幹啥的。
  6. 設定主埠。
  7. 將當前Isolate新增到連結串列中。

當上面的一些操作執行完畢後,一個Isolate物件就建立成功了。

深入理解Isolate

2.2、isolate的執行

再回到SpawnIsolateTask類中,當呼叫CreateWithinExistingIsolateGroup建立Isolate成功後,也僅是建立了一個Isolate物件。這時候的Isolate並未執行,也不能執行該Isolate中的任何程式碼。所以還得主動呼叫IsolateRun函式,使Isolate能夠執行其中的程式碼並執行相應的訊息。

首先需要通過initialize_callback函式來將Isolate設定為可執行。initialize_callback函式在Dart VM初始化的時候設定,對應著[->third_party/dart/runtime/bin/main.cc]中的OnIsolateInitialize函式。把Isolate設定為可執行後,才可以執行Isolate

[->third_party/dart/runtime/vm/isolate.cc]

void Isolate::Run() {
  //向訊息處理器中新增的第一個訊息
  //記住該RunIsolate函式,在後面會說到
  message_handler()->Run(Dart::thread_pool(), RunIsolate, ShutdownIsolate,
                         reinterpret_cast<uword>(this));
}
複製程式碼

[->third_party/dart/runtime/vm/MessageHandler.cc]

void MessageHandler::Run(ThreadPool* pool,
                         StartCallback start_callback,
                         EndCallback end_callback,
                         CallbackData data) {
  MonitorLocker ml(&monitor_);
  pool_ = pool;
  start_callback_ = start_callback;
  end_callback_ = end_callback;
  callback_data_ = data;
  task_running_ = true;
  //線上程池中執行任務
  const bool launched_successfully = pool_->Run<MessageHandlerTask>(this);
}
複製程式碼

然後繼續非同步執行,但這次是在子Isolate中執行的。下面再來看MessageHandlerTask,在MessageHandlerTaskrun函式中執行的是TaskCallback函式。

[->third_party/dart/runtime/vm/message_handler.cc]

void MessageHandler::TaskCallback() {
  MessageStatus status = kOK;
  bool run_end_callback = false;
  bool delete_me = false;
  EndCallback end_callback = NULL;
  CallbackData callback_data = 0;
  {
    ...

    if (status == kOK) {
      //僅當子Isolate第一次執行時,start_callback_才不為null
      if (start_callback_ != nullptr) {
        ml.Exit();
        //呼叫Isolate的第一個函式(允許多執行緒併發執行)
        status = start_callback_(callback_data_);
        ASSERT(Isolate::Current() == NULL);
        start_callback_ = NULL;
        ml.Enter();
      }
      ...
    }

    ...
  }

  ...
}
複製程式碼

先不管訊息處理[見小結3],這裡重點來看start_callback_,它對應著RunIsolate這個函式。

[->third_party/dart/runtime/vm/isolate.cc]

//執行Isolate
static MessageHandler::MessageStatus RunIsolate(uword parameter) {
  ...
  {
    ...

    //args是呼叫Dart層_startIsolate函式所需的引數集合
    const Array& args = Array::Handle(Array::New(7));
    args.SetAt(0, SendPort::Handle(SendPort::New(state->parent_port())));
    args.SetAt(1, Instance::Handle(func.ImplicitStaticClosure()));
    args.SetAt(2, Instance::Handle(state->BuildArgs(thread)));
    args.SetAt(3, Instance::Handle(state->BuildMessage(thread)));
    args.SetAt(4, is_spawn_uri ? Bool::True() : Bool::False());
    args.SetAt(5, ReceivePort::Handle(ReceivePort::New(
                      isolate->main_port(), true /* control port */)));
    args.SetAt(6, capabilities);

    //呼叫Dart層的_startIsolate函式,該函式在isolate_patch.dart檔案中
    const Library& lib = Library::Handle(Library::IsolateLibrary());
    const String& entry_name = String::Handle(String::New("_startIsolate"));
    const Function& entry_point =
        Function::Handle(lib.LookupLocalFunction(entry_name));
    ASSERT(entry_point.IsFunction() && !entry_point.IsNull());

    result = DartEntry::InvokeFunction(entry_point, args);
    if (result.IsError()) {
      return StoreError(thread, Error::Cast(result));
    }
  }
  return MessageHandler::kOK;
}
複製程式碼

RunIsolate中,會呼叫isolate_patch.dart中的_startIsolate函式,從而呼叫建立Isolate物件時傳遞的初始化函式。

@pragma("vm:entry-point", "call")
void _startIsolate(
    SendPort parentPort,
    Function entryPoint,
    List<String> args,
    var message,
    bool isSpawnUri,
    RawReceivePort controlPort,
    List capabilities) {
  // The control port (aka the main isolate port) does not handle any messages.
  if (controlPort != null) {
    controlPort.handler = (_) {}; // Nobody home on the control port.
  }

  if (parentPort != null) {
    // Build a message to our parent isolate providing access to the
    // current isolate's control port and capabilities.
    //
    // TODO(floitsch): Send an error message if we can't find the entry point.
    var readyMessage = new List(2);
    readyMessage[0] = controlPort.sendPort;
    readyMessage[1] = capabilities;

    // Out of an excess of paranoia we clear the capabilities from the
    // stack.  Not really necessary.
    capabilities = null;
    //告訴父Isolate,當前`Isolate`已經建立成功
    parentPort.send(readyMessage);
  }

  // Delay all user code handling to the next run of the message loop. This
  // allows us to intercept certain conditions in the event dispatch, such as
  // starting in paused state.
  RawReceivePort port = new RawReceivePort();
  port.handler = (_) {
    port.close();

    if (isSpawnUri) {
      if (entryPoint is _BinaryFunction) {
        (entryPoint as dynamic)(args, message);
      } else if (entryPoint is _UnaryFunction) {
        (entryPoint as dynamic)(args);
      } else {
        entryPoint();
      }
    } else {
      //初始化函式
      entryPoint(message);
    }
  };
  // Make sure the message handler is triggered.
  port.sendPort.send(null);
}
複製程式碼

_startIsolate函式中主要是做了以下幾件事。

  1. 告訴父Isolate,子Isolate已經建立成功。
  2. 呼叫子Isolate的初始化函式,也就是入口函式。

到此,一個新的Isolate就已經建立完畢。在建立過程中,會從Dart SDK呼叫虛擬機器函式,然後在新的Isolate物件中通過非同步的方式呼叫入口函式。

注意:主Isolate的入口函式就是熟悉的main函式。

3、isolate之間的通訊原理

通過前面一節,知道了Dart是如何建立一個新的Isolate物件的。但也還是省略了很多東西的,比如子Isolate通知父Isolate的原理,也就是Isolate間的通訊原理。

3.1、ReceivePort與SendPort

Isolate給另外一個Isolate傳送訊息之前,需要先來熟悉ReceivePortSendPort。程式碼如下。

abstract class ReceivePort implements Stream {
  //宣告外部實現
  external factory ReceivePort();
}

//在isolate_patch.dart中
@patch
class ReceivePort {
  @patch
  factory ReceivePort() => new _ReceivePortImpl();

  @patch
  factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) {
    return new _ReceivePortImpl.fromRawReceivePort(rawPort);
  }
}

class _ReceivePortImpl extends Stream implements ReceivePort {
  _ReceivePortImpl() : this.fromRawReceivePort(new RawReceivePort());

  _ReceivePortImpl.fromRawReceivePort(this._rawPort) {
    _controller = new StreamController(onCancel: close, sync: true);
    _rawPort.handler = _controller.add;
  }
  //返回一個SendPort物件
  SendPort get sendPort {
    return _rawPort.sendPort;
  }
  //監聽傳送的訊息
  StreamSubscription listen(void onData(var message),
      {Function onError, void onDone(), bool cancelOnError}) {
    return _controller.stream.listen(onData,
        onError: onError, onDone: onDone, cancelOnError: cancelOnError);
  }

  ...
}

@patch
class RawReceivePort {
  @patch
  factory RawReceivePort([Function handler]) {
    _RawReceivePortImpl result = new _RawReceivePortImpl();
    result.handler = handler;
    return result;
  }
}
@pragma("vm:entry-point")
class _RawReceivePortImpl implements RawReceivePort {
  factory _RawReceivePortImpl() native "RawReceivePortImpl_factory";
  ...

  SendPort get sendPort {
    return _get_sendport();
  }

 ...

  /**** Internal implementation details ****/
  _get_id() native "RawReceivePortImpl_get_id";
  _get_sendport() native "RawReceivePortImpl_get_sendport";
  ...
}
複製程式碼

在程式碼中,一個ReceivePort物件包含一個RawReceivePort物件及SendPort物件。其中RawReceivePort物件是在虛擬機器中建立的,它對應著虛擬機器中的ReceivePort類。程式碼如下。

[->third_party/dart/runtime/lib.isolate.cc]

DEFINE_NATIVE_ENTRY(RawReceivePortImpl_factory, 0, 1) {
  ASSERT(
      TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
  //建立一個Entry物件並返回一個埠號。
  Dart_Port port_id = PortMap::CreatePort(isolate->message_handler());
  //建立ReceivePort物件
  return ReceivePort::New(port_id, false /* not control port */);
}
複製程式碼

在建立ReceivePort物件物件之前,首先會將當前Isolate中的MessageHandler物件新增到map中。這裡是一個全域性的map,在Dart VM初始化的時候建立,每個元素都是一個Entry物件,在Entry中,有一個MessageHandler物件,一個埠號及該埠的狀態。

  typedef struct {
    //埠號
    Dart_Port port;
    //訊息處理器
    MessageHandler* handler;
    //埠號狀態
    PortState state;
  } Entry;
複製程式碼

[->third_party/dart/runtime/vm/port.cc]

Dart_Port PortMap::CreatePort(MessageHandler* handler) {
  ...
  Entry entry;
  //分配一個埠號
  entry.port = AllocatePort();
  //設定訊息處理器
  entry.handler = handler;
  //埠號狀態
  entry.state = kNewPort;
  //查詢當前entry的位置
  intptr_t index = entry.port % capacity_;
  Entry cur = map_[index];
  // Stop the search at the first found unused (free or deleted) slot.
  //找到空閒或將要被刪除的Entry。
  while (cur.port != 0) {
    index = (index + 1) % capacity_;
    cur = map_[index];
  }

  if (map_[index].handler == deleted_entry_) {
    // Consuming a deleted entry.
    deleted_--;
  }
  //插入到map中
  map_[index] = entry;

  // Increment number of used slots and grow if necessary.
  used_++;
  //檢查是否需要擴容
  MaintainInvariants();

  ...
  //返回埠號
  return entry.port;
}
複製程式碼

注意: 這裡的map的初始容量是8,當達到容量的3/4時,會進行擴容,新的容量是舊的容量2倍。熟悉Java的就知道,這跟HashMap類似,初始容量為8,載入因子為0.75,擴容是指數級增長。

再來看ReceivePort物件的建立。

[->third_party/dart/runtime/vm/object.cc]

RawReceivePort* ReceivePort::New(Dart_Port id,
                                 bool is_control_port,
                                 Heap::Space space) {
  Thread* thread = Thread::Current();
  Zone* zone = thread->zone();
  const SendPort& send_port =
      //建立SendPort物件
      SendPort::Handle(zone, SendPort::New(id, thread->isolate()->origin_id()));

  ReceivePort& result = ReceivePort::Handle(zone);
  { 
    //建立ReceivePort物件
    RawObject* raw = Object::Allocate(ReceivePort::kClassId,//classId
                                      ReceivePort::InstanceSize(),//物件大小
                                      space);
    NoSafepointScope no_safepoint;
    result ^= raw;
    result.StorePointer(&result.raw_ptr()->send_port_, send_port.raw());
  }
  if (is_control_port) {
    //更新埠的狀態,設為kControlPort
    PortMap::SetPortState(id, PortMap::kControlPort);
  } else {
    //更新埠的狀態,設為kLivePort
    PortMap::SetPortState(id, PortMap::kLivePort);
  }
  return result.raw();
}
複製程式碼

[->third_party/dart/runtime/vm/object.cc]

RawSendPort* SendPort::New(Dart_Port id,
                           Dart_Port origin_id,
                           Heap::Space space) {
  SendPort& result = SendPort::Handle();
  { 
    //建立SendPort物件
    RawObject* raw =
        Object::Allocate(SendPort::kClassId, //classId
                         SendPort::InstanceSize(), //物件ID
                         space);
    NoSafepointScope no_safepoint;
    result ^= raw;
    result.StoreNonPointer(&result.raw_ptr()->id_, id);
    result.StoreNonPointer(&result.raw_ptr()->origin_id_, origin_id);
  }
  return result.raw();
}
複製程式碼

這裡建立物件時傳遞的classId是在Isolate物件初始化時註冊的,然後根據該classId來建立相應的物件。在這裡,ReceivePort對應著Dart SDK中的_RawReceivePortImpl物件,SendPort對應著Dart SDK中的_SendPortImpl物件。

也就是當建立ReceivePort物件時,會通過Dart VM來建立對應的_RawReceivePortImpl物件及SendPort對應的_SendPortImpl物件。

3.2、isolate間通訊

ReceivePort建立成功後,就可以通過呼叫_SendPortImplsend函式來傳送訊息。

@pragma("vm:entry-point")
class _SendPortImpl implements SendPort {
  ...
  /*--- public interface ---*/
  @pragma("vm:entry-point", "call")
  void send(var message) {
    _sendInternal(message);
  }

  ...

  // Forward the implementation of sending messages to the VM.
  void _sendInternal(var message) native "SendPortImpl_sendInternal_";
}
複製程式碼

_sendInternal的具體實現在Dart VM中。

[->third_party/dart/runtime/lib/isolate.cc]

DEFINE_NATIVE_ENTRY(SendPortImpl_sendInternal_, 0, 2) {
  ...

  //目標Isolate所對應埠號
  const Dart_Port destination_port_id = port.Id();
  const bool can_send_any_object = isolate->origin_id() == port.origin_id();

  if (ApiObjectConverter::CanConvert(obj.raw())) {//如果傳送訊息為null或者傳送訊息不是堆物件
    PortMap::PostMessage(
        Message::New(destination_port_id, obj.raw(), Message::kNormalPriority));
  } else {
    //建立一個MessageWriter物件——writer
    MessageWriter writer(can_send_any_object);
    // TODO(turnidge): Throw an exception when the return value is false?
    PortMap::PostMessage(writer.WriteMessage(obj, destination_port_id,
                                             Message::kNormalPriority));
  }
  return Object::null();
}
複製程式碼

[->third_party/dart/runtime/vm/port.cc]

bool PortMap::PostMessage(std::unique_ptr<Message> message,
                          bool before_events) {
  MutexLocker ml(mutex_);
  //在map中根據目標埠號尋找Entry所在的位置
  intptr_t index = FindPort(message->dest_port());
  if (index < 0) {
    return false;
  }
  //從map中拿到Entry物件並取出MessageHandler物件
  MessageHandler* handler = map_[index].handler;
  //這裡的handler是目標Isolate中的MessageHandler
  handler->PostMessage(std::move(message), before_events);
  return true;
}
複製程式碼

到這裡就已經成功將訊息加入到了目標IsolateMessageHandler中,成功完成了Isolate間訊息的傳遞,但還尚未對訊息進行處理。

再來看Isolate對於訊息的處理。

[->third_party/dart/runtime/vm/message_handler.cc]

void MessageHandler::PostMessage(std::unique_ptr<Message> message,
                                 bool before_events) {
  Message::Priority saved_priority;

  {
    MonitorLocker ml(&monitor_);
    ...

    saved_priority = message->priority();
    if (message->IsOOB()) {
      //加入到OOB型別訊息的佇列中
      oob_queue_->Enqueue(std::move(message), before_events);
    } else {
      //加入到普通訊息佇列中
      queue_->Enqueue(std::move(message), before_events);
    }
    if (paused_for_messages_) {
      ml.Notify();
    }

    if (pool_ != nullptr && !task_running_) {
      task_running_ = true;
      //非同步處理
      const bool launched_successfully = pool_->Run<MessageHandlerTask>(this);
    }
  }

  // Invoke any custom message notification.
  //如果自定義了訊息通知函式,那麼在訊息處理完畢後會呼叫該函式
  MessageNotify(saved_priority);
}
複製程式碼

PostMessage中主要是做了以下操作。

  1. 根據訊息級別將訊息加入到不同的佇列中。主要有OOB訊息及普通訊息兩個級別,OOB訊息在佇列oob_queue_中,普通訊息在佇列queue_中。OOB訊息級別高於普通訊息,會立即處理。
  2. 將一個訊息處理任務MessageHandlerTask加入到執行緒中。

這裡的執行緒池是在Dart VM建立的時候建立的,在Isolate執行時傳遞給MessageHandler的。

下面再來看MessageHandlerTask,在MessageHandlerTaskrun函式中執行的是TaskCallback函式。

[->third_party/dart/runtime/vm/message_handler.cc]

void MessageHandler::TaskCallback() {
  MessageStatus status = kOK;
  bool run_end_callback = false;
  bool delete_me = false;
  EndCallback end_callback = NULL;
  CallbackData callback_data = 0;
  {
    ...

    if (status == kOK) {
      ...
      bool handle_messages = true;
      while (handle_messages) {
        handle_messages = false;
        // Handle any pending messages for this message handler.
        if (status != kShutdown) {
          //處理訊息
          status = HandleMessages(&ml, (status == kOK), true);
        }
        if (status == kOK && HasLivePorts()) {
          handle_messages = CheckIfIdleLocked(&ml);
        }
      }
    }
    ...
  }

  ...
}
複製程式碼

訊息的處理是在HandleMessages函式中進行的。

[->third_party/dart/runtime/vm/message_handler.cc]

MessageHandler::MessageStatus MessageHandler::HandleMessages(
    MonitorLocker* ml,
    bool allow_normal_messages,
    bool allow_multiple_normal_messages) {
  ...
  //從佇列中獲取一個訊息,優先OOB訊息
  std::unique_ptr<Message> message = DequeueMessage(min_priority);
  //沒有訊息時退出迴圈,停止訊息的處理
  while (message != nullptr) {
  
    //獲取訊息的長度
    intptr_t message_len = message->Size();

    ...
    //獲取訊息級別
    Message::Priority saved_priority = message->priority();
    Dart_Port saved_dest_port = message->dest_port();
    MessageStatus status = kOK;
    {
      DisableIdleTimerScope disable_idle_timer(idle_time_handler);
      //訊息的處理
      status = HandleMessage(std::move(message));
    }
    ...
    //如果是已關閉狀態,將清除OOB型別訊息
    if (status == kShutdown) {
      ClearOOBQueue();
      break;
    }
    ...
    //繼續從佇列中獲取訊息
    message = DequeueMessage(min_priority);
  }
  return max_status;
}
複製程式碼

HandleMessages函式中會根據訊息的優先順序別來遍歷所有訊息並一一處理,直至處理完畢。具體訊息處理是在HandleMessage函式中進行的。該函式在其子類IsolateMessageHandler中實現。

[->third_party/dart/runtime/vm/isolate.cc]

MessageHandler::MessageStatus IsolateMessageHandler::HandleMessage(
    std::unique_ptr<Message> message) {
  ...
  //如果是普通訊息
  if (!message->IsOOB() && (message->dest_port() != Message::kIllegalPort)) {
    //呼叫Dart層的_lookupHandler函式,返回該函式在isolate_patch.dart中
    msg_handler = DartLibraryCalls::LookupHandler(message->dest_port());
    ...
  }

  ...

  MessageStatus status = kOK;
  if (message->IsOOB()) {//處理OOB訊息
    ...
  } else if (message->dest_port() == Message::kIllegalPort) {//處理OOB訊息,主要是處理延遲OOB訊息
    ...
  } else {//處理普通訊息
    ...
    //呼叫Dart層的_RawReceivePortImpl物件中的_handleMessage函式,該函式在isolate_patch.dart中
    const Object& result =
        Object::Handle(zone, DartLibraryCalls::HandleMessage(msg_handler, msg));
    if (result.IsError()) {
      status = ProcessUnhandledException(Error::Cast(result));
    } else {
      ...
    }
  }
  return status;
}

複製程式碼

在這裡先暫時不管OOB訊息的處理,來看普通訊息的處理。

  1. 首先呼叫Dart SDK中_RawReceivePortImpl物件的_lookupHandler函式,返回一個在建立_RawReceivePortImpl物件時註冊的一個自定義函式。
  2. 呼叫Dart SDK中_RawReceivePortImpl物件的_handleMessage函式並傳入1中返回的自定義函式,通過該自定義函式將訊息分發出去。

深入理解Isolate

至此,一個Isolate就已經成功的向另外一個Isolate成功傳送並接收訊息。而雙向通訊也很簡單,在父Isolate中建立一個埠,並在建立子Isolate時,將這個埠傳遞給子Isolate。然後在子Isolate呼叫其入口函式時也建立一個新埠,並通過父Isolate傳遞過來的埠把子Isolate建立的埠傳遞給父Isolate,這樣父Isolate與子Isolate分別擁有對方的一個埠號,從而實現了通訊。具體程式碼[見小節1.2]。

4、總結

主要是梳理了純Dart環境中Isolate的建立、執行及通訊的實現,內容比較多且枯燥。可以發現,IsolateJavac/c++中的執行緒複雜多了,比如有自己的堆。當然,Isolate也不僅僅只有上述的一些功能,還有程式碼的讀取、解析等,後面再一一梳理。

【參考資料】

Glossary of VM Terms

Dart asynchronous programming: Isolates and event loops

Introduction to Dart VM

相關文章