Flutter之Timer原理解析

大逗大人發表於2020-06-22

在開發中,Timer總是一定無法繞過的。通過它,我們可以來實現任務的輪詢、定時執行等。當然,由於一些原因,一些平臺中不建議使用Timer。在Android中,基本上就是不建議使用它,而是通過HandlerScheduledThreadPoolExecutor等來替代Timer。那如果在Flutter中尼?下面就來看Flutter中如何使用TimerTimer的實現原理。

1、Timer的使用

Flutter中,Timer是無處不在的,有直接使用其API,也有使用Timer的包裝類,如Future。但在本文,會通過Timer及其API來深入瞭解實現原理。先來看Timer的使用。

//任務的定時執行。延遲1秒後輸出f1
Timer(Duration(milliseconds: 1000), () {
  print("f1");
});
int count = 0;
//任務的週期性執行
Timer.periodic(Duration(milliseconds: 1000), (timer) {
  print("f2");
  count++;
  if (count == 3) {
    //當執行count=3時,取消timer中的任務
    timer.cancel();
  }
});
//非同步任務執行,輸出f3
Timer.run(() {
  print("f3");
});
複製程式碼

以上就是Timer的全部用法,主要是任務的定時執行、任務的週期性執行及任務的非同步執行。由於任務週期性執行的實現原理與任務定時執行的實現原理基本相同,所有Timer就主要分為定時任務的執行及非同步任務的執行兩中情況。

下面也就根據這兩種情況來分析Timer的實現原理。

2、Timer原理解析

由於無論那種任務型別都需要建立一個Timer物件,所以就先來看Timer物件的建立。


abstract class Timer {
  //延遲一定時間後執行callback
  factory Timer(Duration duration, void callback()) {
    if (Zone.current == Zone.root) {
      // No need to bind the callback. We know that the root's timer will
      // be invoked in the root zone.
      return Zone.current.createTimer(duration, callback);
    }
    return Zone.current
        .createTimer(duration, Zone.current.bindCallbackGuarded(callback));
  }
  //建立一個timer物件
  external static Timer _createTimer(Duration duration, void callback());
  //建立一個可輪詢timer物件
  external static Timer _createPeriodicTimer(
      Duration duration, void callback(Timer timer));
}
複製程式碼

在上面程式碼中,Timer建構函式中最會終呼叫_createTimer來建立一個_Timer物件。所以下面就來看_createTimer方法的具體實現。

@patch
class Timer {
  @patch
  static Timer _createTimer(Duration duration, void callback()) {
    // TODO(iposva): Remove _TimerFactory and use VMLibraryHooks exclusively.
    if (_TimerFactory._factory == null) {
      _TimerFactory._factory = VMLibraryHooks.timerFactory;
    }
    if (_TimerFactory._factory == null) {
      throw new UnsupportedError("Timer interface not supported.");
    }
    int milliseconds = duration.inMilliseconds;
    if (milliseconds < 0) milliseconds = 0;
    return _TimerFactory._factory(milliseconds, (_) {
      callback();
    }, false);
  }
}
複製程式碼

_createTimer中,最終呼叫的是_TimerFactory._factory方法。由於在Flutter的第一個isolate初始化成功後,會呼叫_setupHooks方法將_Timer._factory賦給_TimerFactory._factory。所以_createTimer中最終呼叫了_Timer._factory方法。

@pragma("vm:entry-point", "call")
_setupHooks() {
  VMLibraryHooks.timerFactory = _Timer._factory;
}
複製程式碼

_Timer._factory方法中,直接就是建立一個_timer物件,也就是在Timer的具體實現類是_Timer。下面就來看_Timer的具體實現程式碼。

class _Timer implements Timer {
  //訊息型別:表示需要取消event handler中已存在某個timer
  static const _NO_TIMER = -1;

  //根據傳送的值來區分訊息型別
  //訊息型別:表示非同步執行的timer
  static const _ZERO_EVENT = 1;
  //訊息型別:表示已經超時的timer
  static const _TIMEOUT_EVENT = null;

