summarylogtreecommitdiffstats
path: root/0011-ozone-wayland-Do-not-use-possibly-blocking-dispatch-.patch
blob: def2841ee47628f751752262a5a8e32e4c6bb198 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
From 4f4c199c6654ef75319eabf3977e916b2a744d3f Mon Sep 17 00:00:00 2001
From: Maksim Sisov <msisov@igalia.com>
Date: Thu, 8 Aug 2019 11:47:41 +0000
Subject: [PATCH 11/11] [ozone/wayland] Do not use possibly blocking dispatch
 API

Using wl_display_dispatch is a wrong thing to do as it is a blocking
method (see [1]). It can cause freezes and deadlocks.

Instead, we need to do something like this -

while (wl_display_prepare_read_queue(display, queue) != 0)
        wl_display_dispatch_queue_pending(display, queue);
wl_display_flush(display);

ret = poll(fds, nfds, -1);
if (has_error(ret))
        wl_display_cancel_read(display);
else
        wl_display_read_events(display);

wl_display_dispatch_queue_pending(display, queue);

However, there is no need to do a second poll as long
as we are notified by libevent that the display fd is
non-blocking. However, to ensure we can read, we have
to prepare the queue, which tells other clients we
are going to read from the queue. If prepare fails,
it means there is nothing to read. Thus, we push
the events into the queue and return early instead.

We have also to add an "automatic" flush as part
of the polling mechanism. This poll must ensure
that all the data has been written to the queue.
If not, -1 is returned and EAGAIN is set. In this
case, we have to stop watching the fd for read
access and start watching it for the write access.

As soon as all the data is written, we can get
back to reading.

[1] https://people.freedesktop.org/~whot/wayland-doxygen/wayland/Client/classwl__display.html#a30a9c4f020f3e77581c7a81ecdb4913d

Bug: 987821
Change-Id: I536513034c18a47da079a2b56a75d32f20f72ad6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1730159
Reviewed-by: Robert Kroeger <rjkroege@chromium.org>
Commit-Queue: Maksim Sisov <msisov@igalia.com>
Cr-Commit-Position: refs/heads/master@{#685148}
---
 .../wayland/host/wayland_connection.cc        | 74 +++++++++++++++----
 .../wayland/host/wayland_connection.h         |  4 +
 .../wayland/host/wayland_data_device.cc       |  2 +-
 .../wayland/host/wayland_data_source.cc       |  2 +-
 .../platform/wayland/host/wayland_keyboard.cc |  2 +-
 .../platform/wayland/host/wayland_window.cc   |  3 +-
 .../wayland/host/wayland_window_unittest.cc   | 17 +++--
 .../wayland/host/xdg_surface_wrapper_v5.cc    |  1 +
 .../wayland/host/xdg_surface_wrapper_v6.cc    |  3 +-
 9 files changed, 84 insertions(+), 24 deletions(-)

diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index bf6d12717861..203db09c7f15 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -101,20 +101,29 @@ bool WaylandConnection::StartProcessingEvents() {
     return true;
 
   DCHECK(display_);
+
+  MaybePrepareReadQueue();
+
+  // Displatch event from display to server.
   wl_display_flush(display_.get());
 
-  DCHECK(base::MessageLoopCurrentForUI::IsSet());
-  if (!base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
-          wl_display_get_fd(display_.get()), true,
-          base::MessagePumpLibevent::WATCH_READ, &controller_, this))
-    return false;
+  return BeginWatchingFd(base::MessagePumpLibevent::WATCH_READ);
+}
 
