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;
21 };
22 
23 typedef struct _FlTaskRunnerTask {
24  // absolute time of task (based on g_get_monotonic_time).
26 
27  // flutter task to execute if schedule through
28  // fl_task_runner_post_flutter_task.
29  FlutterTask task;
31 
32 G_DEFINE_TYPE(FlTaskRunner, fl_task_runner, G_TYPE_OBJECT)
33 
34 // Removes expired tasks from the task queue and executes them.
35 // The execution is performed with mutex unlocked.
36 static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner* self) {
37  GList* expired_tasks = nullptr;
38 
39  gint64 current_time = g_get_monotonic_time();
40 
41  GList* l = self->pending_tasks;
42  while (l != nullptr) {
43  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
44  if (task->task_time_micros <= current_time) {
45  GList* link = l;
46  l = l->next;
47  self->pending_tasks = g_list_remove_link(self->pending_tasks, link);
48  expired_tasks = g_list_concat(expired_tasks, link);
49  } else {
50  l = l->next;
51  }
52  }
53 
54  g_mutex_unlock(&self->mutex);
55 
56  g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
57  if (engine != nullptr) {
58  l = expired_tasks;
59  while (l != nullptr) {
60  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
61  fl_engine_execute_task(engine, &task->task);
62  l = l->next;
63  }
64  }
65 
66  g_list_free_full(expired_tasks, g_free);
67 
68  g_mutex_lock(&self->mutex);
69 }
70 
71 static void fl_task_runner_tasks_did_change_locked(FlTaskRunner* self);
72 
73 // Invoked from a timeout source. Removes and executes expired tasks
74 // and reschedules timeout if needed.
75 static gboolean fl_task_runner_on_expired_timeout(gpointer data) {
76  FlTaskRunner* self = FL_TASK_RUNNER(data);
77 
78  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
79  (void)locker; // unused variable
80 
81  g_object_ref(self);
82 
83  self->timeout_source_id = 0;
85 
86  // reschedule timeout
88 
89  g_object_unref(self);
90 
91  return FALSE;
92 }
93 
94 // Returns the absolute time of next expired task (in microseconds, based on
95 // g_get_monotonic_time). If no task is scheduled returns G_MAXINT64.
97  FlTaskRunner* self) {
98  gint64 min_time = G_MAXINT64;
99  GList* l = self->pending_tasks;
100  while (l != nullptr) {
101  FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
102  min_time = MIN(min_time, task->task_time_micros);
103  l = l->next;
104  }
105  return min_time;
106 }
107 
108 static void fl_task_runner_tasks_did_change_locked(FlTaskRunner* self) {
109  // Reschedule timeout
110  if (self->timeout_source_id != 0) {
111  g_source_remove(self->timeout_source_id);
112  self->timeout_source_id = 0;
113  }
114  gint64 min_time = fl_task_runner_next_task_expiration_time_locked(self);
115  if (min_time != G_MAXINT64) {
116  gint64 remaining = MAX(min_time - g_get_monotonic_time(), 0);
117  self->timeout_source_id =
118  g_timeout_add(remaining / kMillisecondsPerMicrosecond + 1,
120  }
121 }
122 
123 void fl_task_runner_dispose(GObject* object) {
124  FlTaskRunner* self = FL_TASK_RUNNER(object);
125 
126  g_weak_ref_clear(&self->engine);
127  g_mutex_clear(&self->mutex);
128  g_cond_clear(&self->cond);
129 
130  g_list_free_full(self->pending_tasks, g_free);
131  if (self->timeout_source_id != 0) {
132  g_source_remove(self->timeout_source_id);
133  }
134 
135  G_OBJECT_CLASS(fl_task_runner_parent_class)->dispose(object);
136 }
137 
138 static void fl_task_runner_class_init(FlTaskRunnerClass* klass) {
139  G_OBJECT_CLASS(klass)->dispose = fl_task_runner_dispose;
140 }
141 
142 static void fl_task_runner_init(FlTaskRunner* self) {
143  g_mutex_init(&self->mutex);
144  g_cond_init(&self->cond);
145 }
146 
147 FlTaskRunner* fl_task_runner_new(FlEngine* engine) {
148  FlTaskRunner* self =
149  FL_TASK_RUNNER(g_object_new(fl_task_runner_get_type(), nullptr));
150  g_weak_ref_init(&self->engine, G_OBJECT(engine));
151  return self;
152 }
153 
154 void fl_task_runner_post_flutter_task(FlTaskRunner* self,
155  FlutterTask task,
156  uint64_t target_time_nanos) {
157  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
158  (void)locker; // unused variable
159 
160  FlTaskRunnerTask* runner_task = g_new0(FlTaskRunnerTask, 1);
161  runner_task->task = task;
162  runner_task->task_time_micros =
163  target_time_nanos / kMicrosecondsPerNanosecond;
164 
165  self->pending_tasks = g_list_append(self->pending_tasks, runner_task);
167 
168  // Tasks changed, so wake up anything blocking in fl_task_runner_wait.
169  g_cond_signal(&self->cond);
170 }
171 
172 void fl_task_runner_wait(FlTaskRunner* self) {
173  g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
174  (void)locker; // unused variable
175 
176  g_cond_wait_until(&self->cond, &self->mutex,
180 }
181 
182 void fl_task_runner_stop_wait(FlTaskRunner* self) {
183  g_cond_signal(&self->cond);
184 }
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
g_autoptr(GMutexLocker) locker
void fl_engine_execute_task(FlEngine *self, FlutterTask *task)
Definition: fl_engine.cc:1458
void fl_task_runner_wait(FlTaskRunner *self)
void fl_task_runner_stop_wait(FlTaskRunner *self)
void fl_task_runner_dispose(GObject *object)
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)
static constexpr int kMillisecondsPerMicrosecond
static void fl_task_runner_class_init(FlTaskRunnerClass *klass)
static void fl_task_runner_tasks_did_change_locked(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)
guint timeout_source_id
GList * pending_tasks
GObject parent_instance