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  GWeakRef 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 
28  // flutter task to execute if schedule through
29  // fl_task_runner_post_flutter_task.
30  FlutterTask task;
31 
32  // callback to execute if scheduled through fl_task_runner_post_callback.
33  void (*callback)(gpointer data);
34 
35  // data for the callback.
36  gpointer callback_data;
38 
39 G_DEFINE_TYPE(FlTaskRunner, fl_task_runner, G_TYPE_OBJECT)
40 
41 // Removes expired tasks from the task queue and executes them.
42 // The execution is performed with mutex unlocked.
43 static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner* self) {
44  GList* expired_tasks = nullptr;
45 
46  gint64 current_time = g_get_monotonic_time();
47 
48  GList* l = self->pending_tasks;
49  while (l != nullptr) {
50  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
51  if (task->task_time_micros <= current_time) {
52  GList* link = l;
53  l = l->next;
54  self->pending_tasks = g_list_remove_link(self->pending_tasks, link);
55  expired_tasks = g_list_concat(expired_tasks, link);
56  } else {
57  l = l->next;
58  }
59  }
60 
61  g_mutex_unlock(&self->mutex);
62 
63  g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
64  if (engine != nullptr) {
65  l = expired_tasks;
66  while (l != nullptr) {
67  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
68  if (task->callback != nullptr) {
69  task->callback(task->callback_data);
70  } else {
71  fl_engine_execute_task(engine, &task->task);
72  }
73  l = l->next;
74  }
75  }
76 
77  g_list_free_full(expired_tasks, g_free);
78 
79  g_mutex_lock(&self->mutex);
80 }
81 
82 static void fl_task_runner_tasks_did_change_locked(FlTaskRunner* self);
83 
84 // Invoked from a timeout source. Removes and executes expired tasks
85 // and reschedules timeout if needed.
86 static gboolean fl_task_runner_on_expired_timeout(gpointer data) {
87  FlTaskRunner* self = FL_TASK_RUNNER(data);
88 
89  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
90  (void)locker; // unused variable
91 
92  g_object_ref(self);
93 
94  self->timeout_source_id = 0;
96 
97  // reschedule timeout
99 
100  g_object_unref(self);
101 
102  return FALSE;
103 }
104 
105 // Returns the absolute time of next expired task (in microseconds, based on
106 // g_get_monotonic_time). If no task is scheduled returns G_MAXINT64.
108  FlTaskRunner* self) {
109  gint64 min_time = G_MAXINT64;
110  GList* l = self->pending_tasks;
111  while (l != nullptr) {
112  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
113  min_time = MIN(min_time, task->task_time_micros);
114  l = l->next;
115  }
116  return min_time;
117 }
118 
119 static void fl_task_runner_tasks_did_change_locked(FlTaskRunner* self) {
120  if (self->blocking_main_thread) {
121  // Wake up blocked thread
122  g_cond_signal(&self->cond);
123  } else {
124  // Reschedule timeout
125  if (self->timeout_source_id != 0) {
126  g_source_remove(self->timeout_source_id);
127  self->timeout_source_id = 0;
128  }
129  gint64 min_time = fl_task_runner_next_task_expiration_time_locked(self);
130  if (min_time != G_MAXINT64) {
131  gint64 remaining = MAX(min_time - g_get_monotonic_time(), 0);
132  self->timeout_source_id =
133  g_timeout_add(remaining / kMillisecondsPerMicrosecond + 1,
135  }
136  }
137 }
138 
139 void fl_task_runner_dispose(GObject* object) {
140  FlTaskRunner* self = FL_TASK_RUNNER(object);
141 
142  // this should never happen because the task runner is retained while blocking
143  // main thread
144  g_assert(!self->blocking_main_thread);
145 
146  g_weak_ref_clear(&self->engine);
147  g_mutex_clear(&self->mutex);
148  g_cond_clear(&self->cond);
149 
150  g_list_free_full(self->pending_tasks, g_free);
151  if (self->timeout_source_id != 0) {
152  g_source_remove(self->timeout_source_id);
153  }
154 
155  G_OBJECT_CLASS(fl_task_runner_parent_class)->dispose(object);
156 }
157 
158 static void fl_task_runner_class_init(FlTaskRunnerClass* klass) {
159  G_OBJECT_CLASS(klass)->dispose = fl_task_runner_dispose;
160 }
161 
162 static void fl_task_runner_init(FlTaskRunner* self) {
163  g_mutex_init(&self->mutex);
164  g_cond_init(&self->cond);
165 }
166 
167 FlTaskRunner* fl_task_runner_new(FlEngine* engine) {
168  FlTaskRunner* self =
169  FL_TASK_RUNNER(g_object_new(fl_task_runner_get_type(), nullptr));
170  g_weak_ref_init(&self->engine, G_OBJECT(engine));
171  return self;
172 }
173 
174 void fl_task_runner_post_flutter_task(FlTaskRunner* self,
175  FlutterTask task,
176  uint64_t target_time_nanos) {
177  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
178  (void)locker; // unused variable
179 
180  FlTaskRunnerTask* runner_task = g_new0(FlTaskRunnerTask, 1);
181  runner_task->task = task;
182  runner_task->task_time_micros =
183  target_time_nanos / kMicrosecondsPerNanosecond;
184 
185  self->pending_tasks = g_list_append(self->pending_tasks, runner_task);
187 }
188 
189 void fl_task_runner_post_callback(FlTaskRunner* self,
190  void (*callback)(gpointer data),
191  gpointer data) {
192  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
193  (void)locker; // unused variable
194 
195  FlTaskRunnerTask* runner_task = g_new0(FlTaskRunnerTask, 1);
196  runner_task->callback = callback;
197  runner_task->callback_data = data;
198 
199  self->pending_tasks = g_list_append(self->pending_tasks, runner_task);
201 }
202 
203 void fl_task_runner_block_main_thread(FlTaskRunner* self) {
204  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
205  (void)locker; // unused variable
206 
207  g_return_if_fail(self->blocking_main_thread == FALSE);
208 
209  g_object_ref(self);
210 
211  self->blocking_main_thread = true;
212  while (self->blocking_main_thread) {
213  g_cond_wait_until(&self->cond, &self->mutex,
216  }
217 
218  // Tasks might have changed in the meanwhile, reschedule timeout
220 
221  g_object_unref(self);
222 }
223 
224 void fl_task_runner_release_main_thread(FlTaskRunner* self) {
225  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
226  (void)locker; // unused variable
227 
228  g_return_if_fail(self->blocking_main_thread == TRUE);
229 
230  self->blocking_main_thread = FALSE;
231  g_cond_signal(&self->cond);
232 }
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
void fl_engine_execute_task(FlEngine *self, FlutterTask *task)
Definition: fl_engine.cc:1424
void fl_task_runner_dispose(GObject *object)
void fl_task_runner_post_callback(FlTaskRunner *self, void(*callback)(gpointer data), gpointer data)
static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner *self)
void fl_task_runner_post_flutter_task(FlTaskRunner *self, FlutterTask task, uint64_t target_time_nanos)
struct _FlTaskRunnerTask FlTaskRunnerTask
static gint64 fl_task_runner_next_task_expiration_time_locked(FlTaskRunner *self)
void fl_task_runner_release_main_thread(FlTaskRunner *self)
static constexpr int kMillisecondsPerMicrosecond
static void fl_task_runner_class_init(FlTaskRunnerClass *klass)
static void fl_task_runner_tasks_did_change_locked(FlTaskRunner *self)
void fl_task_runner_block_main_thread(FlTaskRunner *self)
static gboolean fl_task_runner_on_expired_timeout(gpointer data)
static constexpr int kMicrosecondsPerNanosecond
FlTaskRunner * fl_task_runner_new(FlEngine *engine)
static void fl_task_runner_init(FlTaskRunner *self)
gboolean blocking_main_thread
guint timeout_source_id
GList * pending_tasks
GObject parent_instance
void(* callback)(gpointer data)