  //建立一個二叉堆,該堆按照喚醒時間進行排序。
  static final _heap = new _TimerHeap();
  //連結串列中的第一個Timer
  static _Timer _firstZeroTimer;
  //連結串列中的最後一個Timer
  static _Timer _lastZeroTimer;

  //使用id來對具有相同到期時間的Timer進行排序。
  //ID_MASK入隊後或計時器佇列為空時,將回收ID。
  static const _ID_MASK = 0x1fffffff;
  static int _idCount = 0;

  static RawReceivePort _receivePort;
  static SendPort _sendPort;
  static int _scheduledWakeupTime;

  static bool _handlingCallbacks = false;

  Function _callback; //timer觸發的回撥方法,如果timer已被取消,則為null
  int _wakeupTime; //喚醒時間
  final int _milliSeconds; //建立指定的持續時間
  final bool _repeating; //是否週期性
  var _indexOrNext; //如果Timer在_TimerHeap中,該值就是在該堆中的索引。如果是在連結串列中,則是當前Timer指向的下一個Timer
  int _id; //當前Timer所對應的id,如果到期時間相同,則根據此id來進行排序

  int _tick = 0; // Backing for [tick],

  //獲取下一個可用id
  static int _nextId() {
    var result = _idCount;
    _idCount = (_idCount + 1) & _ID_MASK;
    return result;
  }
  //建立一個Timer物件
  _Timer._internal(
      this._callback, this._wakeupTime, this._milliSeconds, this._repeating)
      : _id = _nextId();

  static Timer _createTimer(
      void callback(Timer timer), int milliSeconds, bool repeating) {
    //milliSeconds不能小於0,小於0也就意味著超時,需要立即執行。
    if (milliSeconds < 0) {
      milliSeconds = 0;
    }

    //獲取當前時間
    int now = VMLibraryHooks.timerMillisecondClock();
    //得到Timer的喚醒時間
    int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds);
    
