Skip to content

Commit

Permalink
Merge #1151
Browse files Browse the repository at this point in the history
1151: XWayland refactor and fix r=AlanGriffiths a=wmww

Previously, `XWaylandServer::spawn()` forked, and handled both forks in a large switch statement. This splits the two branches into `XWaylandServer::execl_xwayland()` and `XWaylandServer::connect_to_xwayland()`. Also runs `wl_client_create()` on the Wayland thread to fix #1147. It may be helpful to look over the commits individually.

Co-authored-by: William Wold <[email protected]>
  • Loading branch information
bors[bot] and wmww authored Dec 16, 2019
2 parents 455947e + 90caa91 commit f3cf65b
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 93 deletions.
212 changes: 119 additions & 93 deletions src/server/frontend_xwayland/xwayland_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,8 @@ mf::XWaylandServer::~XWaylandServer()

void mf::XWaylandServer::spawn()
{
int fd;
int wl_client_fd[2], wm_fd[2];
int status;
std::string fd_str, abs_fd_str, wm_fd_str;
enum { server, client, size };
int wl_client_fd[size], wm_fd[size];

xserver_status = STARTING;

Expand All @@ -115,10 +113,6 @@ void mf::XWaylandServer::spawn()
return;
}

std::ostringstream _dsp_str;
_dsp_str << ":" << xdisplay;
auto dsp_str = _dsp_str.str();

// This is not pretty! but SIGUSR1 is the only way we know the xserver
// is ready to accept connections to the wm fd
// If not it will have a race condition on start that might end
Expand All @@ -130,110 +124,142 @@ void mf::XWaylandServer::spawn()

mir::log_info("Starting Xwayland");
pid = fork();

switch (pid)
{
case -1:
mir::log_error("Failed to fork");
break;

case 0:
fd = dup(wl_client_fd[1]);
if (fd < 0)
mir::log_error("Failed to duplicate xwayland FD");
setenv("WAYLAND_SOCKET", std::to_string(fd).c_str(), 1);
setenv("EGL_PLATFORM", "DRM", 1);
close(wl_client_fd[server]);
close(wm_fd[server]);
execl_xwayland(wl_client_fd[client], wm_fd[client]);
break;

set_cloexec(socket_fd, false);
set_cloexec(abstract_socket_fd, false);
default:
close(wl_client_fd[client]);
close(wm_fd[client]);
connect_to_xwayland(wl_client_fd[server], wm_fd[server]);
break;
}
}

fd = dup(socket_fd);
if (fd < 0)
mir::log_error("Failed to duplicate xwayland FD");
fd_str = std::to_string(fd);
void mf::XWaylandServer::execl_xwayland(int wl_client_client_fd, int wm_client_fd)
{
setenv("EGL_PLATFORM", "DRM", 1);

auto const wl_client_fd = dup(wl_client_client_fd);
if (wl_client_fd < 0)
mir::log_error("Failed to duplicate xwayland FD");
else
setenv("WAYLAND_SOCKET", std::to_string(wl_client_fd).c_str(), 1);

set_cloexec(socket_fd, false);
set_cloexec(abstract_socket_fd, false);

auto const socket_fd = dup(this->socket_fd);
if (socket_fd < 0)
mir::log_error("Failed to duplicate xwayland FD");
auto const socket_fd_str = std::to_string(socket_fd);

auto const abstract_socket_fd = dup(this->abstract_socket_fd);
if (abstract_socket_fd < 0)
mir::log_error("Failed to duplicate xwayland abstract FD");
auto const abstract_socket_fd_str = std::to_string(abstract_socket_fd);

auto const wm_fd = dup(wm_client_fd);
if (wm_fd < 0)
mir::log_error("Failed to duplicate xwayland wm FD");
auto const wm_fd_str = std::to_string(wm_fd);

// forward SIGUSR1 to parent thread (us)
signal(SIGUSR1, SIG_IGN);

// Last second abort
if (terminate) return;

auto const dsp_str = ":" + std::to_string(xdisplay);

execl(xwayland_path.c_str(),
"Xwayland",
dsp_str.c_str(),
"-rootless",
"-listen", abstract_socket_fd_str.c_str(),
"-listen", socket_fd_str.c_str(),
"-wm", wm_fd_str.c_str(),
"-terminate",
NULL);
}