-  watching_ = true;
-  return true;
+void WaylandConnection::MaybePrepareReadQueue() {
+  if (prepared_)
+    return;
+
+  if (wl_display_prepare_read(display()) != -1) {
+    prepared_ = true;
+    return;
+  }
+  // Nothing to read, send events to the queue.
+  wl_display_dispatch_pending(display());
 }
 
 void WaylandConnection::ScheduleFlush() {
-  if (scheduled_flush_ || !watching_)
+  if (scheduled_flush_)
     return;
   DCHECK(base::MessageLoopCurrentForUI::IsSet());
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -207,13 +216,38 @@ void WaylandConnection::DispatchUiEvent(Event* event) {
 }
 
 void WaylandConnection::OnFileCanReadWithoutBlocking(int fd) {
-  wl_display_dispatch(display_.get());
-  std::vector<WaylandWindow*> windows = wayland_window_manager_.GetAllWindows();
-  for (auto* window : windows)
-    window->ApplyPendingBounds();
+  if (prepared_) {
+    prepared_ = false;
+    if (wl_display_read_events(display()) == -1)
+      return;
+    wl_display_dispatch_pending(display());
+  }
+
+  MaybePrepareReadQueue();
+
+  if (!prepared_)
+    return;
+
+  // Automatic Flush.
+  int ret = wl_display_flush(display_.get());
+  if (ret != -1 || errno != EAGAIN)
+    return;
+
+  // if all data could not be written, errno will be set to EAGAIN and -1
+  // returned. In that case, use poll on the display file descriptor to wait for
+  // it to become writable again.
+  BeginWatchingFd(base::MessagePumpLibevent::WATCH_WRITE);
 }
 
-void WaylandConnection::OnFileCanWriteWithoutBlocking(int fd) {}
+void WaylandConnection::OnFileCanWriteWithoutBlocking(int fd) {
+  int ret = wl_display_flush(display_.get());
+  if (ret != -1 || errno != EAGAIN)
+    BeginWatchingFd(base::MessagePumpLibevent::WATCH_READ);
+  else if (ret < 0 && errno != EPIPE && prepared_)
+    wl_display_cancel_read(display());
+
+  // Otherwise just continue watching in the same mode.
+}
 
 void WaylandConnection::EnsureDataDevice() {
   if (!data_device_manager_ || !seat_)
@@ -225,6 +259,20 @@ void WaylandConnection::EnsureDataDevice() {
                                                   data_device_.get());
 }
 
+bool WaylandConnection::BeginWatchingFd(
+    base::WatchableIOMessagePumpPosix::Mode mode) {
+  if (watching_) {
+    // Stop watching first.
+    watching_ = !controller_.StopWatchingFileDescriptor();
+    DCHECK(!watching_);
+  }
+
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
+  watching_ = base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
+      wl_display_get_fd(display_.get()), true, mode, &controller_, this);
+  return watching_;
+}
+
 // static
 void WaylandConnection::Global(void* data,
                                wl_registry* registry,
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h
index 8a56ebc6b921..ff587ae9ee6f 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -146,6 +146,9 @@ class WaylandConnection : public PlatformEventSource,
   // Make sure data device is properly initialized
   void EnsureDataDevice();
 
+  bool BeginWatchingFd(base::WatchableIOMessagePumpPosix::Mode mode);
+  void MaybePrepareReadQueue();
+
   // wl_registry_listener
   static void Global(void* data,
                      wl_registry* registry,
@@ -192,6 +195,7 @@ class WaylandConnection : public PlatformEventSource,
 
   bool scheduled_flush_ = false;
   bool watching_ = false;
+  bool prepared_ = false;
   base::MessagePumpLibevent::FdWatchController controller_;
 
   uint32_t serial_ = 0;
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device.cc b/ui/ozone/platform/wayland/host/wayland_data_device.cc
index a36cd3b7a888..56e568fbd164 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_device.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_device.cc
@@ -355,7 +355,7 @@ void WaylandDataDevice::RegisterDeferredReadCallback() {
   deferred_read_callback_.reset(wl_display_sync(connection_->display()));
   wl_callback_add_listener(deferred_read_callback_.get(),
                            &kDeferredReadListener, this);
-  wl_display_flush(connection_->display());
+  connection_->ScheduleFlush();
 }
 
 void WaylandDataDevice::DeferredReadCallback(void* data,
diff --git a/ui/ozone/platform/wayland/host/wayland_data_source.cc b/ui/ozone/platform/wayland/host/wayland_data_source.cc
index dcd6b75aa299..1f41fd6786b9 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_source.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_source.cc
@@ -36,7 +36,7 @@ void WaylandDataSource::WriteToClipboard(
   wl_data_device_set_selection(connection_->data_device(), data_source_.get(),
                                connection_->serial());
 
-  wl_display_flush(connection_->display());
+  connection_->ScheduleFlush();
 }
 
 void WaylandDataSource::UpdateDataMap(
diff --git a/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/ui/ozone/platform/wayland/host/wayland_keyboard.cc
index 181e6e56b4b6..69cd9686bdf8 100644
--- a/ui/ozone/platform/wayland/host/wayland_keyboard.cc
+++ b/ui/ozone/platform/wayland/host/wayland_keyboard.cc
@@ -165,7 +165,7 @@ void WaylandKeyboard::FlushInput(base::OnceClosure closure) {
   // get spurious repeats.
   sync_callback_.reset(wl_display_sync(connection_->display()));
   wl_callback_add_listener(sync_callback_.get(), &callback_listener_, this);
-  wl_display_flush(connection_->display());
+  connection_->ScheduleFlush();
 }
 
 void WaylandKeyboard::DispatchKey(uint32_t key,
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc
index 12b3661985a6..cf0f3c336414 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -285,7 +285,6 @@ void WaylandWindow::ApplyPendingBounds() {
 
   SetBoundsDip(pending_bounds_dip_);
   xdg_surface_->SetWindowGeometry(pending_bounds_dip_);
-  xdg_surface_->AckConfigure();
   pending_bounds_dip_ = gfx::Rect();
   connection_->ScheduleFlush();
 
@@ -659,6 +658,8 @@ void WaylandWindow::HandleSurfaceConfigure(int32_t width,
     delegate_->OnWindowStateChanged(state_);
   }
 
+  ApplyPendingBounds();
+
   if (did_active_change)
     delegate_->OnActivationChanged(is_active_);
 
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index 8083f7a84dcd..5fdf4a6184ba 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -677,19 +677,26 @@ TEST_P(WaylandWindowTest, HasCaptureUpdatedOnPointerEvents) {
 
 TEST_P(WaylandWindowTest, ConfigureEvent) {
   ScopedWlArray states;
-  SendConfigureEvent(1000, 1000, 12, states.get());
-  SendConfigureEvent(1500, 1000, 13, states.get());
 
-  // Make sure that the implementation does not call OnBoundsChanged for each
-  // configure event if it receives multiple in a row.
-  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000))));
+  // The surface must react on each configure event and send bounds to its
+  // delegate.
+
+  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1000, 1000))));
   // Responding to a configure event, the window geometry in here must respect
   // the sizing negotiations specified by the configure event.
   // |xdg_surface_| must receive the following calls in both xdg_shell_v5 and
   // xdg_shell_v6. Other calls like SetTitle or SetMaximized are recieved by
   // xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5.