    //建立一個Timer物件
    _Timer timer =
        new _Timer._internal(callback, wakeupTime, milliSeconds, repeating);
    //將新建立的Timer放到適當的結構中,並在必要時進行對應的通知。
    //如果Timer中是非同步任務,則加入到連結串列中,否則加入到二叉堆中
    timer._enqueue();
    return timer;
  }
  
  //通過工廠模式來建立一個timer
  factory _Timer(int milliSeconds, void callback(Timer timer)) {
    return _createTimer(callback, milliSeconds, false);
  }
  
  //通過工廠模式來建立一個週期性執行的timer
  factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) {
    return _createTimer(callback, milliSeconds, true);
  }
  
  //timer是否在二叉堆中
  bool get _isInHeap => _indexOrNext is int;

  //首先根據喚醒時間來排序,如果喚醒時間相同則根據timer的_id來排序
  int _compareTo(_Timer other) {
    int c = _wakeupTime - other._wakeupTime;
    if (c != 0) return c;
    return _id - other._id;
  }
  
  //判斷timer是否可使用,實際上就是判斷回撥方法是否為null
  bool get isActive => _callback != null;

  int get tick => _tick;

  //取消已經設定的timer,如果Timer存在於二叉堆中,則將其從堆中刪除。否則繼續保留在連結串列中,因為它們需要消耗相應的待處理訊息。
  void cancel() {
    _callback = null;
    //實際上只有存在於二叉堆中的Timer被刪除。連結串列中的Timer需要消耗其相應的喚醒訊息,以便將它們留在佇列中。
    if (!_isInHeap) return;
    bool update = _heap.isFirst(this);
    _heap.remove(this);
    if (update) {
      _notifyEventHandler();
    }
  }
  
  //主要是重新計算下一次的喚醒時間。僅會在週期性執行的Timer中呼叫,
  void _advanceWakeupTime() {
    //重新計算下一次喚醒時間。 對於已經超時的Timer,當前時間就是下一個喚醒時間。
    _id = _nextId();
    if (_milliSeconds > 0) {
      _wakeupTime += _milliSeconds;
    } else {
      _wakeupTime = VMLibraryHooks.timerMillisecondClock();
    }
  }

  //將Timer新增到二叉堆或者連結串列中,如果喚醒時間相同則按照先進先出的規則來取出
  void _enqueue() {
    if (_milliSeconds == 0) {
      if (_firstZeroTimer == null) {
        _lastZeroTimer = this;
        _firstZeroTimer = this;
      } else {
        _lastZeroTimer._indexOrNext = this;
        _lastZeroTimer = this;
      }
      // Every zero timer gets its own event.
      _notifyZeroHandler();
    } else {
      _heap.add(this);
      if (_heap.isFirst(this)) {
        _notifyEventHandler();
      }
    }
  }

  //對於包含非同步任務的timer,需要傳送一個訊息型別為_ZERO_EVENT的訊息。之所以訊息型別是_ZERO_EVENT,主要是為了區分EventHandler訊息(_TIMEOUT_EVENT訊息)。
  static void _notifyZeroHandler() {
    if (_sendPort == null) {
      _createTimerHandler();
    }
    _sendPort.send(_ZERO_EVENT);
  }

  //從連結串列中獲取即將執行的timer及二叉堆中到期時間小於_firstZeroTimer的timer
  static List _queueFromZeroEvent() {
    var pendingTimers = new List();
    
    //從二叉堆中查詢到期時間小於_firstZeroTimer的timer,並加入到一個List中
    var timer;
    while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) {
      timer = _heap.removeFirst();
      pendingTimers.add(timer);
    }
    //獲取連結串列中的第一個timer
    timer = _firstZeroTimer;
    _firstZeroTimer = timer._indexOrNext;
    timer._indexOrNext = null;
    pendingTimers.add(timer);
    return pendingTimers;
  }

  static void _notifyEventHandler() {
    if (_handlingCallbacks) {
      //如果正在進行timer的回撥處理,則不繼續向下執行
      return;
    }

    //如果不存在即將執行的timers,則關閉receive port
    if ((_firstZeroTimer == null) && _heap.isEmpty) {
      //沒有待處理的計時器,則關閉receive port並通知event handler。
      if (_sendPort != null) {
        _cancelWakeup();
        _shutdownTimerHandler();
      }
      return;
    } else if (_heap.isEmpty) {
      //如果二叉堆中不存在timer,則取消喚醒。
      _cancelWakeup();
      return;
    }

    //僅在請求的喚醒時間與預定的喚醒時間不同時傳送訊息。
    var wakeupTime = _heap.first._wakeupTime;
    if ((_scheduledWakeupTime == null) ||
        (wakeupTime != _scheduledWakeupTime)) {
      _scheduleWakeup(wakeupTime);
    }
  }

  //獲取已經超時的timer
  static List _queueFromTimeoutEvent() {
    var pendingTimers = new List();
    if (_firstZeroTimer != null) {
      //從二叉堆中獲取喚醒時間小於連結串列中第一個timer喚醒時間的timer,並將該timer新增到pendingTimers中
      var timer;
      while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) {
        timer = _heap.removeFirst();
        pendingTimers.add(timer);
      }
    } else {
      //從二叉堆中獲取已經到期的timer並新增到pendingTimers中
      var currentTime = VMLibraryHooks.timerMillisecondClock();
      var timer;
      while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) {
        timer = _heap.removeFirst();
        pendingTimers.add(timer);
      }
    }
    return pendingTimers;
  }

  static void _runTimers(List pendingTimers) {
    //如果目前沒有待處理的timer,那麼就有機會在新加入timer之前來重置_idCount
    if (_heap.isEmpty && (_firstZeroTimer == null)) {
      _idCount = 0;
    }

    //如果沒有待處理的timer,則結束方法的執行
    if (pendingTimers.length == 0) {
      return;
    }

    // Trigger all of the pending timers. New timers added as part of the
    // callbacks will be enqueued now and notified in the next spin at the
    // earliest.
    _handlingCallbacks = true;
    var i = 0;
    try {
      for (; i < pendingTimers.length; i++) {
        //獲取下一個timer
        var timer = pendingTimers[i];
        timer._indexOrNext = null;

        // One of the timers in the pending_timers list can cancel
        // one of the later timers which will set the callback to
        // null. Or the pending zero timer has been canceled earlier.
        if (timer._callback != null) {
          var callback = timer._callback;
          if (!timer._repeating) {
            //將timer標記為無效
            timer._callback = null;
          } else if (timer._milliSeconds > 0) {
            var ms = timer._milliSeconds;
            int overdue =
                VMLibraryHooks.timerMillisecondClock() - timer._wakeupTime;
            if (overdue > ms) {
              int missedTicks = overdue ~/ ms;
              timer._wakeupTime += missedTicks * ms;
              timer._tick += missedTicks;
            }
          }
          timer._tick += 1;
          
          //執行timer中註冊的回撥方法
          callback(timer);
          // Re-insert repeating timer if not canceled.
          //如果timer未取消,則重新插入連結串列或者二叉堆中
          if (timer._repeating && (timer._callback != null)) {
            //更新喚醒時間
            timer._advanceWakeupTime();
            timer._enqueue();
          }
          //執行微任務,僅限於非RootIsolate。
          var immediateCallback = _removePendingImmediateCallback();
          if (immediateCallback != null) {
            immediateCallback();
          }
        }
      }
    } finally {
      _handlingCallbacks = false;
      //重新向二叉堆或者連結串列中插入pendingTimers中還存在的timer
      for (i++; i < pendingTimers.length; i++) {
        var timer = pendingTimers[i];
        timer._enqueue();
      }
      _notifyEventHandler();
    }
  }

  static void _handleMessage(msg) {
    var pendingTimers;
    if (msg == _ZERO_EVENT) {
      //獲取包含非同步任務的timer
      pendingTimers = _queueFromZeroEvent();
    } else {
      _scheduledWakeupTime = null; // Consumed the last scheduled wakeup now.
      //獲取已經超時的timer
      pendingTimers = _queueFromTimeoutEvent();
    }
    //執行Timer的回撥方法
    _runTimers(pendingTimers);
    //如果當前沒有待執行的Timer,則通知event handler或者關閉port
    _notifyEventHandler();
  }

  //告訴event handler,在特定時間,當前isolated中的timer需要被喚醒
  static void _scheduleWakeup(int wakeupTime) {
    if (_sendPort == null) {
      _createTimerHandler();
    }
    VMLibraryHooks.eventHandlerSendData(null, _sendPort, wakeupTime);
    _scheduledWakeupTime = wakeupTime;
  }

  //取消event handler中等待喚醒的timer
  static void _cancelWakeup() {
    if (_sendPort != null) {
      VMLibraryHooks.eventHandlerSendData(null, _sendPort, _NO_TIMER);
      _scheduledWakeupTime = null;
    }
  }

  //建立一個receive port並註冊一個message handler
  static void _createTimerHandler() {
    assert(_sendPort == null);
    _receivePort = new RawReceivePort(_handleMessage);
    _sendPort = _receivePort.sendPort;
    _scheduledWakeupTime = null;
  }

  static void _shutdownTimerHandler() {
    _receivePort.close();
    _receivePort = null;
    _sendPort = null;
    _scheduledWakeupTime = null;
  }
  
  //建立_timer物件
  static Timer _factory(
      int milliSeconds, void callback(Timer timer), bool repeating) {
    if (repeating) {
      return new _Timer.periodic(milliSeconds, callback);
    }
    return new _Timer(milliSeconds, callback);
  }
}
複製程式碼

