Skip to content

Commit 2cdac7b

Browse files
ONEMPERS-234 port platform task execution loop from glfw embedder
This allows handling of flutter platform events - necessary eg. for Service Protocol handler registration. The code has been ported from glfw implementation in flutter engine, in flutter/shell/platform/glfw/ (adapted to use our ppoll based main loop)
1 parent 9eb583d commit 2cdac7b

File tree

5 files changed

+266
-10
lines changed

5 files changed

+266
-10
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ set(SOURCES
3535
src/utils.cc
3636
src/debug.cc
3737
src/wayland_display.cc
38+
src/event_loop.cc
3839
src/elf.h
3940
src/macros.h
4041
src/keys.h
4142
src/utils.h
4243
src/egl_utils.h
4344
src/debug.h
4445
src/wayland_display.h
46+
src/event_loop.h
4547
)
4648

4749
ecm_add_wayland_client_protocol(

src/event_loop.cc

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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 "event_loop.h"
6+
#include <atomic>
7+
#include <utility>
8+
#include <climits>
9+
10+
#include "event_loop.h"
11+
12+
#include <atomic>
13+
#include <utility>
14+
#include <unistd.h>
15+
16+
namespace flutter {
17+
18+
PlatformEventLoop::PlatformEventLoop(std::thread::id main_thread_id, const TaskExpiredCallback &on_task_expired, int notify_fd)
19+
: main_thread_id_(main_thread_id)
20+
, on_task_expired_(std::move(on_task_expired))
21+
, notify_fd_(notify_fd) {
22+
}
23+
24+
PlatformEventLoop::~PlatformEventLoop() = default;
25+
26+
bool PlatformEventLoop::RunsTasksOnCurrentThread() const {
27+
return std::this_thread::get_id() == main_thread_id_;
28+
}
29+
30+
uint64_t PlatformEventLoop::ProcessEvents() {
31+
std::vector<FlutterTask> expired_tasks;
32+
33+
// Process expired tasks.
34+
{
35+
std::lock_guard<std::mutex> lock(task_queue_mutex_);
36+
const uint64_t start_processing_time = FlutterEngineGetCurrentTime();
37+
while (!task_queue_.empty()) {
38+
const auto &top = task_queue_.top();
39+
// If this task (and all tasks after this) has not yet expired, there is
40+
// nothing more to do. Quit iterating.
41+
if (top.fire_time > start_processing_time) {
42+
break;
43+
}
44+
45+
// Make a record of the expired task. Do NOT service the task here
46+
// because we are still holding onto the task queue mutex. We don't want
47+
// other threads to block on posting tasks onto this thread till we are
48+
// done processing expired tasks.
49+
expired_tasks.push_back(task_queue_.top().task);
50+
51+
// Remove the tasks from the delayed tasks queue.
52+
task_queue_.pop();
53+
}
54+
}
55+
56+
// Fire expired tasks.
57+
{
58+
// Flushing tasks here without holing onto the task queue mutex.
59+
for (const auto &task : expired_tasks) {
60+
on_task_expired_(&task);
61+
}
62+
}
63+
64+
// return timestamp of next event or 0 if none
65+
{
66+
std::lock_guard<std::mutex> lock(task_queue_mutex_);
67+
if (task_queue_.empty()) {
68+
return 0;
69+
} else {
70+
return task_queue_.top().fire_time;
71+
}
72+
}
73+
}
74+
75+
void PlatformEventLoop::PostTask(FlutterTask flutter_task, uint64_t flutter_target_time_nanos) {
76+
static std::atomic<uint64_t> sGlobalTaskOrder(0);
77+
78+
Task task;
79+
task.order = ++sGlobalTaskOrder;
80+
task.fire_time = flutter_target_time_nanos;
81+
task.task = flutter_task;
82+
83+
{
84+
std::lock_guard<std::mutex> lock(task_queue_mutex_);
85+
task_queue_.push(task);
86+
87+
// Make sure the queue mutex is unlocked before waking up the loop. In case
88+
// the wake causes this thread to be descheduled for the primary thread to
89+
// process tasks, the acquisition of the lock on that thread while holding
90+
// the lock here momentarily till the end of the scope is a pessimization.
91+
}
92+
Wake();
93+
}
94+
95+
void PlatformEventLoop::Wake() {
96+
ssize_t ret = 0;
97+
uint64_t val = 1;
98+
do {
99+
ret = write(notify_fd_, &val, sizeof(val));
100+
} while (ret < 0 && errno == EAGAIN);
101+
}
102+
103+
} // namespace flutter

src/event_loop.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
#ifndef FLUTTER_SHELL_PLATFORM_GLFW_EVENT_LOOP_H_
6+
#define FLUTTER_SHELL_PLATFORM_GLFW_EVENT_LOOP_H_
7+
8+
#include <chrono>
9+
#include <deque>
10+
#include <functional>
11+
#include <mutex>
12+
#include <queue>
13+
#include <thread>
14+
15+
#include <flutter_embedder.h>
16+
17+
namespace flutter {
18+
19+
// platform event loop, to handle flutter platform events
20+
// adapted from flutter engine glfw embedder (flutter/shell/platform/glfw/flutter_glfw.cc)
21+
class PlatformEventLoop {
22+
public:
23+
using TaskExpiredCallback = std::function<void(const FlutterTask *)>;
24+
// Creates an event loop running on the given thread, calling
25+
// |on_task_expired| to run tasks.
26+
// notify_fd event descriptor will be used to wake up the main thread when events arrive
27+
PlatformEventLoop(std::thread::id main_thread_id, const TaskExpiredCallback &on_task_expired, int notify_fd);
28+
29+
virtual ~PlatformEventLoop();
30+
31+
// Disallow copy.
32+
PlatformEventLoop(const PlatformEventLoop &) = delete;
33+
PlatformEventLoop &operator=(const PlatformEventLoop &) = delete;
34+
35+
// Returns if the current thread is the thread used by this event loop.
36+
bool RunsTasksOnCurrentThread() const;
37+
38+
// processes expired events, and returns next wake up point or 0 if there are no events
39+
uint64_t ProcessEvents();
40+
41+
// Posts a Flutter engine task to the event loop for delayed execution.
42+
void PostTask(FlutterTask flutter_task, uint64_t flutter_target_time_nanos);
43+
44+
protected:
45+
46+
// Wakes the main thread
47+
void Wake();
48+
49+
struct Task {
50+
uint64_t order;
51+
uint64_t fire_time;
52+
FlutterTask task;
53+
54+
struct Comparer {
55+
bool operator()(const Task &a, const Task &b) {
56+
if (a.fire_time == b.fire_time) {
57+
return a.order > b.order;
58+
}
59+
return a.fire_time > b.fire_time;
60+
}
61+
};
62+
};
63+
std::thread::id main_thread_id_;
64+
TaskExpiredCallback on_task_expired_;
65+
std::mutex task_queue_mutex_;
66+
std::priority_queue<Task, std::deque<Task>, Task::Comparer> task_queue_;
67+
int notify_fd_;
68+
};
69+
70+
} // namespace flutter
71+
72+
#endif // FLUTTER_SHELL_PLATFORM_GLFW_EVENT_LOOP_H_

src/wayland_display.cc

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,18 @@ bool WaylandDisplay::SetupEngine(const std::string &bundle_path, const std::vect
649649
},
650650
};
651651