fd = dup(abstract_socket_fd);
if (fd < 0)
mir::log_error("Failed to duplicate xwayland abstract FD");
abs_fd_str = std::to_string(fd);
void mf::XWaylandServer::connect_to_xwayland(int wl_client_server_fd, int wm_server_fd)
{
wl_client* client = nullptr;
std::mutex client_mutex;
std::condition_variable client_ready;

fd = dup(wm_fd[1]);
if (fd < 0)
mir::log_error("Failed to duplicate xwayland wm FD");
wm_fd_str = std::to_string(fd);
wlc->run_on_wayland_display([wl_client_server_fd, &client, &client_mutex, &client_ready](wl_display* display)
{
std::lock_guard<std::mutex> lock{client_mutex};
client = wl_client_create(display, wl_client_server_fd);
client_ready.notify_all();
});

// forward SIGUSR1 to parent thread (us)
signal(SIGUSR1, SIG_IGN);
std::unique_lock<std::mutex> lock{client_mutex};
client_ready.wait(lock);

// More ugliness
int tries = 0;
while (!xserver_ready)
{
// Last second abort
if (terminate) return;

execl(xwayland_path.c_str(),
"Xwayland",
dsp_str.c_str(),
"-rootless",
"-listen", abs_fd_str.c_str(),
"-listen", fd_str.c_str(),
"-wm", wm_fd_str.c_str(),
"-terminate",
NULL);
break;
case -1:
mir::log_error("Failed to fork");
break;
default:
close(wl_client_fd[1]);
close(wm_fd[1]);
auto wlclient = wl_client_create(wlc->get_wl_display(), wl_client_fd[0]);

// More ugliness
int tries = 0;
while (!xserver_ready)
{
// Last second abort
if (terminate) return;

// Check for stalled startup
if (waitpid(pid, NULL, WNOHANG) != 0 || tries > 200) {
xserver_status = FAILED;
mir::log_info("Stalled start of Xserver, trying to start again!");
spawn();
return;
}

std::this_thread::sleep_for(std::chrono::milliseconds(10));
tries++;
// Check for stalled startup
if (waitpid(pid, NULL, WNOHANG) != 0 || tries > 200) {
xserver_status = FAILED;
mir::log_info("Stalled start of Xserver, trying to start again!");
spawn();
return;
}

// Last second abort
if (terminate) return;
wm->start(wlclient, wm_fd[0]);
mir::log_info("XServer is running");
xserver_status = RUNNING;

// Reset the tries since server is running now
xserver_spawn_tries = 0;

waitpid(pid, &status, 0); // Blocking
if (WIFEXITED(status)) {
mir::log_info("Xserver stopped");
xserver_status = STOPPED;
} else {
// Failed, crash or killed
mir::log_info("Xserver crashed or got killed");
xserver_status = FAILED;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
tries++;
}

if (terminate) return;
wm->destroy();
xserver_ready = false;
// Last second abort
if (terminate) return;
wm->start(client, wm_server_fd);
mir::log_info("XServer is running");
xserver_status = RUNNING;

if (xserver_status == FAILED) {
mir::log_info("Trying to start Xwayland again!");
spawn();
return;
}
// Reset the tries since server is running now
xserver_spawn_tries = 0;

spawn_xserver_on_event_loop();
int status;
waitpid(pid, &status, 0); // Blocking
if (WIFEXITED(status)) {
mir::log_info("Xserver stopped");
xserver_status = STOPPED;
} else {
// Failed, crash or killed
mir::log_info("Xserver crashed or got killed");
xserver_status = FAILED;
}

mir::log_info("Xwayland stopped");
break;
if (terminate) return;
wm->destroy();
xserver_ready = false;

if (xserver_status == FAILED) {
mir::log_info("Trying to start Xwayland again!");
spawn();
return;
}

spawn_xserver_on_event_loop();

mir::log_info("Xwayland stopped");
}

bool mf::XWaylandServer::set_cloexec(int fd, bool cloexec) {
Expand Down
5 changes: 5 additions & 0 deletions src/server/frontend_xwayland/xwayland_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ class XWaylandServer
static bool xserver_ready;

private:
/// Forks off the XWayland process
void spawn();
/// Called after fork() if we should turn into XWayland
void execl_xwayland(int wl_client_client_fd, int wm_client_fd);
/// Called after fork() if we should continue on as Mir
void connect_to_xwayland(int wl_client_server_fd, int wm_server_fd);
void new_spawn_thread();
int create_lockfile();
int create_socket(struct sockaddr_un *addr, size_t path_size);
Expand Down

0 comments on commit f3cf65b

Please sign in to comment.