vtest: overhaul vtest_server_run_renderer

Add vtest_server_run to replace vtest_server_run_renderer.  The new
function is the main loop of the server, which

 - accepts new client connections
 - dispatches client commands
 - removes inactive clients
 - lazily initi/cleanup the renderer
 - optionally forks for each connection

in a single loop.

Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-By: Gert Wollny <gert.wollny@collabora.com>
Reviewed-By: Gurchetan Singh <gurchetansingh@chromium.org>
macos/master
Chia-I Wu 5 years ago
parent 97c2225912
commit 4150a90ca3
  1. 251
      vtest/vtest_server.c

@ -36,6 +36,7 @@
#include "util.h" #include "util.h"
#include "util/u_double_list.h" #include "util/u_double_list.h"
#include "util/u_math.h"
#include "util/u_memory.h" #include "util/u_memory.h"
#include "vtest.h" #include "vtest.h"
#include "vtest_protocol.h" #include "vtest_protocol.h"
@ -52,6 +53,7 @@ struct vtest_client
struct list_head head; struct list_head head;
bool in_fd_ready;
struct vtest_context *context; struct vtest_context *context;
}; };
@ -73,7 +75,9 @@ struct vtest_server
int ctx_flags; int ctx_flags;
struct list_head new_clients;
struct list_head active_clients; struct list_head active_clients;
struct list_head inactive_clients;
}; };
struct vtest_server server = { struct vtest_server server = {
@ -97,9 +101,7 @@ static void vtest_server_set_signal_child(void);
static void vtest_server_set_signal_segv(void); static void vtest_server_set_signal_segv(void);
static void vtest_server_open_read_file(void); static void vtest_server_open_read_file(void);
static void vtest_server_open_socket(void); static void vtest_server_open_socket(void);
static void vtest_server_run_renderer(void); static void vtest_server_run(void);
static void vtest_server_wait_for_socket_accept(void);
static void vtest_server_tidy_fds(void);
static void vtest_server_close_socket(void); static void vtest_server_close_socket(void);
static int vtest_client_dispatch_commands(struct vtest_client *client); static int vtest_client_dispatch_commands(struct vtest_client *client);
@ -113,7 +115,9 @@ while (__AFL_LOOP(1000)) {
vtest_server_getenv(); vtest_server_getenv();
vtest_server_parse_args(argc, argv); vtest_server_parse_args(argc, argv);
list_inithead(&server.new_clients);
list_inithead(&server.active_clients); list_inithead(&server.active_clients);
list_inithead(&server.inactive_clients);
if (server.do_fork) { if (server.do_fork) {
vtest_server_set_signal_child(); vtest_server_set_signal_child();
@ -121,21 +125,7 @@ while (__AFL_LOOP(1000)) {
vtest_server_set_signal_segv(); vtest_server_set_signal_segv();
} }
if (server.read_file != NULL) { vtest_server_run();
vtest_server_open_read_file();
vtest_server_run_renderer();
vtest_server_tidy_fds();
} else {
vtest_server_open_socket();
do {
vtest_server_wait_for_socket_accept();
vtest_server_run_renderer();
vtest_server_tidy_fds();
} while (server.loop);
vtest_server_close_socket();
}
#ifdef __AFL_LOOP #ifdef __AFL_LOOP
if (!server.main_server) { if (!server.main_server) {
@ -289,7 +279,7 @@ static int vtest_server_add_client(int in_fd, int out_fd)
client->input.data.fd = in_fd; client->input.data.fd = in_fd;
client->input.read = vtest_block_read; client->input.read = vtest_block_read;
list_addtail(&client->head, &server.active_clients); list_addtail(&client->head, &server.new_clients);
return 0; return 0;
} }
@ -348,34 +338,78 @@ err:
exit(1); exit(1);
} }
static void vtest_server_wait_for_socket_accept(void) static void vtest_server_wait_clients(void)
{ {
struct vtest_client *client;
fd_set read_fds; fd_set read_fds;
int new_fd; int max_fd = -1;
int ret; int ret;
FD_ZERO(&read_fds); FD_ZERO(&read_fds);
FD_SET(server.socket, &read_fds);
ret = select(server.socket + 1, &read_fds, NULL, NULL, NULL); LIST_FOR_EACH_ENTRY(client, &server.active_clients, head) {
FD_SET(client->in_fd, &read_fds);
max_fd = MAX2(client->in_fd, max_fd);
}
/* accept new clients when there is none */
if (server.socket >= 0 && max_fd < 0) {
FD_SET(server.socket, &read_fds);
max_fd = MAX2(server.socket, max_fd);
}
if (max_fd < 0) {
if (!LIST_IS_EMPTY(&server.new_clients)) {
return;
}
fprintf(stderr, "server has no fd to wait\n");
exit(1);
}
ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
if (ret < 0) { if (ret < 0) {
perror("Failed to select on socket!"); perror("Failed to select on socket!");
exit(1); exit(1);
} }
if (!FD_ISSET(server.socket, &read_fds)) { LIST_FOR_EACH_ENTRY(client, &server.active_clients, head) {
perror("Odd state in fd_set."); if (FD_ISSET(client->in_fd, &read_fds)) {
exit(1); client->in_fd_ready = true;
}
} }
new_fd = accept(server.socket, NULL, NULL); if (server.socket >= 0 && FD_ISSET(server.socket, &read_fds)) {
if (new_fd < 0) { int new_fd = accept(server.socket, NULL, NULL);
perror("Failed to accept socket."); if (new_fd < 0) {
exit(1); perror("Failed to accept socket.");
exit(1);
}
if (vtest_server_add_client(new_fd, new_fd)) {
perror("Failed to add client.");
exit(1);
}
} }
}
if (vtest_server_add_client(new_fd, new_fd)) { static void vtest_server_dispatch_clients(void)
perror("Failed to add client."); {
exit(1); struct vtest_client *client, *tmp;
LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.active_clients, head) {
int err;
if (!client->in_fd_ready)
continue;
client->in_fd_ready = false;
err = vtest_client_dispatch_commands(client);
if (err) {
fprintf(stderr, "client failed: err %d\n", err);
list_del(&client->head);
list_addtail(&client->head, &server.inactive_clients);
}
} }
} }
@ -386,6 +420,7 @@ static pid_t vtest_server_fork(void)
if (pid == 0) { if (pid == 0) {
/* child */ /* child */
vtest_server_set_signal_segv(); vtest_server_set_signal_segv();
vtest_server_close_socket();
server.main_server = false; server.main_server = false;
server.do_fork = false; server.do_fork = false;
server.loop = false; server.loop = false;
@ -394,45 +429,119 @@ static pid_t vtest_server_fork(void)
return pid; return pid;
} }
static void vtest_server_run_renderer(void) static void vtest_server_fork_clients(void)
{ {
struct vtest_client *client = struct vtest_client *client, *tmp;
LIST_ENTRY(struct vtest_client, server.active_clients.next, head);
int err, ret;
if (server.do_fork) { LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.new_clients, head) {
if (vtest_server_fork()) { if (vtest_server_fork()) {
/* parent */ /* parent: move new clients to the inactive list */
return; list_del(&client->head);
list_addtail(&client->head, &server.inactive_clients);
} else {
/* child: move the first new client to the active list */
list_del(&client->head);
list_addtail(&client->head, &server.active_clients);
/* move the rest new clients to the inactive list */
LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.new_clients, head) {
list_del(&client->head);
list_addtail(&client->head, &server.inactive_clients);
}
} }
} }
}
ret = vtest_init_renderer(server.ctx_flags, server.render_device); static void vtest_server_activate_clients(void)
if (ret) { {
return; struct vtest_client *client, *tmp;
/* move new clients to the active list */
LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.new_clients, head) {
list_addtail(&client->head, &server.active_clients);
} }
list_inithead(&server.new_clients);
}
do { static void vtest_server_inactivate_clients(void)
ret = vtest_wait_for_fd_read(client->in_fd); {
if (ret < 0) { struct vtest_client *client, *tmp;
err = 1;
break; /* move active clients to the inactive list */
LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.active_clients, head) {
list_addtail(&client->head, &server.inactive_clients);
}
list_inithead(&server.active_clients);
}
static void vtest_server_tidy_clients(void)
{
struct vtest_client *client, *tmp;
LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.inactive_clients, head) {
if (client->context) {
vtest_destroy_context(client->context);
} }
err = vtest_client_dispatch_commands(client); if (client->in_fd >= 0) {
if (err) { close(client->in_fd);
break;
} }
} while (1);
fprintf(stderr, "socket failed (%d) - closing renderer\n", err); if (client->out_fd >= 0 && client->out_fd != client->in_fd) {
close(client->out_fd);
}
if (client->context) { free(client);
vtest_destroy_context(client->context);
client->context = NULL;
} }
vtest_cleanup_renderer(); list_inithead(&server.inactive_clients);
}
static void vtest_server_run(void)
{
bool run = true;
if (server.read_file) {
vtest_server_open_read_file();
} else {
vtest_server_open_socket();
}
while (run) {
const bool was_empty = LIST_IS_EMPTY(&server.active_clients);
bool is_empty;
vtest_server_wait_clients();
vtest_server_dispatch_clients();
if (server.do_fork) {
vtest_server_fork_clients();
} else {
vtest_server_activate_clients();
}
/* init renderer after the first active client is added */
is_empty = LIST_IS_EMPTY(&server.active_clients);
if (was_empty && !is_empty) {
int ret = vtest_init_renderer(server.ctx_flags, server.render_device);
if (ret) {
vtest_server_inactivate_clients();
run = false;
}
}
vtest_server_tidy_clients();
/* clean up renderer after the last active client is removed */
if (!was_empty && is_empty) {
vtest_cleanup_renderer();
if (!server.loop) {
run = false;
}
}
}
vtest_server_close_socket();
} }
typedef int (*vtest_cmd_fptr_t)(uint32_t); typedef int (*vtest_cmd_fptr_t)(uint32_t);
@ -477,7 +586,6 @@ static int vtest_client_dispatch_commands(struct vtest_client *client)
return 4; return 4;
} }
printf("%s: client context created.\n", __func__); printf("%s: client context created.\n", __func__);
vtest_set_current_context(client->context);
vtest_poll(); vtest_poll();
return 0; return 0;
@ -492,6 +600,8 @@ static int vtest_client_dispatch_commands(struct vtest_client *client)
return 6; return 6;
} }
vtest_set_current_context(client->context);
ret = vtest_commands[header[1]](header[0]); ret = vtest_commands[header[1]](header[0]);
if (ret < 0) { if (ret < 0) {
return 7; return 7;
@ -505,31 +615,6 @@ static int vtest_client_dispatch_commands(struct vtest_client *client)
return 0; return 0;
} }
static void vtest_server_tidy_fds(void)
{
struct vtest_client *client =
LIST_ENTRY(struct vtest_client, server.active_clients.next, head);
// out_fd will be closed by the in_fd clause if they are the same.
if (client->out_fd == client->in_fd) {
client->out_fd = -1;
}
if (client->in_fd != -1) {
close(client->in_fd);
client->in_fd = -1;
client->input.read = NULL;
}
if (client->out_fd != -1) {
close(client->out_fd);
client->out_fd = -1;
}
list_del(&client->head);
free(client);
}
static void vtest_server_close_socket(void) static void vtest_server_close_socket(void)
{ {
if (server.socket != -1) { if (server.socket != -1) {

Loading…
Cancel
Save