This repository was archived by the owner on Feb 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6k
Wire up custom event loop interop for the GLFW embedder. #9089
Merged
chinmaygarde
merged 1 commit into
flutter:master
from
chinmaygarde:glfw_event_loop_interop
Jun 30, 2019
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "flutter/shell/platform/glfw/glfw_event_loop.h" | ||
|
||
#include <GLFW/glfw3.h> | ||
|
||
#include <atomic> | ||
#include <utility> | ||
|
||
namespace flutter { | ||
|
||
GLFWEventLoop::GLFWEventLoop(std::thread::id main_thread_id, | ||
TaskExpiredCallback on_task_expired) | ||
: main_thread_id_(main_thread_id), | ||
on_task_expired_(std::move(on_task_expired)) {} | ||
|
||
GLFWEventLoop::~GLFWEventLoop() = default; | ||
|
||
bool GLFWEventLoop::RunsTasksOnCurrentThread() const { | ||
return std::this_thread::get_id() == main_thread_id_; | ||
} | ||
|
||
void GLFWEventLoop::WaitForEvents(std::chrono::nanoseconds max_wait) { | ||
const auto now = TaskTimePoint::clock::now(); | ||
std::vector<FlutterTask> expired_tasks; | ||
|
||
// Process expired tasks. | ||
{ | ||
std::lock_guard<std::mutex> lock(task_queue_mutex_); | ||
while (!task_queue_.empty()) { | ||
const auto& top = task_queue_.top(); | ||
// If this task (and all tasks after this) has not yet expired, there is | ||
// nothing more to do. Quit iterating. | ||
if (top.fire_time > now) { | ||
break; | ||
} | ||
|
||
// Make a record of the expired task. Do NOT service the task here | ||
// because we are still holding onto the task queue mutex. We don't want | ||
// other threads to block on posting tasks onto this thread till we are | ||
// done processing expired tasks. | ||
expired_tasks.push_back(task_queue_.top().task); | ||
|
||
// Remove the tasks from the delayed tasks queue. | ||
task_queue_.pop(); | ||
} | ||
} | ||
|
||
// Fire expired tasks. | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no scoped lock; why is there a scope block? (I would argue that the fact that you did this despite in not being any mechanical reason I can see is an indication that it should be broken into helpers.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, you are right. I wrote the comments first as pseudocode then filled it out with real code. As noted earlier, I don't think adding scopes means that the block should necessarily be extracted into its own helper routine. |
||
// Flushing tasks here without holing onto the task queue mutex. | ||
for (const auto& task : expired_tasks) { | ||
on_task_expired_(&task); | ||
} | ||
} | ||
|
||
// Sleep till the next task needs to be processed. If a new task comes | ||
// along, the wait in GLFW will be resolved early because PostTask posts an | ||
// empty event. | ||
{ | ||
// Make sure the seconds are not integral. | ||
using Seconds = std::chrono::duration<double, std::ratio<1>>; | ||
|
||
std::lock_guard<std::mutex> lock(task_queue_mutex_); | ||
const auto next_wake = task_queue_.empty() ? TaskTimePoint::max() | ||
: task_queue_.top().fire_time; | ||
|
||
const auto duration_to_wait = std::chrono::duration_cast<Seconds>( | ||
std::min(next_wake - now, max_wait)); | ||
|
||
if (duration_to_wait.count() > 0.0) { | ||
::glfwWaitEventsTimeout(duration_to_wait.count()); | ||
} else { | ||
// Avoid engine task priority inversion by making sure GLFW events are | ||
// always processed even when there is no need to wait for pending engine | ||
// tasks. | ||
::glfwPollEvents(); | ||
} | ||
} | ||
} | ||
|
||
GLFWEventLoop::TaskTimePoint GLFWEventLoop::TimePointFromFlutterTime( | ||
uint64_t flutter_target_time_nanos) { | ||
const auto now = TaskTimePoint::clock::now(); | ||
const auto flutter_duration = | ||
flutter_target_time_nanos - FlutterEngineGetCurrentTime(); | ||
return now + std::chrono::nanoseconds(flutter_duration); | ||
} | ||
|
||
void GLFWEventLoop::PostTask(FlutterTask flutter_task, | ||
uint64_t flutter_target_time_nanos) { | ||
static std::atomic_uint64_t sGlobalTaskOrder(0); | ||
|
||
Task task; | ||
task.order = ++sGlobalTaskOrder; | ||
task.fire_time = TimePointFromFlutterTime(flutter_target_time_nanos); | ||
task.task = flutter_task; | ||
|
||
{ | ||
std::lock_guard<std::mutex> lock(task_queue_mutex_); | ||
task_queue_.push(task); | ||
|
||
// Make sure the queue mutex is unlocked before waking up the loop. In case | ||
// the wake causes this thread to be descheduled for the primary thread to | ||
// process tasks, the acquisition of the lock on that thread while holding | ||
// the lock here momentarily till the end of the scope is a pessimization. | ||
} | ||
|
||
::glfwPostEmptyEvent(); | ||
} | ||
|
||
} // namespace flutter |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef FLUTTER_SHELL_PLATFORM_GLFW_GLFW_EVENT_LOOP_H_ | ||
#define FLUTTER_SHELL_PLATFORM_GLFW_GLFW_EVENT_LOOP_H_ | ||
|
||
#include <chrono> | ||
#include <deque> | ||
#include <mutex> | ||
#include <queue> | ||
#include <thread> | ||
|
||
#include "flutter/shell/platform/embedder/embedder.h" | ||
stuartmorgan-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
namespace flutter { | ||
|
||
// An event loop implementation that supports Flutter Engine tasks scheduling in | ||
// the GLFW event loop. | ||
class GLFWEventLoop { | ||
chinmaygarde marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public: | ||
using TaskExpiredCallback = std::function<void(const FlutterTask*)>; | ||
GLFWEventLoop(std::thread::id main_thread_id, | ||
TaskExpiredCallback on_task_expired); | ||
|
||
~GLFWEventLoop(); | ||
|
||
// Returns if the current thread is the thread used by the GLFW event loop. | ||
bool RunsTasksOnCurrentThread() const; | ||
|
||
// Wait for an any GLFW or pending Flutter Engine events and returns when | ||
// either is encountered. Expired engine events are processed. The optional | ||
// timeout should only be used when non-GLFW or engine events need to be | ||
// processed in a polling manner. | ||
void WaitForEvents( | ||
std::chrono::nanoseconds max_wait = std::chrono::nanoseconds::max()); | ||
|
||
// Post a Flutter engine tasks to the event loop for delayed execution. | ||
void PostTask(FlutterTask flutter_task, uint64_t flutter_target_time_nanos); | ||
|
||
private: | ||
using TaskTimePoint = std::chrono::steady_clock::time_point; | ||
struct Task { | ||
uint64_t order; | ||
TaskTimePoint fire_time; | ||
FlutterTask task; | ||
|
||
struct Comparer { | ||
bool operator()(const Task& a, const Task& b) { | ||
if (a.fire_time == b.fire_time) { | ||
return a.order > b.order; | ||
} | ||
return a.fire_time > b.fire_time; | ||
} | ||
}; | ||
}; | ||
std::thread::id main_thread_id_; | ||
TaskExpiredCallback on_task_expired_; | ||
std::mutex task_queue_mutex_; | ||
std::priority_queue<Task, std::deque<Task>, Task::Comparer> task_queue_; | ||
std::condition_variable task_queue_cv_; | ||
|
||
GLFWEventLoop(const GLFWEventLoop&) = delete; | ||
|
||
GLFWEventLoop& operator=(const GLFWEventLoop&) = delete; | ||
|
||
static TaskTimePoint TimePointFromFlutterTime( | ||
uint64_t flutter_target_time_nanos); | ||
}; | ||
|
||
} // namespace flutter | ||
|
||
#endif // FLUTTER_SHELL_PLATFORM_GLFW_GLFW_EVENT_LOOP_H_ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems unrelated to the PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is necessary after
--build-glfw-shell
on Mac.