652+
// configure platform task runner; render tasks runner will still be provided by flutter
653+
FlutterTaskRunnerDescription platform_task_runner = {};
654+
FlutterCustomTaskRunners task_runners = {};
655+
task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
656+
task_runners.platform_task_runner = &platform_task_runner;
657+
if (ConfigurePlatformTaskRunner(&platform_task_runner)) {
658+
args.custom_task_runners = &task_runners;
659+
} else {
660+
dbgE("Couldn't configure platform task runner\n");
661+
exit(1);
662+
}
663+
652664
std::string libapp_aot_path = bundle_path + "/" + FlutterGetAppAotElfName(); // dw: TODO: There seems to be no convention name we could use, so let's temporary hardcode the path.
653665

654666
if (FlutterEngineRunsAOTCompiledDartCode()) {
@@ -1011,6 +1023,16 @@ ssize_t WaylandDisplay::vSyncSendNotifyData() {
10111023
return rv;
10121024
}
10131025

1026+
static void set_sleep_to_next_platform_event(const uint64_t timestamp_of_next_platform_event, struct timespec &ts) {
1027+
if (timestamp_of_next_platform_event == 0) {
1028+
ts = {.tv_sec = LONG_MAX, .tv_nsec = 0};
1029+
} else {
1030+
const uint64_t now = FlutterEngineGetCurrentTime();
1031+
const uint64_t time_to_sleep = now <= timestamp_of_next_platform_event ? timestamp_of_next_platform_event - now : 0;
1032+
timespec_from_nsec(&ts, time_to_sleep);
1033+
}
1034+
}
1035+
10141036
bool WaylandDisplay::Run() {
10151037
if (!valid_) {
10161038
dbgE("Could not run an invalid display.\n");
@@ -1034,26 +1056,28 @@ bool WaylandDisplay::Run() {
10341056

10351057
wl_display_flush(display_);
10361058

1059+
uint64_t timestamp_of_next_platform_event_ns = 0;
1060+
10371061
do {
1038-
int rv;
10391062

1040-
struct pollfd fds[4] = {
1063+
struct timespec ts;
1064+
set_sleep_to_next_platform_event(timestamp_of_next_platform_event_ns, ts);
1065+
1066+
int rv, ppoll_rv;
1067+
1068+
struct pollfd fds[5] = {
10411069
{.fd = vsync.sv_[vsync.SOCKET_READER], .events = POLLIN, .revents = 0},
10421070
{.fd = fd, .events = POLLIN | POLLERR, .revents = 0},
10431071
{.fd = key.timer_fd_, .events = POLLIN | POLLERR, .revents = 0},
10441072
{.fd = memory_watcher_.event_fd, .events = POLLIN | POLLERR, .revents = 0},
1073+
{.fd = event_loop_._platform_event_loop_eventfd, .events = POLLIN | POLLERR, .revents = 0}
10451074
};
10461075

10471076
do {
1048-
static const struct timespec ts = {
1049-
.tv_sec = LONG_MAX,
1050-
.tv_nsec = 0,
1051-
};
1052-
1053-
rv = ppoll(&fds[0], std::size(fds), &ts, nullptr);
1054-
} while (rv == -1 && errno == EINTR);
1077+
ppoll_rv = ppoll(&fds[0], std::size(fds), &ts, nullptr);
1078+
} while (ppoll_rv == -1 && errno == EINTR);
10551079

1056-
if (rv == -1) {
1080+
if (ppoll_rv == -1) {
10571081
printf("ERROR: ppoll returned -1 (errno: %d)\n", errno);
10581082
return false;
10591083
}
@@ -1115,6 +1139,18 @@ bool WaylandDisplay::Run() {
11151139
}
11161140
}
11171141

1142+
const bool event_loop_wakeup = fds[4].revents & POLLIN;
1143+
if (event_loop_wakeup) {
1144+
uint64_t result;
1145+
do {
1146+
rv = read(fds[4].fd, &result, sizeof result);
1147+
} while (rv == -1 && errno == EINTR);
1148+
}
1149+
// in case of event loop wakeup or if timeout of another event passed - flush events queue
1150+
if (event_loop_wakeup || ppoll_rv == 0) {
1151+
timestamp_of_next_platform_event_ns = event_loop_._platform_event_loop->ProcessEvents();
1152+
}
1153+
11181154
break;
11191155
} while (true);
11201156

@@ -1243,4 +1279,37 @@ bool WaylandDisplay::SetupEGL() {
12431279

12441280
return true;
12451281
}
1282+
1283+
void WaylandDisplay::RunFlutterTask(const FlutterTask *task) {
1284+
if (!engine_) {
1285+
dbgE("FlutterEngineRunTask called before engine was initialized\n");
1286+
} else if (FlutterEngineRunTask(engine_, task) != kSuccess) {
1287+
dbgW("FlutterEngineRunTask failed\n");
1288+
}
1289+
}
1290+
1291+
bool WaylandDisplay::ConfigurePlatformTaskRunner(FlutterTaskRunnerDescription *task_runner) {
1292+
1293+
event_loop_._platform_event_loop_eventfd = eventfd(0, 0);
1294+
if (event_loop_._platform_event_loop_eventfd == -1) {
1295+
dbgE("Couldn't create platform event loop event.\n");
1296+
return false;
1297+
}
1298+
1299+
event_loop_._platform_event_loop = std::make_unique<PlatformEventLoop>(
1300+
std::this_thread::get_id(),
1301+
std::bind(&WaylandDisplay::RunFlutterTask,this,std::placeholders::_1),
1302+
event_loop_._platform_event_loop_eventfd);
1303+
1304+
task_runner->struct_size = sizeof(FlutterTaskRunnerDescription);
1305+
task_runner->user_data = event_loop_._platform_event_loop.get();
1306+
1307+
task_runner->runs_task_on_current_thread_callback = [](void *state) -> bool {
1308+
return reinterpret_cast<PlatformEventLoop *>(state)->RunsTasksOnCurrentThread();
1309+
};
1310+
task_runner->post_task_callback = [](FlutterTask task, uint64_t target_time_nanos, void *state) -> void {
1311+
reinterpret_cast<PlatformEventLoop *>(state)->PostTask(task, target_time_nanos);
1312+
};
1313+
return true;
1314+
}
12461315
} // namespace flutter

src/wayland_display.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <gdk/gdk.h>
1414
#include <xkbcommon/xkbcommon.h>
1515
#include <flutter_embedder.h>
16+
#include "event_loop.h"
1617

1718
#include <memory>
1819
#include <string>
@@ -105,6 +106,10 @@ class WaylandDisplay {
105106

106107
void CleanupMemoryWatcher();
107108

109+
bool ConfigurePlatformTaskRunner(FlutterTaskRunnerDescription *task_runner);
110+
111+
void RunFlutterTask(const FlutterTask *task);
112+
108113
// key repeat related
109114
struct {
110115
int32_t repeat_delay_ms_ = 400;
@@ -128,6 +133,11 @@ class WaylandDisplay {
128133
ssize_t vSyncReadNotifyData();
129134
// }
130135

136+
struct {
137+
std::unique_ptr<PlatformEventLoop> _platform_event_loop;
138+
int _platform_event_loop_eventfd = -1;
139+
} event_loop_;
140+
131141
FLWAY_DISALLOW_COPY_AND_ASSIGN(WaylandDisplay)
132142
};
133143

0 commit comments

Comments
 (0)