_Timer中,根據任務型別的不同,將timer新增到不同的資料結構中。如果是非同步任務,則會將timer新增到一個單連結串列中,根據FIFO的順序來執行;如果是定時任務,則會將timer新增到二叉堆中並根據喚醒時間來進行排序。

下面就先來看非同步任務執行的實現原理。

2.1、非同步任務的執行

根據上面程式碼。可以發現,包含非同步任務的timer是將timer新增到以Timer為節點的單連結串列中,再通過SendPort來傳送一個型別為_ZERO_EVENT的訊息。

那麼SendPort是如何傳送訊息的尼?這在Isolate的建立流程一文中做了詳細的介紹。其傳送訊息就是通過PostMessage函式來將訊息新增到Isolate對應的MessageHandler中,然後等待MessageHandler的處理。下面來看PostMessage函式的實現程式碼。

[->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 {
      //加入到OOB型別訊息的佇列中
      queue_->Enqueue(std::move(message), before_events);
    }
    if (paused_for_messages_) {
      ml.Notify();
    }
 
    //通過task_running_來防止短時間內多次重複執行
    if (pool_ != nullptr && !task_running_) {
      task_running_ = true;
      //交給執行緒池來非同步執行(非RootIsolate)
      const bool launched_successfully = pool_->Run<MessageHandlerTask>(this);
    }
  }

  //呼叫自定義的訊息通知
  MessageNotify(saved_priority);
}
複製程式碼

