156 lines
4.5 KiB
C++
156 lines
4.5 KiB
C++
// Copyright (c) 2018 The Chromium Embedded Framework 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 "tests/cefclient/browser/main_message_loop_multithreaded_gtk.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <gtk/gtkmain.h>
|
|
|
|
#include "include/base/cef_bind.h"
|
|
#include "include/base/cef_logging.h"
|
|
#include "include/wrapper/cef_closure_task.h"
|
|
|
|
namespace client {
|
|
|
|
namespace {
|
|
|
|
base::Lock g_global_lock;
|
|
base::PlatformThreadId g_global_lock_thread = kInvalidPlatformThreadId;
|
|
|
|
void lock_enter() {
|
|
// The GDK lock is not reentrant, so check that we're using it correctly.
|
|
// See comments on ScopedGdkThreadsEnter.
|
|
base::PlatformThreadId current_thread = base::PlatformThread::CurrentId();
|
|
CHECK(current_thread != g_global_lock_thread);
|
|
|
|
g_global_lock.Acquire();
|
|
g_global_lock_thread = current_thread;
|
|
}
|
|
|
|
void lock_leave() {
|
|
g_global_lock_thread = kInvalidPlatformThreadId;
|
|
g_global_lock.Release();
|
|
}
|
|
|
|
// Same as g_idle_add() but specifying the GMainContext.
|
|
guint idle_add(GMainContext* main_context,
|
|
GSourceFunc function,
|
|
gpointer data) {
|
|
GSource* source = g_idle_source_new();
|
|
g_source_set_callback(source, function, data, nullptr);
|
|
guint id = g_source_attach(source, main_context);
|
|
g_source_unref(source);
|
|
return id;
|
|
}
|
|
|
|
// Same as g_timeout_add() but specifying the GMainContext.
|
|
guint timeout_add(GMainContext* main_context,
|
|
guint interval,
|
|
GSourceFunc function,
|
|
gpointer data) {
|
|
GSource* source = g_timeout_source_new(interval);
|
|
g_source_set_callback(source, function, data, nullptr);
|
|
guint id = g_source_attach(source, main_context);
|
|
g_source_unref(source);
|
|
return id;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
MainMessageLoopMultithreadedGtk::MainMessageLoopMultithreadedGtk()
|
|
: thread_id_(base::PlatformThread::CurrentId()) {
|
|
// Initialize Xlib support for concurrent threads. This function must be the
|
|
// first Xlib function a multi-threaded program calls, and it must complete
|
|
// before any other Xlib call is made.
|
|
CHECK(XInitThreads() != 0);
|
|
|
|
// Initialize GDK thread support. See comments on ScopedGdkThreadsEnter.
|
|
gdk_threads_set_lock_functions(lock_enter, lock_leave);
|
|
gdk_threads_init();
|
|
}
|
|
|
|
MainMessageLoopMultithreadedGtk::~MainMessageLoopMultithreadedGtk() {
|
|
DCHECK(RunsTasksOnCurrentThread());
|
|
DCHECK(queued_tasks_.empty());
|
|
}
|
|
|
|
int MainMessageLoopMultithreadedGtk::Run() {
|
|
DCHECK(RunsTasksOnCurrentThread());
|
|
|
|
// Chromium uses the default GLib context so we create our own context and
|
|
// make it the default for this thread.
|
|
main_context_ = g_main_context_new();
|
|
g_main_context_push_thread_default(main_context_);
|
|
|
|
main_loop_ = g_main_loop_new(main_context_, TRUE);
|
|
|
|
// Check the queue when GTK is idle, or at least every 100ms.
|
|
// TODO(cef): It might be more efficient to use input functions
|
|
// (gdk_input_add) and trigger by writing to an fd.
|
|
idle_add(main_context_, MainMessageLoopMultithreadedGtk::TriggerRunTasks,
|
|
this);
|
|
timeout_add(main_context_, 100,
|
|
MainMessageLoopMultithreadedGtk::TriggerRunTasks, this);
|
|
|
|
// Block until g_main_loop_quit().
|
|
g_main_loop_run(main_loop_);
|
|
|
|
// Release GLib resources.
|
|
g_main_loop_unref(main_loop_);
|
|
main_loop_ = nullptr;
|
|
|
|
g_main_context_pop_thread_default(main_context_);
|
|
g_main_context_unref(main_context_);
|
|
main_context_ = nullptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void MainMessageLoopMultithreadedGtk::Quit() {
|
|
PostTask(CefCreateClosureTask(base::Bind(
|
|
&MainMessageLoopMultithreadedGtk::DoQuit, base::Unretained(this))));
|
|
}
|
|
|
|
void MainMessageLoopMultithreadedGtk::PostTask(CefRefPtr<CefTask> task) {
|
|
base::AutoLock lock_scope(lock_);
|
|
|
|
// Queue the task.
|
|
queued_tasks_.push(task);
|
|
}
|
|
|
|
bool MainMessageLoopMultithreadedGtk::RunsTasksOnCurrentThread() const {
|
|
return (thread_id_ == base::PlatformThread::CurrentId());
|
|
}
|
|
|
|
// static
|
|
int MainMessageLoopMultithreadedGtk::TriggerRunTasks(void* self) {
|
|
static_cast<MainMessageLoopMultithreadedGtk*>(self)->RunTasks();
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
void MainMessageLoopMultithreadedGtk::RunTasks() {
|
|
DCHECK(RunsTasksOnCurrentThread());
|
|
|
|
std::queue<CefRefPtr<CefTask>> tasks;
|
|
|
|
{
|
|
base::AutoLock lock_scope(lock_);
|
|
tasks.swap(queued_tasks_);
|
|
}
|
|
|
|
// Execute all queued tasks.
|
|
while (!tasks.empty()) {
|
|
CefRefPtr<CefTask> task = tasks.front();
|
|
tasks.pop();
|
|
task->Execute();
|
|
}
|
|
}
|
|
|
|
void MainMessageLoopMultithreadedGtk::DoQuit() {
|
|
DCHECK(RunsTasksOnCurrentThread());
|
|
g_main_loop_quit(main_loop_);
|
|
}
|
|
|
|
} // namespace client
|