ipc/server: Refactor out per_client_thread

This commit is contained in:
Jakob Bornecrantz 2020-12-09 18:32:03 +00:00
parent 743c289be5
commit 1154e58d06
4 changed files with 206 additions and 193 deletions

View file

@ -64,6 +64,7 @@ add_library(ipc_server STATIC
${IPC_COMMON_SOURCES}
server/ipc_server.h
server/ipc_server_client.c
server/ipc_server_per_client_thread.c
server/ipc_server_process.c
)
target_include_directories(ipc_server

View file

@ -62,6 +62,7 @@ lib_ipc_server = static_library(
generated[4],
'server/ipc_server.h',
'server/ipc_server_client.c',
'server/ipc_server_per_client_thread.c',
'server/ipc_server_process.c',
],
include_directories: [

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Common server side code.
* @brief Handling functions called from generated dispatch function.
* @author Pete Black <pblack@collabora.com>
* @ingroup ipc_server
*/
@ -14,20 +14,6 @@
#include "server/ipc_server.h"
#include "ipc_server_generated.h"
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <sys/epoll.h>
/*
*
@ -642,181 +628,3 @@ ipc_handle_device_set_output(volatile struct ipc_client_state *ics,
return XRT_SUCCESS;
}
/*
*
* Helper functions.
*
*/
static int
setup_epoll(int listen_socket)
{
int ret = epoll_create1(EPOLL_CLOEXEC);
if (ret < 0) {
return ret;
}
int epoll_fd = ret;
struct epoll_event ev = {0};
ev.events = EPOLLIN;
ev.data.fd = listen_socket;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_socket, &ev);
if (ret < 0) {
fprintf(stderr, "ERROR: epoll_ctl(listen_socket) failed '%i'\n",
ret);
return ret;
}
return epoll_fd;
}
/*
*
* Client loop.
*
*/
static void
client_loop(volatile struct ipc_client_state *ics)
{
fprintf(stderr, "SERVER: Client connected\n");
// Make sure it's ready for the client.
u_rt_helper_client_clear((struct u_rt_helper *)&ics->urth);
// Claim the client fd.
int epoll_fd = setup_epoll(ics->imc.socket_fd);
if (epoll_fd < 0) {
return;
}
uint8_t buf[IPC_BUF_SIZE];
while (ics->server->running) {
const int half_a_second_ms = 500;
struct epoll_event event = {0};
// We use epoll here to be able to timeout.
int ret = epoll_wait(epoll_fd, &event, 1, half_a_second_ms);
if (ret < 0) {
fprintf(stderr,
"ERROR: Failed epoll_wait '%i', disconnecting "
"client.\n",
ret);
break;
}
// Timed out, loop again.
if (ret == 0) {
continue;
}
// Detect clients disconnecting gracefully.
if (ret > 0 && (event.events & EPOLLHUP) != 0) {
fprintf(stderr, "SERVER: Client disconnected\n");
break;
}
// Finally get the data that is waiting for us.
//! @todo replace this call
ssize_t len = recv(ics->imc.socket_fd, &buf, IPC_BUF_SIZE, 0);
if (len < 4) {
fprintf(stderr,
"ERROR: Invalid packet received, "
"disconnecting "
"client.\n");
break;
}
// Check the first 4 bytes of the message and dispatch.
ipc_command_t *ipc_command = (uint32_t *)buf;
xrt_result_t result = ipc_dispatch(ics, ipc_command);
if (result != XRT_SUCCESS) {
fprintf(stderr,
"ERROR: During packet handling, "
"disconnecting "
"client.\n");
break;
}
}
close(epoll_fd);
epoll_fd = -1;
// Multiple threads might be looking at these fields.
os_mutex_lock(&ics->server->global_state_lock);
ipc_message_channel_close((struct ipc_message_channel *)&ics->imc);
// Reset the urth for the next client.
u_rt_helper_client_clear((struct u_rt_helper *)&ics->urth);
ics->num_swapchains = 0;
ics->server->threads[ics->server_thread_index].state =
IPC_THREAD_STOPPING;
ics->server_thread_index = -1;
memset((void *)&ics->client_state, 0, sizeof(struct ipc_app_state));
// Make sure to reset the renderstate fully.
ics->rendering_state = false;
ics->render_state.num_layers = 0;
for (uint32_t i = 0; i < ARRAY_SIZE(ics->render_state.layers); ++i) {
volatile struct ipc_layer_entry *rl =
&ics->render_state.layers[i];
rl->swapchain_ids[0] = 0;
rl->swapchain_ids[1] = 0;
rl->data.flip_y = false;
/*!
* @todo this is redundant, we're setting both elements of a
* union. Why? Can we just zero the whole render_state?
*/
rl->data.stereo.l.sub.image_index = 0;
rl->data.stereo.r.sub.image_index = 0;
rl->data.quad.sub.image_index = 0;
rl->data.cube.sub.image_index = 0;
rl->data.cylinder.sub.image_index = 0;
rl->data.equirect.sub.image_index = 0;
//! @todo set rects or array index?
}
// Destroy all swapchains now.
for (uint32_t j = 0; j < IPC_MAX_CLIENT_SWAPCHAINS; j++) {
xrt_swapchain_destroy((struct xrt_swapchain **)&ics->xscs[j]);
ics->swapchain_data[j].active = false;
IPC_TRACE(ics->server, "IPC: Destroyed swapchain %d\n", j);
}
os_mutex_unlock(&ics->server->global_state_lock);
// Should we stop the server when a client disconnects?
if (ics->server->exit_on_disconnect) {
ics->server->running = false;
}
}
/*
*
* Entry point.
*
*/
void *
ipc_server_client_thread(void *_ics)
{
volatile struct ipc_client_state *ics = _ics;
client_loop(ics);
update_server_state(ics->server);
return NULL;
}

View file

@ -0,0 +1,203 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Per client thread listening on the socket.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_server
*/
#include "xrt/xrt_gfx_native.h"
#include "util/u_misc.h"
#include "server/ipc_server.h"
#include "ipc_server_generated.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <sys/socket.h>
/*
*
* Helper functions.
*
*/
static int
setup_epoll(int listen_socket)
{
int ret = epoll_create1(EPOLL_CLOEXEC);
if (ret < 0) {
return ret;
}
int epoll_fd = ret;
struct epoll_event ev = {0};
ev.events = EPOLLIN;
ev.data.fd = listen_socket;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_socket, &ev);
if (ret < 0) {
fprintf(stderr, "ERROR: epoll_ctl(listen_socket) failed '%i'\n",
ret);
return ret;
}
return epoll_fd;
}
/*
*
* Client loop.
*
*/
static void
client_loop(volatile struct ipc_client_state *ics)
{
fprintf(stderr, "SERVER: Client connected\n");
// Make sure it's ready for the client.
u_rt_helper_client_clear((struct u_rt_helper *)&ics->urth);
// Claim the client fd.
int epoll_fd = setup_epoll(ics->imc.socket_fd);
if (epoll_fd < 0) {
return;
}
uint8_t buf[IPC_BUF_SIZE];
while (ics->server->running) {
const int half_a_second_ms = 500;
struct epoll_event event = {0};
// We use epoll here to be able to timeout.
int ret = epoll_wait(epoll_fd, &event, 1, half_a_second_ms);
if (ret < 0) {
fprintf(stderr,
"ERROR: Failed epoll_wait '%i', disconnecting "
"client.\n",
ret);
break;
}
// Timed out, loop again.
if (ret == 0) {
continue;
}
// Detect clients disconnecting gracefully.
if (ret > 0 && (event.events & EPOLLHUP) != 0) {
fprintf(stderr, "SERVER: Client disconnected\n");
break;
}
// Finally get the data that is waiting for us.
//! @todo replace this call
ssize_t len = recv(ics->imc.socket_fd, &buf, IPC_BUF_SIZE, 0);
if (len < 4) {
fprintf(stderr,
"ERROR: Invalid packet received, "
"disconnecting "
"client.\n");
break;
}
// Check the first 4 bytes of the message and dispatch.
ipc_command_t *ipc_command = (uint32_t *)buf;
xrt_result_t result = ipc_dispatch(ics, ipc_command);
if (result != XRT_SUCCESS) {
fprintf(stderr,
"ERROR: During packet handling, "
"disconnecting "
"client.\n");
break;
}
}
close(epoll_fd);
epoll_fd = -1;
// Multiple threads might be looking at these fields.
os_mutex_lock(&ics->server->global_state_lock);
ipc_message_channel_close((struct ipc_message_channel *)&ics->imc);
// Reset the urth for the next client.
u_rt_helper_client_clear((struct u_rt_helper *)&ics->urth);
ics->num_swapchains = 0;
ics->server->threads[ics->server_thread_index].state =
IPC_THREAD_STOPPING;
ics->server_thread_index = -1;
memset((void *)&ics->client_state, 0, sizeof(struct ipc_app_state));
// Make sure to reset the renderstate fully.
ics->rendering_state = false;
ics->render_state.num_layers = 0;
for (uint32_t i = 0; i < ARRAY_SIZE(ics->render_state.layers); ++i) {
volatile struct ipc_layer_entry *rl =
&ics->render_state.layers[i];
rl->swapchain_ids[0] = 0;
rl->swapchain_ids[1] = 0;
rl->data.flip_y = false;
/*!
* @todo this is redundant, we're setting both elements of a
* union. Why? Can we just zero the whole render_state?
*/
rl->data.stereo.l.sub.image_index = 0;
rl->data.stereo.r.sub.image_index = 0;
rl->data.quad.sub.image_index = 0;
rl->data.cube.sub.image_index = 0;
rl->data.cylinder.sub.image_index = 0;
rl->data.equirect.sub.image_index = 0;
//! @todo set rects or array index?
}
// Destroy all swapchains now.
for (uint32_t j = 0; j < IPC_MAX_CLIENT_SWAPCHAINS; j++) {
xrt_swapchain_destroy((struct xrt_swapchain **)&ics->xscs[j]);
ics->swapchain_data[j].active = false;
IPC_TRACE(ics->server, "IPC: Destroyed swapchain %d\n", j);
}
os_mutex_unlock(&ics->server->global_state_lock);
// Should we stop the server when a client disconnects?
if (ics->server->exit_on_disconnect) {
ics->server->running = false;
}
}
/*
*
* Entry point.
*
*/
void *
ipc_server_client_thread(void *_ics)
{
volatile struct ipc_client_state *ics = _ics;
client_loop(ics);
update_server_state(ics->server);
return NULL;
}