由於在FlutterRootIsolate中,pool_為null。所以在非RootIsolate中,訊息是通過執行緒池中的子執行緒來執行RawReceivePort物件建立時設定的回撥方法——_handleMessage

再來看MessageNotify函式,它的實現是在其子類IsolateMessageHandler中。

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

void IsolateMessageHandler::MessageNotify(Message::Priority priority) {
  if (priority >= Message::kOOBPriority) {
    //即使mutator執行緒繁忙,也要處理優先順序為OOB的訊息
    I->ScheduleInterrupts(Thread::kMessageInterrupt);
  }
  //獲取Isolate的message_notify_callback_的值
  Dart_MessageNotifyCallback callback = I->message_notify_callback();
  if (callback != nullptr) {
    // Allow the embedder to handle message notification.
    (*callback)(Api::CastIsolate(I));
  }
}
複製程式碼

一般情況下,callback為null,但RootIsolate卻例外。是因為在Flutter的Engine啟動過程中,也就是在RootIsolateMessageHandler初始化時,會給callback賦值。

[->third_party/tonic/dart_message_handler.cc]

void DartMessageHandler::Initialize(TaskDispatcher dispatcher) {
  //僅能呼叫一次
  task_dispatcher_ = dispatcher;
  //給RootIsolate的message_notify_callback_賦值
  Dart_SetMessageNotifyCallback(MessageNotifyCallback);
}
複製程式碼

也就是當呼叫callback時,對應的是MessageNotifyCallback函式的執行。

[->third_party/tonic/dart_message_handler.cc]


void DartMessageHandler::OnMessage(DartState* dart_state) {
  auto task_dispatcher_ = dart_state->message_handler().task_dispatcher_;

  auto weak_dart_state = dart_state->GetWeakPtr();
  //在Android中,任務交給UI執行緒中的loop來執行。
  //在iOS中,也是通過類似loop的訊息處理器來執行
  task_dispatcher_([weak_dart_state]() {
    if (auto dart_state = weak_dart_state.lock()) {
      dart_state->message_handler().OnHandleMessage(dart_state.get());
    }
  });
}

void DartMessageHandler::MessageNotifyCallback(Dart_Isolate dest_isolate) {
  auto dart_state = DartState::From(dest_isolate);
  //呼叫OnMessage函式
  dart_state->message_handler().OnMessage(dart_state);
}
複製程式碼

通過上面程式碼,可以發現在Android平臺的RootIsolate中,訊息的處理是通過UI執行緒中的loop來處理。從Android角度來看,就是通過handler來傳送一個訊息。

