Flutter Linux Embedder
fl_task_runner.cc
Go to the documentation of this file.
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 
7 
8 static constexpr int kMicrosecondsPerNanosecond = 1000;
9 static constexpr int kMillisecondsPerMicrosecond = 1000;
10 
11 struct _FlTaskRunner {
12  GObject parent_instance;
13 
14  FlEngine* engine;
15 
16  GMutex mutex;
17  GCond cond;
18 
20  GList /*<FlTaskRunnerTask>*/* pending_tasks;
22 };
23 
24 typedef struct _FlTaskRunnerTask {
25  // absolute time of task (based on g_get_monotonic_time)
27  FlutterTask task;
29 
30 G_DEFINE_TYPE(FlTaskRunner, fl_task_runner, G_TYPE_OBJECT)
31 
32 // Removes expired tasks from the task queue and executes them.
33 // The execution is performed with mutex unlocked.
34 static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner* self) {
35  GList* expired_tasks = nullptr;
36 
37  gint64 current_time = g_get_monotonic_time();
38 
39  GList* l = self->pending_tasks;
40  while (l != nullptr) {
41  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
42  if (task->task_time_micros <= current_time) {
43  GList* link = l;
44  l = l->next;
45  self->pending_tasks = g_list_remove_link(self->pending_tasks, link);
46  expired_tasks = g_list_concat(expired_tasks, link);
47  } else {
48  l = l->next;
49  }
50  }
51 
52  g_mutex_unlock(&self->mutex);
53 
54  l = expired_tasks;
55  while (l != nullptr && self->engine) {
56  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
57  fl_engine_execute_task(self->engine, &task->task);
58  l = l->next;
59  }
60 
61  g_list_free_full(expired_tasks, g_free);
62 
63  g_mutex_lock(&self->mutex);
64 }
65 
66 static void fl_task_runner_tasks_did_change_locked(FlTaskRunner* self);
67 
68 // Invoked from a timeout source. Removes and executes expired tasks
69 // and reschedules timeout if needed.
70 static gboolean fl_task_runner_on_expired_timeout(gpointer data) {
71  FlTaskRunner* self = FL_TASK_RUNNER(data);
72 
73  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
74  (void)locker; // unused variable
75 
76  g_object_ref(self);
77 
78  self->timeout_source_id = 0;
80 
81  // reschedule timeout
83 
84  g_object_unref(self);
85 
86  return FALSE;
87 }
88 
89 // Returns the absolute time of next expired task (in microseconds, based on
90 // g_get_monotonic_time). If no task is scheduled returns G_MAXINT64.
92  FlTaskRunner* self) {
93  gint64 min_time = G_MAXINT64;
94  GList* l = self->pending_tasks;
95  while (l != nullptr) {
96  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
97  min_time = MIN(min_time, task->task_time_micros);
98  l = l->next;
99  }
100  return min_time;
101 }
102 
103 static void fl_task_runner_tasks_did_change_locked(FlTaskRunner* self) {
104  if (self->blocking_main_thread) {
105  // Wake up blocked thread
106  g_cond_signal(&self->cond);
107  } else {
108  // Reschedule timeout
109  if (self->timeout_source_id != 0) {
110  g_source_remove(self->timeout_source_id);
111  self->timeout_source_id = 0;
112  }
113  gint64 min_time = fl_task_runner_next_task_expiration_time_locked(self);
114  if (min_time != G_MAXINT64) {
115  gint64 remaining = MAX(min_time - g_get_monotonic_time(), 0);
116  self->timeout_source_id =
117  g_timeout_add(remaining / kMillisecondsPerMicrosecond + 1,
119  }
120  }
121 }
122 
123 static void engine_weak_notify_cb(gpointer user_data,
124  GObject* where_the_object_was) {
125  FlTaskRunner* self = FL_TASK_RUNNER(user_data);
126  self->engine = nullptr;
127 }
128 
129 void fl_task_runner_dispose(GObject* object) {
130  FlTaskRunner* self = FL_TASK_RUNNER(object);
131 
132  // this should never happen because the task runner is retained while blocking
133  // main thread
134  g_assert(!self->blocking_main_thread);
135 
136  if (self->engine != nullptr) {
137  g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self);
138  self->engine = nullptr;
139  }
140 
141  g_mutex_clear(&self->mutex);
142  g_cond_clear(&self->cond);
143 
144  g_list_free_full(self->pending_tasks, g_free);
145  if (self->timeout_source_id != 0) {
146  g_source_remove(self->timeout_source_id);
147  }
148 
149  G_OBJECT_CLASS(fl_task_runner_parent_class)->dispose(object);
150 }
151 
152 static void fl_task_runner_class_init(FlTaskRunnerClass* klass) {
153  G_OBJECT_CLASS(klass)->dispose = fl_task_runner_dispose;
154 }
155 
156 static void fl_task_runner_init(FlTaskRunner* self) {
157  g_mutex_init(&self->mutex);
158  g_cond_init(&self->cond);
159 }
160 
161 FlTaskRunner* fl_task_runner_new(FlEngine* engine) {
162  FlTaskRunner* res =
163  FL_TASK_RUNNER(g_object_new(fl_task_runner_get_type(), nullptr));
164  res->engine = engine;
165  g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, res);
166  return res;
167 }
168 
169 void fl_task_runner_post_task(FlTaskRunner* self,
170  FlutterTask task,
171  uint64_t target_time_nanos) {
172  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
173  (void)locker; // unused variable
174 
175  FlTaskRunnerTask* runner_task = g_new0(FlTaskRunnerTask, 1);
176  runner_task->task = task;
177  runner_task->task_time_micros =
178  target_time_nanos / kMicrosecondsPerNanosecond;
179 
180  self->pending_tasks = g_list_append(self->pending_tasks, runner_task);
182 }
183 
184 void fl_task_runner_block_main_thread(FlTaskRunner* self) {
185  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
186  (void)locker; // unused variable
187 
188  g_return_if_fail(self->blocking_main_thread == FALSE);
189 
190  g_object_ref(self);
191 
192  self->blocking_main_thread = true;
193  while (self->blocking_main_thread) {
194  g_cond_wait_until(&self->cond, &self->mutex,
197  }
198 
199  // Tasks might have changed in the meanwhile, reschedule timeout
201 
202  g_object_unref(self);
203 }
204 
205 void fl_task_runner_release_main_thread(FlTaskRunner* self) {
206  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
207  (void)locker; // unused variable
208 
209  g_return_if_fail(self->blocking_main_thread == TRUE);
210 
211  self->blocking_main_thread = FALSE;
212  g_cond_signal(&self->cond);
213 }
kMicrosecondsPerNanosecond
static constexpr int kMicrosecondsPerNanosecond
Definition: fl_task_runner.cc:8
fl_task_runner.h
_FlTaskRunnerTask::task_time_micros
gint64 task_time_micros
Definition: fl_task_runner.cc:26
FlTaskRunnerTask
struct _FlTaskRunnerTask FlTaskRunnerTask
fl_task_runner_process_expired_tasks_locked
static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner *self)
Definition: fl_task_runner.cc:34
fl_task_runner_dispose
void fl_task_runner_dispose(GObject *object)
Definition: fl_task_runner.cc:129
user_data
FlKeyEvent uint64_t FlKeyResponderAsyncCallback gpointer user_data
Definition: fl_key_channel_responder.cc:121
_FlTaskRunner::engine
FlEngine * engine
Definition: fl_task_runner.cc:14
fl_task_runner_next_task_expiration_time_locked
static gint64 fl_task_runner_next_task_expiration_time_locked(FlTaskRunner *self)
Definition: fl_task_runner.cc:91
fl_task_runner_init
static void fl_task_runner_init(FlTaskRunner *self)
Definition: fl_task_runner.cc:156
fl_task_runner_class_init
static void fl_task_runner_class_init(FlTaskRunnerClass *klass)
Definition: fl_task_runner.cc:152
fl_task_runner_on_expired_timeout
static gboolean fl_task_runner_on_expired_timeout(gpointer data)
Definition: fl_task_runner.cc:70
engine_weak_notify_cb
static void engine_weak_notify_cb(gpointer user_data, GObject *where_the_object_was)
Definition: fl_task_runner.cc:123
_FlTaskRunner::cond
GCond cond
Definition: fl_task_runner.cc:17
_FlTaskRunner::pending_tasks
GList * pending_tasks
Definition: fl_task_runner.cc:20
fl_task_runner_post_task
void fl_task_runner_post_task(FlTaskRunner *self, FlutterTask task, uint64_t target_time_nanos)
Definition: fl_task_runner.cc:169
fl_engine_private.h
G_DEFINE_TYPE
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
Definition: fl_basic_message_channel.cc:37
TRUE
return TRUE
Definition: fl_pixel_buffer_texture_test.cc:53
_FlTaskRunner::parent_instance
GObject parent_instance
Definition: fl_task_runner.cc:12
_FlTaskRunner::blocking_main_thread
gboolean blocking_main_thread
Definition: fl_task_runner.cc:21
fl_task_runner_release_main_thread
void fl_task_runner_release_main_thread(FlTaskRunner *self)
Definition: fl_task_runner.cc:205
_FlTaskRunner
Definition: fl_task_runner.cc:11
kMillisecondsPerMicrosecond
static constexpr int kMillisecondsPerMicrosecond
Definition: fl_task_runner.cc:9
fl_task_runner_tasks_did_change_locked
static void fl_task_runner_tasks_did_change_locked(FlTaskRunner *self)
Definition: fl_task_runner.cc:103
_FlTaskRunner::timeout_source_id
guint timeout_source_id
Definition: fl_task_runner.cc:19
engine
FlEngine * engine
Definition: fl_view_accessible.cc:26
fl_engine_execute_task
void fl_engine_execute_task(FlEngine *self, FlutterTask *task)
Definition: fl_engine.cc:905
_FlTaskRunnerTask::task
FlutterTask task
Definition: fl_task_runner.cc:27
fl_task_runner_block_main_thread
void fl_task_runner_block_main_thread(FlTaskRunner *self)
Definition: fl_task_runner.cc:184
fl_task_runner_new
FlTaskRunner * fl_task_runner_new(FlEngine *engine)
Definition: fl_task_runner.cc:161
_FlTaskRunnerTask
Definition: fl_task_runner.cc:24
_FlTaskRunner::mutex
GMutex mutex
Definition: fl_task_runner.cc:16