+  EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1000, 1000)).Times(1);
+  EXPECT_CALL(*xdg_surface_, AckConfigure(12));
+  SendConfigureEvent(1000, 1000, 12, states.get());
+
+  Sync();
+
+  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000))));
   EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1500, 1000)).Times(1);
   EXPECT_CALL(*xdg_surface_, AckConfigure(13));
+  SendConfigureEvent(1500, 1000, 13, states.get());
 }
 
 TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) {
diff --git a/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v5.cc b/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v5.cc
index 7d4172d2f0cb..c89fa87bff9b 100644
--- a/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v5.cc
+++ b/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v5.cc
@@ -99,6 +99,7 @@ void XDGSurfaceWrapperV5::Configure(void* data,
   surface->pending_configure_serial_ = serial;
   surface->wayland_window_->HandleSurfaceConfigure(width, height, is_maximized,
                                                    is_fullscreen, is_activated);
+  surface->AckConfigure();
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v6.cc b/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v6.cc
index 9cac792bf630..c7c9fc079590 100644
--- a/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v6.cc
+++ b/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v6.cc
@@ -123,8 +123,7 @@ void XDGSurfaceWrapperV6::Configure(void* data,
   XDGSurfaceWrapperV6* surface = static_cast<XDGSurfaceWrapperV6*>(data);
   surface->pending_configure_serial_ = serial;
 
-  if (surface->surface_for_popup_)
-    surface->AckConfigure();
+  surface->AckConfigure();
 }
 
 // static
-- 
2.22.0