總而言之,timer中非同步任務的處理主要分為以下兩種情況。

  1. 在非RootIsolate中,是通過執行緒池獲取一個子執行緒來處理任務。
  2. RootIsolate中,如果是Android平臺,則通過UI執行緒中的loop來處理任務。如果是iOS平臺,則通過UI執行緒中的類似loop的訊息處理器來處理任務。

2.2、定時任務的執行

通過對_ZERO_EVENT訊息的處理來執行了timer中的非同步任務。那麼再來看定時任務的執行,該任務則是通過event handler來處理的。

Flutter之Dart虛擬機器啟動一文中說過,當Dart VM虛擬機器啟動時會建立一個名為event handler的子執行緒,並在該子執行緒中通過非同步IO來實現任務的執行。根據平臺不同,非同步IO的實現方式不同。在Android中,是通過Linuxepoll來實現的;在iOS中,是通過kqueue來實現的。

timer物件新增到二叉堆之後,會根據喚醒時間來排序,如果當前timer物件的喚醒時間最短,則會通知event handler。這裡的VMLibraryHooks.eventHandlerSendData是在Isolate初始化時賦值的,它對應著_EventHandler._sendData

@patch
class _EventHandler {
  @patch
  static void _sendData(Object sender, SendPort sendPort, int data)
      native "EventHandler_SendData";

  static int _timerMillisecondClock()
      native "EventHandler_TimerMillisecondClock";
}
複製程式碼

[->third_party/dart/runtime/bin/eventhandler.cc]

void FUNCTION_NAME(EventHandler_SendData)(Dart_NativeArguments args) {
  // Get the id out of the send port. If the handle is not a send port
  // we will get an error and propagate that out.
  Dart_Handle handle = Dart_GetNativeArgument(args, 1);
  Dart_Port dart_port;
  //拿到SendPort
  handle = Dart_SendPortGetId(handle, &dart_port);
  ...
  
  Dart_Handle sender = Dart_GetNativeArgument(args, 0);
  intptr_t id;
  if (Dart_IsNull(sender)) {
    id = kTimerId;
  } else {...}
  //拿到喚醒時間
  int64_t data = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2));
  //傳送訊息
  event_handler->SendData(id, dart_port, data);
}
複製程式碼

由於event_handler在不同系統有不同實現,所以這裡以Android為例。

[->third_party/dart/runtime/bin/eventhandler_android.cc]

//傳送資料
void EventHandlerImplementation::SendData(intptr_t id,
                                          Dart_Port dart_port,
                                          int64_t data) {
  WakeupHandler(id, dart_port, data);
}

void EventHandlerImplementation::WakeupHandler(intptr_t id,
                                               Dart_Port dart_port,
                                               int64_t data) {
  InterruptMessage msg;
  //訊息id
  msg.id = id;
  //傳遞的dart_port
  msg.dart_port = dart_port;
  //訊息需要傳遞的資料,在當前傳遞的是任務的喚醒時間
  msg.data = data;
  // WriteToBlocking will write up to 512 bytes atomically, and since our msg
  // is smaller than 512, we don't need a thread lock.
  // See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'.
  ASSERT(kInterruptMessageSize < PIPE_BUF);
  //訊息寫入
  intptr_t result =
      FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize);
  if (result != kInterruptMessageSize) {
    if (result == -1) {
      perror("Interrupt message failure:");
    }
    FATAL1("Interrupt message failure. Wrote %" Pd " bytes.", result);
  }
}

//處理拿到的事件
void EventHandlerImplementation::HandleEvents(struct epoll_event* events,
                                              int size) {
  bool interrupt_seen = false;
  for (int i = 0; i < size; i++) {
    if (events[i].data.ptr == NULL) {
      interrupt_seen = true;
    } else {
      DescriptorInfo* di =
          reinterpret_cast<DescriptorInfo*>(events[i].data.ptr);
      const intptr_t old_mask = di->Mask();
      const intptr_t event_mask = GetPollEvents(events[i].events, di);
      if ((event_mask & (1 << kErrorEvent)) != 0) {
        di->NotifyAllDartPorts(event_mask);
        UpdateEpollInstance(old_mask, di);
      } else if (event_mask != 0) {
        Dart_Port port = di->NextNotifyDartPort(event_mask);
        UpdateEpollInstance(old_mask, di);
        //通過訊息的dart_port來呼叫註冊的回撥方法
        DartUtils::PostInt32(port, event_mask);
      }
    }
  }
  if (interrupt_seen) {
    // Handle after socket events, so we avoid closing a socket before we handle
    // the current events.
    HandleInterruptFd();
  }
}
複製程式碼

