• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4 
5 #include "tests/cefclient/browser/main_message_loop_multithreaded_gtk.h"
6 
7 #include <X11/Xlib.h>
8 #include <gtk/gtk.h>
9 
10 #include "include/base/cef_callback.h"
11 #include "include/base/cef_logging.h"
12 #include "include/wrapper/cef_closure_task.h"
13 
14 namespace client {
15 
16 namespace {
17 
18 base::Lock g_global_lock;
19 base::PlatformThreadId g_global_lock_thread = kInvalidPlatformThreadId;
20 
lock_enter()21 void lock_enter() {
22   // The GDK lock is not reentrant, so check that we're using it correctly.
23   // See comments on ScopedGdkThreadsEnter.
24   base::PlatformThreadId current_thread = base::PlatformThread::CurrentId();
25   CHECK(current_thread != g_global_lock_thread);
26 
27   g_global_lock.Acquire();
28   g_global_lock_thread = current_thread;
29 }
30 
lock_leave()31 void lock_leave() {
32   g_global_lock_thread = kInvalidPlatformThreadId;
33   g_global_lock.Release();
34 }
35 
36 // Same as g_idle_add() but specifying the GMainContext.
idle_add(GMainContext * main_context,GSourceFunc function,gpointer data)37 guint idle_add(GMainContext* main_context,
38                GSourceFunc function,
39                gpointer data) {
40   GSource* source = g_idle_source_new();
41   g_source_set_callback(source, function, data, nullptr);
42   guint id = g_source_attach(source, main_context);
43   g_source_unref(source);
44   return id;
45 }
46 
47 // Same as g_timeout_add() but specifying the GMainContext.
timeout_add(GMainContext * main_context,guint interval,GSourceFunc function,gpointer data)48 guint timeout_add(GMainContext* main_context,
49                   guint interval,
50                   GSourceFunc function,
51                   gpointer data) {
52   GSource* source = g_timeout_source_new(interval);
53   g_source_set_callback(source, function, data, nullptr);
54   guint id = g_source_attach(source, main_context);
55   g_source_unref(source);
56   return id;
57 }
58 
59 }  // namespace
60 
MainMessageLoopMultithreadedGtk()61 MainMessageLoopMultithreadedGtk::MainMessageLoopMultithreadedGtk()
62     : thread_id_(base::PlatformThread::CurrentId()) {
63   // Initialize Xlib support for concurrent threads. This function must be the
64   // first Xlib function a multi-threaded program calls, and it must complete
65   // before any other Xlib call is made.
66   CHECK(XInitThreads() != 0);
67 
68   // Initialize GDK thread support. See comments on ScopedGdkThreadsEnter.
69   gdk_threads_set_lock_functions(lock_enter, lock_leave);
70   gdk_threads_init();
71 }
72 
~MainMessageLoopMultithreadedGtk()73 MainMessageLoopMultithreadedGtk::~MainMessageLoopMultithreadedGtk() {
74   DCHECK(RunsTasksOnCurrentThread());
75   DCHECK(queued_tasks_.empty());
76 }
77 
Run()78 int MainMessageLoopMultithreadedGtk::Run() {
79   DCHECK(RunsTasksOnCurrentThread());
80 
81   // We use the default Glib context and Chromium creates its own context in
82   // MessagePumpGlib (starting in M86).
83   main_context_ = g_main_context_default();
84 
85   main_loop_ = g_main_loop_new(main_context_, TRUE);
86 
87   // Check the queue when GTK is idle, or at least every 100ms.
88   // TODO(cef): It might be more efficient to use input functions
89   // (gdk_input_add) and trigger by writing to an fd.
90   idle_add(main_context_, MainMessageLoopMultithreadedGtk::TriggerRunTasks,
91            this);
92   timeout_add(main_context_, 100,
93               MainMessageLoopMultithreadedGtk::TriggerRunTasks, this);
94 
95   // Block until g_main_loop_quit().
96   g_main_loop_run(main_loop_);
97 
98   // Release GLib resources.
99   g_main_loop_unref(main_loop_);
100   main_loop_ = nullptr;
101 
102   main_context_ = nullptr;
103 
104   return 0;
105 }
106 
Quit()107 void MainMessageLoopMultithreadedGtk::Quit() {
108   PostTask(CefCreateClosureTask(base::BindOnce(
109       &MainMessageLoopMultithreadedGtk::DoQuit, base::Unretained(this))));
110 }
111 
PostTask(CefRefPtr<CefTask> task)112 void MainMessageLoopMultithreadedGtk::PostTask(CefRefPtr<CefTask> task) {
113   base::AutoLock lock_scope(lock_);
114 
115   // Queue the task.
116   queued_tasks_.push(task);
117 }
118 
RunsTasksOnCurrentThread() const119 bool MainMessageLoopMultithreadedGtk::RunsTasksOnCurrentThread() const {
120   return (thread_id_ == base::PlatformThread::CurrentId());
121 }
122 
123 // static
TriggerRunTasks(void * self)124 int MainMessageLoopMultithreadedGtk::TriggerRunTasks(void* self) {
125   static_cast<MainMessageLoopMultithreadedGtk*>(self)->RunTasks();
126   return G_SOURCE_CONTINUE;
127 }
128 
RunTasks()129 void MainMessageLoopMultithreadedGtk::RunTasks() {
130   DCHECK(RunsTasksOnCurrentThread());
131 
132   std::queue<CefRefPtr<CefTask>> tasks;
133 
134   {
135     base::AutoLock lock_scope(lock_);
136     tasks.swap(queued_tasks_);
137   }
138 
139   // Execute all queued tasks.
140   while (!tasks.empty()) {
141     CefRefPtr<CefTask> task = tasks.front();
142     tasks.pop();
143     task->Execute();
144   }
145 }
146 
DoQuit()147 void MainMessageLoopMultithreadedGtk::DoQuit() {
148   DCHECK(RunsTasksOnCurrentThread());
149   g_main_loop_quit(main_loop_);
150 }
151 
152 }  // namespace client
153