This repository was archived by the owner on Feb 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6k
Add the functionality to merge and unmerge MessageLoopTaskQueues #9436
Merged
iskakaushik
merged 14 commits into
flutter:master
from
iskakaushik:introduce_merged_lock
Jul 12, 2019
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d917024
Add the functionality to merge and unmerge MessageLoopTaskQueues
3edf1a9
fix licenses
3930258
Expose TaskQueueId from TaskRunner interface
06b7c06
Merge branch 'master' into introduce_merged_lock
iskakaushik a5087fe
Merge branch 'master' of github.com:flutter/engine into introduce_mer…
a33cb8e
Fix wake-up and also add a method to check Owns
d1492d3
Merge branch 'master' of github.com:flutter/engine into check-if-runs…
997cbfc
fix merge conflicts for realz
f341bec
Merge branch 'check-if-runs-on-same-thread' into introduce_merged_lock
b897c5a
Fix scope of scoped locks and make queue_id a struct
608a9ee
Merge branch 'master' of github.com:flutter/engine into introduce_mer…
c9d4e82
fix benchmarks
926e9c2
fix some ownership semantics
3b96103
Zero out TaskQueueId
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| // 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_task_queues.h" | ||
|
|
||
| namespace fml { | ||
|
|
||
| // RAII class for managing merged locks. | ||
| class MessageLoopTaskQueues::MergedQueuesRunner { | ||
| public: | ||
| // TODO (kaushikiska): refactor mutexes out side of MessageLoopTaskQueues | ||
| // for better DI. | ||
| MergedQueuesRunner(MessageLoopTaskQueues& task_queues, | ||
| TaskQueueId owner, | ||
| MutexType type = MutexType::kTasks) | ||
| : owner_(owner), | ||
| subsumed_(task_queues_._kUnmerged), | ||
| task_queues_(task_queues), | ||
| type_(type) { | ||
| task_queues_.GetMutex(owner, type).lock(); | ||
| subsumed_ = task_queues_.owner_to_subsumed_[owner]; | ||
| if (isMerged(subsumed_)) { | ||
| task_queues_.GetMutex(subsumed_, type).lock(); | ||
| } | ||
| } | ||
|
|
||
| // First invokes on owner and then subsumed (if present). | ||
| void InvokeMerged(std::function<void(const TaskQueueId)> closure) { | ||
| closure(owner_); | ||
| if (isMerged(subsumed_)) { | ||
| closure(subsumed_); | ||
| } | ||
| } | ||
|
|
||
| ~MergedQueuesRunner() { | ||
| if (isMerged(subsumed_)) { | ||
| task_queues_.GetMutex(subsumed_, type_).unlock(); | ||
| } | ||
| task_queues_.GetMutex(owner_, type_).unlock(); | ||
| } | ||
|
|
||
| private: | ||
| bool isMerged(TaskQueueId queue_id) { | ||
| return queue_id != MessageLoopTaskQueues::_kUnmerged; | ||
| } | ||
|
|
||
| const TaskQueueId owner_; | ||
| TaskQueueId subsumed_; | ||
| MessageLoopTaskQueues& task_queues_; | ||
| const MutexType type_; | ||
|
|
||
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(MergedQueuesRunner); | ||
| }; | ||
|
|
||
| } // namespace fml | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,11 +5,15 @@ | |
| #define FML_USED_ON_EMBEDDER | ||
|
|
||
| #include "flutter/fml/message_loop_task_queues.h" | ||
| #include "flutter/fml/merged_queues_runner.cc" | ||
| #include "flutter/fml/message_loop_impl.h" | ||
|
|
||
| namespace fml { | ||
|
|
||
| std::mutex MessageLoopTaskQueues::creation_mutex_; | ||
| const size_t TaskQueueId::kUnmerged = ULONG_MAX; | ||
| const TaskQueueId MessageLoopTaskQueues::_kUnmerged = | ||
| TaskQueueId(TaskQueueId::kUnmerged); | ||
| fml::RefPtr<MessageLoopTaskQueues> MessageLoopTaskQueues::instance_; | ||
|
|
||
| fml::RefPtr<MessageLoopTaskQueues> MessageLoopTaskQueues::GetInstance() { | ||
|
|
@@ -22,7 +26,7 @@ fml::RefPtr<MessageLoopTaskQueues> MessageLoopTaskQueues::GetInstance() { | |
|
|
||
| TaskQueueId MessageLoopTaskQueues::CreateTaskQueue() { | ||
| std::scoped_lock creation(queue_meta_mutex_); | ||
| TaskQueueId loop_id = task_queue_id_counter_; | ||
| TaskQueueId loop_id = TaskQueueId(task_queue_id_counter_); | ||
| ++task_queue_id_counter_; | ||
|
|
||
| observers_mutexes_.push_back(std::make_unique<std::mutex>()); | ||
|
|
@@ -33,6 +37,9 @@ TaskQueueId MessageLoopTaskQueues::CreateTaskQueue() { | |
| delayed_tasks_.push_back(DelayedTaskQueue()); | ||
| wakeables_.push_back(NULL); | ||
|
|
||
| owner_to_subsumed_.push_back(_kUnmerged); | ||
| subsumed_to_owner_.push_back(_kUnmerged); | ||
|
|
||
| return loop_id; | ||
| } | ||
|
|
||
|
|
@@ -42,8 +49,9 @@ MessageLoopTaskQueues::MessageLoopTaskQueues() | |
| MessageLoopTaskQueues::~MessageLoopTaskQueues() = default; | ||
|
|
||
| void MessageLoopTaskQueues::Dispose(TaskQueueId queue_id) { | ||
| std::scoped_lock lock(GetMutex(queue_id, MutexType::kTasks)); | ||
| delayed_tasks_[queue_id] = {}; | ||
| MergedQueuesRunner merged_tasks = MergedQueuesRunner(*this, queue_id); | ||
| merged_tasks.InvokeMerged( | ||
| [&](TaskQueueId queue_id) { delayed_tasks_[queue_id] = {}; }); | ||
| } | ||
|
|
||
| void MessageLoopTaskQueues::RegisterTask(TaskQueueId queue_id, | ||
|
|
@@ -52,39 +60,47 @@ void MessageLoopTaskQueues::RegisterTask(TaskQueueId queue_id, | |
| std::scoped_lock lock(GetMutex(queue_id, MutexType::kTasks)); | ||
| size_t order = order_++; | ||
| delayed_tasks_[queue_id].push({order, std::move(task), target_time}); | ||
| WakeUp(queue_id, delayed_tasks_[queue_id].top().GetTargetTime()); | ||
| TaskQueueId loop_to_wake = queue_id; | ||
| if (subsumed_to_owner_[queue_id] != _kUnmerged) { | ||
| loop_to_wake = subsumed_to_owner_[queue_id]; | ||
| } | ||
| WakeUp(loop_to_wake, delayed_tasks_[queue_id].top().GetTargetTime()); | ||
| } | ||
|
|
||
| bool MessageLoopTaskQueues::HasPendingTasks(TaskQueueId queue_id) { | ||
| std::scoped_lock lock(GetMutex(queue_id, MutexType::kTasks)); | ||
| return !delayed_tasks_[queue_id].empty(); | ||
| MergedQueuesRunner merged_tasks = MergedQueuesRunner(*this, queue_id); | ||
| return HasPendingTasksUnlocked(queue_id); | ||
| } | ||
|
|
||
| void MessageLoopTaskQueues::GetTasksToRunNow( | ||
| TaskQueueId queue_id, | ||
| FlushType type, | ||
| std::vector<fml::closure>& invocations) { | ||
| std::scoped_lock lock(GetMutex(queue_id, MutexType::kTasks)); | ||
| MergedQueuesRunner merged_tasks = MergedQueuesRunner(*this, queue_id); | ||
|
|
||
| if (!HasPendingTasksUnlocked(queue_id)) { | ||
| return; | ||
| } | ||
|
|
||
| const auto now = fml::TimePoint::Now(); | ||
| DelayedTaskQueue& tasks = delayed_tasks_[queue_id]; | ||
|
|
||
| while (!tasks.empty()) { | ||
| const auto& top = tasks.top(); | ||
| while (HasPendingTasksUnlocked(queue_id)) { | ||
| TaskQueueId top_queue = _kUnmerged; | ||
| const auto& top = PeekNextTaskUnlocked(queue_id, top_queue); | ||
| if (top.GetTargetTime() > now) { | ||
| break; | ||
| } | ||
| invocations.emplace_back(std::move(top.GetTask())); | ||
| tasks.pop(); | ||
| delayed_tasks_[top_queue].pop(); | ||
| if (type == FlushType::kSingle) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We no longer make use of this in the concurrent tasks queue. So lets just get rid of flush type with the default being multiple.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created this flutter/flutter#36083, will follow this up. |
||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (tasks.empty()) { | ||
| if (!HasPendingTasksUnlocked(queue_id)) { | ||
| WakeUp(queue_id, fml::TimePoint::Max()); | ||
| } else { | ||
| WakeUp(queue_id, tasks.top().GetTargetTime()); | ||
| WakeUp(queue_id, GetNextWakeTimeUnlocked(queue_id)); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -96,8 +112,14 @@ void MessageLoopTaskQueues::WakeUp(TaskQueueId queue_id, fml::TimePoint time) { | |
| } | ||
|
|
||
| size_t MessageLoopTaskQueues::GetNumPendingTasks(TaskQueueId queue_id) { | ||
| std::scoped_lock lock(GetMutex(queue_id, MutexType::kTasks)); | ||
| return delayed_tasks_[queue_id].size(); | ||
| MergedQueuesRunner merged_tasks = MergedQueuesRunner(*this, queue_id); | ||
| if (subsumed_to_owner_[queue_id] != _kUnmerged) { | ||
| return 0; | ||
| } | ||
| size_t total_tasks = 0; | ||
| merged_tasks.InvokeMerged( | ||
| [&](TaskQueueId queue) { total_tasks += delayed_tasks_[queue].size(); }); | ||
| return total_tasks; | ||
| } | ||
|
|
||
| void MessageLoopTaskQueues::AddTaskObserver(TaskQueueId queue_id, | ||
|
|
@@ -114,10 +136,14 @@ void MessageLoopTaskQueues::RemoveTaskObserver(TaskQueueId queue_id, | |
| } | ||
|
|
||
| void MessageLoopTaskQueues::NotifyObservers(TaskQueueId queue_id) { | ||
| std::scoped_lock lock(GetMutex(queue_id, MutexType::kObservers)); | ||
| for (const auto& observer : task_observers_[queue_id]) { | ||
| observer.second(); | ||
| } | ||
| MergedQueuesRunner merged_observers = | ||
| MergedQueuesRunner(*this, queue_id, MutexType::kObservers); | ||
|
|
||
| merged_observers.InvokeMerged([&](TaskQueueId queue) { | ||
| for (const auto& observer : task_observers_[queue]) { | ||
| observer.second(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| // Thread safety analysis disabled as it does not account for defered locks. | ||
|
|
@@ -131,7 +157,7 @@ void MessageLoopTaskQueues::Swap(TaskQueueId primary, TaskQueueId secondary) | |
| std::mutex& t1 = GetMutex(primary, MutexType::kTasks); | ||
| std::mutex& t2 = GetMutex(secondary, MutexType::kTasks); | ||
|
|
||
| std::scoped_lock(o1, o2, t1, t2); | ||
| std::scoped_lock lock(o1, o2, t1, t2); | ||
|
|
||
| std::swap(task_observers_[primary], task_observers_[secondary]); | ||
| std::swap(delayed_tasks_[primary], delayed_tasks_[secondary]); | ||
|
|
@@ -140,9 +166,133 @@ void MessageLoopTaskQueues::Swap(TaskQueueId primary, TaskQueueId secondary) | |
| void MessageLoopTaskQueues::SetWakeable(TaskQueueId queue_id, | ||
| fml::Wakeable* wakeable) { | ||
| std::scoped_lock lock(GetMutex(queue_id, MutexType::kWakeables)); | ||
| FML_CHECK(!wakeables_[queue_id]) << "Wakeable can only be set once."; | ||
| wakeables_[queue_id] = wakeable; | ||
| } | ||
|
|
||
| bool MessageLoopTaskQueues::Merge(TaskQueueId owner, TaskQueueId subsumed) { | ||
| // task_observers locks | ||
| std::mutex& o1 = GetMutex(owner, MutexType::kObservers); | ||
| std::mutex& o2 = GetMutex(subsumed, MutexType::kObservers); | ||
|
|
||
| // delayed_tasks locks | ||
| std::mutex& t1 = GetMutex(owner, MutexType::kTasks); | ||
| std::mutex& t2 = GetMutex(subsumed, MutexType::kTasks); | ||
|
|
||
| std::scoped_lock lock(o1, o2, t1, t2); | ||
|
|
||
| if (owner == subsumed) { | ||
| return true; | ||
| } | ||
|
|
||
| if (owner_to_subsumed_[owner] == subsumed) { | ||
| return true; | ||
| } | ||
|
|
||
| std::vector<TaskQueueId> owner_subsumed_keys = { | ||
| owner_to_subsumed_[owner], owner_to_subsumed_[subsumed], | ||
| subsumed_to_owner_[owner], subsumed_to_owner_[subsumed]}; | ||
|
|
||
| for (auto key : owner_subsumed_keys) { | ||
| if (key != _kUnmerged) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| owner_to_subsumed_[owner] = subsumed; | ||
| subsumed_to_owner_[subsumed] = owner; | ||
|
|
||
| if (HasPendingTasksUnlocked(owner)) { | ||
| WakeUp(owner, GetNextWakeTimeUnlocked(owner)); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner) { | ||
| MergedQueuesRunner merged_observers = | ||
| MergedQueuesRunner(*this, owner, MutexType::kObservers); | ||
| MergedQueuesRunner merged_tasks = | ||
| MergedQueuesRunner(*this, owner, MutexType::kTasks); | ||
|
|
||
| const TaskQueueId subsumed = owner_to_subsumed_[owner]; | ||
| if (subsumed == _kUnmerged) { | ||
| return false; | ||
| } | ||
|
|
||
| subsumed_to_owner_[subsumed] = _kUnmerged; | ||
| owner_to_subsumed_[owner] = _kUnmerged; | ||
|
|
||
| if (HasPendingTasksUnlocked(owner)) { | ||
| WakeUp(owner, GetNextWakeTimeUnlocked(owner)); | ||
| } | ||
|
|
||
| if (HasPendingTasksUnlocked(subsumed)) { | ||
| WakeUp(subsumed, GetNextWakeTimeUnlocked(subsumed)); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool MessageLoopTaskQueues::Owns(TaskQueueId owner, TaskQueueId subsumed) { | ||
| MergedQueuesRunner merged_observers = MergedQueuesRunner(*this, owner); | ||
| return subsumed == owner_to_subsumed_[owner] || owner == subsumed; | ||
| } | ||
|
|
||
| // Subsumed queues will never have pending tasks. | ||
| // Owning queues will consider both their and their subsumed tasks. | ||
| bool MessageLoopTaskQueues::HasPendingTasksUnlocked(TaskQueueId queue_id) { | ||
| if (subsumed_to_owner_[queue_id] != _kUnmerged) { | ||
| return false; | ||
| } | ||
|
|
||
| if (!delayed_tasks_[queue_id].empty()) { | ||
| return true; | ||
| } | ||
|
|
||
| const TaskQueueId subsumed = owner_to_subsumed_[queue_id]; | ||
| if (subsumed == _kUnmerged) { | ||
| // this is not an owner and queue is empty. | ||
| return false; | ||
| } else { | ||
| return !delayed_tasks_[subsumed].empty(); | ||
| } | ||
| } | ||
|
|
||
| fml::TimePoint MessageLoopTaskQueues::GetNextWakeTimeUnlocked( | ||
| TaskQueueId queue_id) { | ||
| TaskQueueId tmp = _kUnmerged; | ||
| return PeekNextTaskUnlocked(queue_id, tmp).GetTargetTime(); | ||
| } | ||
|
|
||
| const DelayedTask& MessageLoopTaskQueues::PeekNextTaskUnlocked( | ||
| TaskQueueId owner, | ||
| TaskQueueId& top_queue_id) { | ||
| FML_DCHECK(HasPendingTasksUnlocked(owner)); | ||
| const TaskQueueId subsumed = owner_to_subsumed_[owner]; | ||
| if (subsumed == _kUnmerged) { | ||
| top_queue_id = owner; | ||
| return delayed_tasks_[owner].top(); | ||
| } | ||
| // we are owning another task queue | ||
| const bool subsumed_has_task = !delayed_tasks_[subsumed].empty(); | ||
| const bool owner_has_task = !delayed_tasks_[owner].empty(); | ||
| if (owner_has_task && subsumed_has_task) { | ||
| const auto owner_task = delayed_tasks_[owner].top(); | ||
| const auto subsumed_task = delayed_tasks_[subsumed].top(); | ||
| if (owner_task > subsumed_task) { | ||
| top_queue_id = subsumed; | ||
| } else { | ||
| top_queue_id = owner; | ||
| } | ||
| } else if (owner_has_task) { | ||
| top_queue_id = owner; | ||
| } else { | ||
| top_queue_id = subsumed; | ||
| } | ||
| return delayed_tasks_[top_queue_id].top(); | ||
| } | ||
|
|
||
| std::mutex& MessageLoopTaskQueues::GetMutex(TaskQueueId queue_id, | ||
| MutexType type) { | ||
| std::scoped_lock lock(queue_meta_mutex_); | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets make task queue ID a struct with a single member of
size_t.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done