通過epoll就能在指定的時間來處理事件,然後通過dart_port來找到對應的MessageHandler並處理。

[->third_party/dart/runtime/bin/dartutils.cc]

bool DartUtils::PostInt32(Dart_Port port_id, int32_t value) {
  // Post a message with the integer value.
  int32_t min = 0xc0000000;  // -1073741824
  int32_t max = 0x3fffffff;  // 1073741823
  ASSERT(min <= value && value < max);
  Dart_CObject object;
  object.type = Dart_CObject_kInt32;
  object.value.as_int32 = value;
  return Dart_PostCObject(port_id, &object);
}
複製程式碼

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

DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message) {
  return PostCObjectHelper(port_id, message);
}

static bool PostCObjectHelper(Dart_Port port_id, Dart_CObject* message) {
  ApiMessageWriter writer;
  std::unique_ptr<Message> msg =
      writer.WriteCMessage(message, port_id, Message::kNormalPriority);

  if (msg == nullptr) {
    return false;
  }

  // Post the message at the given port.
  return PortMap::PostMessage(std::move(msg));
}
複製程式碼

最終也就跟_ZERO_EVENT訊息的處理流程一樣,在當前isolateMessageHandler中呼叫建立RawReceivePort物件時設定的回撥方法——_handleMessage

2.3、回撥方法的執行

無論是非同步任務,還是需要延時執行的任務,最終執行的回撥方法都是_handleMessage。在該回撥方法中,會根據訊息型別來進行不同的區分,如果訊息型別是_ZERO_EVENT,則會從_queueFromZeroEvent取出對應的Timer物件並執行其回撥方法;否則就從_queueFromTimeoutEvent中取出對應timer物件並執行其回撥方法。

先來看_queueFromZeroEvent方法。

  static List _queueFromZeroEvent() {
    var pendingTimers = new List();
    
    //從二叉堆中查詢到期時間小於_firstZeroTimer的timer,並加入到一個List中
    var timer;
    while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) {
      timer = _heap.removeFirst();
      pendingTimers.add(timer);
    }
    //獲取連結串列中的第一個timer
    timer = _firstZeroTimer;
    _firstZeroTimer = timer._indexOrNext;
    timer._indexOrNext = null;
    pendingTimers.add(timer);
    return pendingTimers;
  }
複製程式碼

在該方法中,會將二叉堆中喚醒時間比連結串列中的第一個timer物件喚醒時間還短的timer物件加入到集合pendingTimers中,然後再將連結串列中的第一個timer物件加入到集合pendingTimers中。

再來看_queueFromTimeoutEvent方法。

  static List _queueFromTimeoutEvent() {
    var pendingTimers = new List();
    if (_firstZeroTimer != null) {
      //從二叉堆中獲取喚醒時間小於連結串列中第一個timer喚醒時間的timer,並將該timer新增到pendingTimers中
      var timer;
      while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) {
        timer = _heap.removeFirst();
        pendingTimers.add(timer);
      }
    } else {
      //從二叉堆中獲取已經到期的timer並新增到pendingTimers中
      var currentTime = VMLibraryHooks.timerMillisecondClock();
      var timer;
      while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) {
        timer = _heap.removeFirst();
        pendingTimers.add(timer);
      }
    }
    return pendingTimers;
  }
複製程式碼

在該方法中,也會將二叉堆中喚醒時間比連結串列中的第一個timer物件喚醒時間還短的timer物件加入到集合pendingTimers中。如果此時連結串列的第一個timer物件為空,則會將二叉堆Timer物件的喚醒時間與當前時間進行對比,如果喚醒時間小於當前當前時間,則將timer新增到集合pendingTimers中。

