1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #include "flutter/shell/platform/glfw/event_loop.h" |
6 | |
7 | #include <atomic> |
8 | #include <utility> |
9 | |
10 | namespace flutter { |
11 | |
12 | EventLoop::EventLoop(std::thread::id main_thread_id, |
13 | const TaskExpiredCallback& on_task_expired) |
14 | : main_thread_id_(main_thread_id), on_task_expired_(on_task_expired) {} |
15 | |
16 | EventLoop::~EventLoop() = default; |
17 | |
18 | bool EventLoop::RunsTasksOnCurrentThread() const { |
19 | return std::this_thread::get_id() == main_thread_id_; |
20 | } |
21 | |
22 | void EventLoop::WaitForEvents(std::chrono::nanoseconds max_wait) { |
23 | const auto now = TaskTimePoint::clock::now(); |
24 | std::vector<FlutterTask> expired_tasks; |
25 | |
26 | // Process expired tasks. |
27 | { |
28 | std::lock_guard<std::mutex> lock(task_queue_mutex_); |
29 | while (!task_queue_.empty()) { |
30 | const auto& top = task_queue_.top(); |
31 | // If this task (and all tasks after this) has not yet expired, there is |
32 | // nothing more to do. Quit iterating. |
33 | if (top.fire_time > now) { |
34 | break; |
35 | } |
36 | |
37 | // Make a record of the expired task. Do NOT service the task here |
38 | // because we are still holding onto the task queue mutex. We don't want |
39 | // other threads to block on posting tasks onto this thread till we are |
40 | // done processing expired tasks. |
41 | expired_tasks.push_back(x: task_queue_.top().task); |
42 | |
43 | // Remove the tasks from the delayed tasks queue. |
44 | task_queue_.pop(); |
45 | } |
46 | } |
47 | |
48 | // Fire expired tasks. |
49 | { |
50 | // Flushing tasks here without holing onto the task queue mutex. |
51 | for (const auto& task : expired_tasks) { |
52 | on_task_expired_(&task); |
53 | } |
54 | } |
55 | |
56 | // Sleep till the next task needs to be processed. If a new task comes |
57 | // along, the wait will be resolved early because PostTask calls Wake(). |
58 | { |
59 | TaskTimePoint next_wake; |
60 | { |
61 | std::lock_guard<std::mutex> lock(task_queue_mutex_); |
62 | TaskTimePoint max_wake_timepoint = |
63 | max_wait == std::chrono::nanoseconds::max() ? TaskTimePoint::max() |
64 | : now + max_wait; |
65 | TaskTimePoint next_event_timepoint = task_queue_.empty() |
66 | ? TaskTimePoint::max() |
67 | : task_queue_.top().fire_time; |
68 | next_wake = std::min(a: max_wake_timepoint, b: next_event_timepoint); |
69 | } |
70 | WaitUntil(time: next_wake); |
71 | } |
72 | } |
73 | |
74 | EventLoop::TaskTimePoint EventLoop::TimePointFromFlutterTime( |
75 | uint64_t flutter_target_time_nanos) { |
76 | const auto now = TaskTimePoint::clock::now(); |
77 | const int64_t flutter_duration = |
78 | flutter_target_time_nanos - FlutterEngineGetCurrentTime(); |
79 | return now + std::chrono::nanoseconds(flutter_duration); |
80 | } |
81 | |
82 | void EventLoop::PostTask(FlutterTask flutter_task, |
83 | uint64_t flutter_target_time_nanos) { |
84 | static std::atomic_uint64_t sGlobalTaskOrder(0); |
85 | |
86 | Task task; |
87 | task.order = ++sGlobalTaskOrder; |
88 | task.fire_time = TimePointFromFlutterTime(flutter_target_time_nanos); |
89 | task.task = flutter_task; |
90 | |
91 | { |
92 | std::lock_guard<std::mutex> lock(task_queue_mutex_); |
93 | task_queue_.push(v: task); |
94 | |
95 | // Make sure the queue mutex is unlocked before waking up the loop. In case |
96 | // the wake causes this thread to be descheduled for the primary thread to |
97 | // process tasks, the acquisition of the lock on that thread while holding |
98 | // the lock here momentarily till the end of the scope is a pessimization. |
99 | } |
100 | Wake(); |
101 | } |
102 | |
103 | } // namespace flutter |
104 | |