mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
632a37b5d5
The core underlying issue is that vector push_back could re-allocate and cause us to segfault. I have switched the backing queues to a map per @jason-simmons suggestion in flutter/flutter#38778. I've also added a test to capture the aforementioned bug. I've run internal tests several times to validate that this is fixed. General threading note for this class is that only the following operations take a write lock on the meta mutex: 1. Create 2. Dispose The rest of the operations take read lock on the meta mutex and acquire finer grained locks for the duration of the operation. We can not grab read lock for the entire duration of NotifyObservers for example because observer can in-turn create other queues -- Which we should not block. Additional changes: 1. Make as many methods as possible const. Unlocked methods are all const. 2. Migrate all the queue members to a struct, and have a map. 3. Get rid of the un-used Swap functionality.
143 lines
4.1 KiB
C++
143 lines
4.1 KiB
C++
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#define FML_USED_ON_EMBEDDER
|
|
|
|
#include "flutter/fml/message_loop_impl.h"
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "flutter/fml/build_config.h"
|
|
#include "flutter/fml/logging.h"
|
|
#include "flutter/fml/trace_event.h"
|
|
|
|
#if OS_MACOSX
|
|
#include "flutter/fml/platform/darwin/message_loop_darwin.h"
|
|
#elif OS_ANDROID
|
|
#include "flutter/fml/platform/android/message_loop_android.h"
|
|
#elif OS_LINUX
|
|
#include "flutter/fml/platform/linux/message_loop_linux.h"
|
|
#elif OS_WIN
|
|
#include "flutter/fml/platform/win/message_loop_win.h"
|
|
#endif
|
|
|
|
namespace fml {
|
|
|
|
fml::RefPtr<MessageLoopImpl> MessageLoopImpl::Create() {
|
|
#if OS_MACOSX
|
|
return fml::MakeRefCounted<MessageLoopDarwin>();
|
|
#elif OS_ANDROID
|
|
return fml::MakeRefCounted<MessageLoopAndroid>();
|
|
#elif OS_LINUX
|
|
return fml::MakeRefCounted<MessageLoopLinux>();
|
|
#elif OS_WIN
|
|
return fml::MakeRefCounted<MessageLoopWin>();
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
MessageLoopImpl::MessageLoopImpl()
|
|
: task_queue_(MessageLoopTaskQueues::GetInstance()),
|
|
queue_id_(task_queue_->CreateTaskQueue()),
|
|
terminated_(false) {
|
|
task_queue_->SetWakeable(queue_id_, this);
|
|
}
|
|
|
|
MessageLoopImpl::~MessageLoopImpl() {
|
|
task_queue_->Dispose(queue_id_);
|
|
}
|
|
|
|
void MessageLoopImpl::PostTask(fml::closure task, fml::TimePoint target_time) {
|
|
FML_DCHECK(task != nullptr);
|
|
FML_DCHECK(task != nullptr);
|
|
if (terminated_) {
|
|
// If the message loop has already been terminated, PostTask should destruct
|
|
// |task| synchronously within this function.
|
|
return;
|
|
}
|
|
task_queue_->RegisterTask(queue_id_, task, target_time);
|
|
}
|
|
|
|
void MessageLoopImpl::AddTaskObserver(intptr_t key, fml::closure callback) {
|
|
FML_DCHECK(callback != nullptr);
|
|
FML_DCHECK(MessageLoop::GetCurrent().GetLoopImpl().get() == this)
|
|
<< "Message loop task observer must be added on the same thread as the "
|
|
"loop.";
|
|
if (callback != nullptr) {
|
|
task_queue_->AddTaskObserver(queue_id_, key, callback);
|
|
} else {
|
|
FML_LOG(ERROR) << "Tried to add a null TaskObserver.";
|
|
}
|
|
}
|
|
|
|
void MessageLoopImpl::RemoveTaskObserver(intptr_t key) {
|
|
FML_DCHECK(MessageLoop::GetCurrent().GetLoopImpl().get() == this)
|
|
<< "Message loop task observer must be removed from the same thread as "
|
|
"the loop.";
|
|
task_queue_->RemoveTaskObserver(queue_id_, key);
|
|
}
|
|
|
|
void MessageLoopImpl::DoRun() {
|
|
if (terminated_) {
|
|
// Message loops may be run only once.
|
|
return;
|
|
}
|
|
|
|
// Allow the implementation to do its thing.
|
|
Run();
|
|
|
|
// The loop may have been implicitly terminated. This can happen if the
|
|
// implementation supports termination via platform specific APIs or just
|
|
// error conditions. Set the terminated flag manually.
|
|
terminated_ = true;
|
|
|
|
// The message loop is shutting down. Check if there are expired tasks. This
|
|
// is the last chance for expired tasks to be serviced. Make sure the
|
|
// terminated flag is already set so we don't accrue additional tasks now.
|
|
RunExpiredTasksNow();
|
|
|
|
// When the message loop is in the process of shutting down, pending tasks
|
|
// should be destructed on the message loop's thread. We have just returned
|
|
// from the implementations |Run| method which we know is on the correct
|
|
// thread. Drop all pending tasks on the floor.
|
|
task_queue_->DisposeTasks(queue_id_);
|
|
}
|
|
|
|
void MessageLoopImpl::DoTerminate() {
|
|
terminated_ = true;
|
|
Terminate();
|
|
}
|
|
|
|
void MessageLoopImpl::FlushTasks(FlushType type) {
|
|
TRACE_EVENT0("fml", "MessageLoop::FlushTasks");
|
|
std::vector<fml::closure> invocations;
|
|
|
|
task_queue_->GetTasksToRunNow(queue_id_, type, invocations);
|
|
|
|
for (const auto& invocation : invocations) {
|
|
invocation();
|
|
std::vector<fml::closure> observers =
|
|
task_queue_->GetObserversToNotify(queue_id_);
|
|
for (const auto& observer : observers) {
|
|
observer();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MessageLoopImpl::RunExpiredTasksNow() {
|
|
FlushTasks(FlushType::kAll);
|
|
}
|
|
|
|
void MessageLoopImpl::RunSingleExpiredTaskNow() {
|
|
FlushTasks(FlushType::kSingle);
|
|
}
|
|
|
|
TaskQueueId MessageLoopImpl::GetTaskQueueId() const {
|
|
return queue_id_;
|
|
}
|
|
|
|
} // namespace fml
|