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
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/exo/wayland/test/test_wayland_client_thread.h"
#include <utility>
#include <poll.h>
#include <wayland-client-core.h>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/task/current_thread.h"
namespace exo::wayland::test {
TestWaylandClientThread::TestWaylandClientThread(const std::string& name)
: Thread(name), controller_(FROM_HERE) {}
TestWaylandClientThread::~TestWaylandClientThread() {
// Guarantee that no new events will come during or after the destruction of
// the display.
stopped_ = true;
task_runner()->PostTask(FROM_HERE,
base::BindOnce(&TestWaylandClientThread::DoCleanUp,
base::Unretained(this)));
// Ensure the task above is run.
FlushForTesting();
}
bool TestWaylandClientThread::Start(
base::OnceCallback<std::unique_ptr<TestClient>()> init_callback) {
base::Thread::Options options;
options.message_pump_type = base::MessagePumpType::IO;
CHECK(base::Thread::StartWithOptions(std::move(options)));
RunAndWait(base::BindOnce(&TestWaylandClientThread::DoInit,
base::Unretained(this), std::move(init_callback)));
return !!client_;
}
void TestWaylandClientThread::RunAndWait(
base::OnceCallback<void(TestClient*)> callback) {
base::OnceClosure closure =
base::BindOnce(std::move(callback), base::Unretained(client_.get()));
RunAndWait(std::move(closure));
}
void TestWaylandClientThread::RunAndWait(base::OnceClosure closure) {
base::RunLoop run_loop;
task_runner()->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&TestWaylandClientThread::DoRun, base::Unretained(this),
std::move(closure)),
run_loop.QuitClosure());
// TODO(crbug.com/40260645): Use busy loop to workaround RunLoop::Run()
// erroneously advancing mock time.
while (!run_loop.AnyQuitCalled()) {
run_loop.RunUntilIdle();
}
}
void TestWaylandClientThread::OnFileCanReadWithoutBlocking(int fd) {
if (stopped_) {
return;
}
if (wl_display_prepare_read(client_->display()) != 0) {
return;
}
// Disconnect can be seen as a read event, and wl_display_prepare_read_queue()
// is used to prevent read from other thread and does not actually check the
// `fd`'s state. Make sure that `fd` has indeed has data to read.
struct pollfd fds;
fds.fd = fd;
fds.events = POLLIN;
fds.revents = 0;
auto ret = poll(&fds, 1, -1);
if (ret != POLLIN) {
wl_display_cancel_read(client_->display());
return;
}
wl_display_read_events(client_->display());
wl_display_dispatch_pending(client_->display());
wl_display_flush(client_->display());
}
void TestWaylandClientThread::OnFileCanWriteWithoutBlocking(int fd) {}
void TestWaylandClientThread::DoInit(
TestWaylandClientThread::InitCallback init_callback) {
client_ = std::move(init_callback).Run();
if (!client_)
return;
const bool result = base::CurrentIOThread::Get().WatchFileDescriptor(
wl_display_get_fd(client_->display()), /*persistent=*/true,
base::MessagePumpEpoll::WATCH_READ, &controller_, this);
if (!result)
client_.reset();
}
void TestWaylandClientThread::DoRun(base::OnceClosure closure) {
std::move(closure).Run();
wl_display_flush(client_->display());
wl_display_roundtrip(client_->display());
}
void TestWaylandClientThread::DoCleanUp() {
controller_.StopWatchingFileDescriptor();
client_.reset();
}
} // namespace exo::wayland::test
|