經過_queueFromZeroEvent_queueFromTimeoutEvent兩個方法,就獲取到了所有待執行的timer物件。然後呼叫_runTimers方法來執行所有待執行的timer物件。待_runTimers方法執行完畢後,還會呼叫_notifyEventHandler來通知event handler或者關閉port

再來看_runTimers方法的實現。

  static void _runTimers(List pendingTimers) {
    //如果目前沒有待處理的timer,那麼就有機會在新加入timer之前來重置_idCount
    if (_heap.isEmpty && (_firstZeroTimer == null)) {
      _idCount = 0;
    }

    //如果沒有待處理的timer,則結束方法的執行
    if (pendingTimers.length == 0) {
      return;
    }

    // Trigger all of the pending timers. New timers added as part of the
    // callbacks will be enqueued now and notified in the next spin at the
    // earliest.
    _handlingCallbacks = true;
    var i = 0;
    try {
      for (; i < pendingTimers.length; i++) {
        //獲取下一個timer
        var timer = pendingTimers[i];
        timer._indexOrNext = null;

        // One of the timers in the pending_timers list can cancel
        // one of the later timers which will set the callback to
        // null. Or the pending zero timer has been canceled earlier.
        if (timer._callback != null) {
          var callback = timer._callback;
          if (!timer._repeating) {
            //將timer標記為無效
            timer._callback = null;
          } else if (timer._milliSeconds > 0) {
            var ms = timer._milliSeconds;
            int overdue =
                VMLibraryHooks.timerMillisecondClock() - timer._wakeupTime;
            if (overdue > ms) {
              int missedTicks = overdue ~/ ms;
              timer._wakeupTime += missedTicks * ms;
              timer._tick += missedTicks;
            }
          }
          timer._tick += 1;
          
          //執行timer中註冊的回撥方法
          callback(timer);
          // Re-insert repeating timer if not canceled.
          //如果timer未取消,則重新插入連結串列或者二叉堆中
          if (timer._repeating && (timer._callback != null)) {
            //更新喚醒時間
            timer._advanceWakeupTime();
            timer._enqueue();
          }
          //執行微任務,僅限於非RootIsolate。
          var immediateCallback = _removePendingImmediateCallback();
          if (immediateCallback != null) {
            immediateCallback();
          }
        }
      }
    } finally {
      _handlingCallbacks = false;
      //重新向二叉堆或者連結串列中插入pendingTimers中還存在的timer
      for (i++; i < pendingTimers.length; i++) {
        var timer = pendingTimers[i];
        timer._enqueue();
      }
      _notifyEventHandler();
    }
  }
複製程式碼

在上面程式碼中,主要就是遍歷pendingTimers中的Timer物件,獲取Timer中的任務callback並執行,在上面示例中,就是輸出f1、f2及f3。如果是週期性任務,則會在callback執行完畢後更新喚醒時間並重新新增到連結串列或二叉堆中。如果在非RootIsolate中,還會執行微任務。如果最終pendingTimers中還存在未遍歷的Timer,則將這些Timer新增到連結串列或二叉堆中並通知event handler

3、總結

經過上面的分析,全面瞭解了Timer的使用及其實現原理。它的使用很簡單,實現原理也分為以下幾點。

  1. 如果是非同步任務,則通過isolate中的MessageHandler來處理。使用方式是呼叫Timerrun方法。
  2. 如果是定時任務或週期性任務,則通過event handler來處理並通過isolate中的MessageHandler來執行任務。使用方式是通過工廠模式建立Timer或者呼叫Timerperiodic方法。
  3. 如果在非RootIsolate中,Timer的任務執行完畢後都會執行微任務。

由於Future的非同步機制是通過Timer來實現的,所以瞭解了Timer的實現原理,也就知道了Future的非同步部分的實現原理。

相關文章