Compare commits
4 Commits
threadwait
...
threadwait
Author | SHA1 | Date | |
---|---|---|---|
73d098872c | |||
943d264b35 | |||
32dc192883 | |||
7732f1097b |
@ -5,6 +5,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
#include <SDL2/SDL_timer.h>
|
||||
#include <SDL2/SDL_platform.h>
|
||||
@ -318,12 +319,14 @@ connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
|
||||
}
|
||||
|
||||
static void
|
||||
close_socket(socket_t socket) {
|
||||
assert(socket != INVALID_SOCKET);
|
||||
net_shutdown(socket, SHUT_RDWR);
|
||||
if (!net_close(socket)) {
|
||||
close_socket(socket_t *socket) {
|
||||
assert(*socket != INVALID_SOCKET);
|
||||
net_shutdown(*socket, SHUT_RDWR);
|
||||
if (!net_close(*socket)) {
|
||||
LOGW("Could not close socket");
|
||||
return;
|
||||
}
|
||||
*socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void
|
||||
@ -335,14 +338,10 @@ static int
|
||||
run_wait_server(void *data) {
|
||||
struct server *server = data;
|
||||
cmd_simple_wait(server->process, NULL); // ignore exit code
|
||||
// no need for synchronization, server_socket is initialized before this
|
||||
// thread was created
|
||||
if (server->server_socket != INVALID_SOCKET
|
||||
&& SDL_AtomicCAS(&server->server_socket_closed, 0, 1)) {
|
||||
// On Linux, accept() is unblocked by shutdown(), but on Windows, it is
|
||||
// unblocked by closesocket(). Therefore, call both (close_socket()).
|
||||
close_socket(server->server_socket);
|
||||
}
|
||||
|
||||
// wake up any net_select_interruptible()
|
||||
close(server->pipe_intr[1]);
|
||||
|
||||
LOGD("Server terminated");
|
||||
return 0;
|
||||
}
|
||||
@ -367,35 +366,36 @@ server_start(struct server *server, const char *serial,
|
||||
goto error1;
|
||||
}
|
||||
|
||||
// server will connect to our server socket
|
||||
server->process = execute_server(server, params);
|
||||
if (server->process == PROCESS_NONE) {
|
||||
bool ok = net_pipe(server->pipe_intr);
|
||||
if (!ok) {
|
||||
perror("pipe");
|
||||
goto error2;
|
||||
}
|
||||
|
||||
// If the server process dies before connecting to the server socket, then
|
||||
// the client will be stuck forever on accept(). To avoid the problem, we
|
||||
// must be able to wake up the accept() call when the server dies. To keep
|
||||
// things simple and multiplatform, just spawn a new thread waiting for the
|
||||
// server process and calling shutdown()/close() on the server socket if
|
||||
// necessary to wake up any accept() blocking call.
|
||||
// server will connect to our server socket
|
||||
server->process = execute_server(server, params);
|
||||
if (server->process == PROCESS_NONE) {
|
||||
goto error3;
|
||||
}
|
||||
|
||||
server->wait_server_thread =
|
||||
SDL_CreateThread(run_wait_server, "wait-server", server);
|
||||
if (!server->wait_server_thread) {
|
||||
cmd_terminate(server->process);
|
||||
cmd_simple_wait(server->process, NULL); // ignore exit code
|
||||
goto error2;
|
||||
goto error3;
|
||||
}
|
||||
|
||||
server->tunnel_enabled = true;
|
||||
|
||||
return true;
|
||||
|
||||
error3:
|
||||
close(server->pipe_intr[0]);
|
||||
close(server->pipe_intr[1]);
|
||||
error2:
|
||||
if (!server->tunnel_forward) {
|
||||
// the wait server thread is not started, SDL_AtomicSet() is sufficient
|
||||
SDL_AtomicSet(&server->server_socket_closed, 1);
|
||||
close_socket(server->server_socket);
|
||||
close_socket(&server->server_socket);
|
||||
}
|
||||
disable_tunnel(server);
|
||||
error1:
|
||||
@ -406,23 +406,34 @@ error1:
|
||||
bool
|
||||
server_connect_to(struct server *server) {
|
||||
if (!server->tunnel_forward) {
|
||||
bool acceptable = net_select_interruptible(server->server_socket,
|
||||
server->pipe_intr[0]);
|
||||
if (!acceptable) {
|
||||
// the process died, accept() would never succeed
|
||||
return false;
|
||||
}
|
||||
|
||||
server->video_socket = net_accept(server->server_socket);
|
||||
if (server->video_socket == INVALID_SOCKET) {
|
||||
return false;
|
||||
}
|
||||
|
||||
acceptable = net_select_interruptible(server->server_socket,
|
||||
server->pipe_intr[0]);
|
||||
if (!acceptable) {
|
||||
// the process died, accept() would never succeed
|
||||
return false;
|
||||
}
|
||||
server->control_socket = net_accept(server->server_socket);
|
||||
if (server->control_socket == INVALID_SOCKET) {
|
||||
// the video_socket will be cleaned up on destroy
|
||||
return false;
|
||||
}
|
||||
|
||||
close(server->pipe_intr[0]);
|
||||
|
||||
// we don't need the server socket anymore
|
||||
if (SDL_AtomicCAS(&server->server_socket_closed, 0, 1)) {
|
||||
// close it from here
|
||||
close_socket(server->server_socket);
|
||||
// otherwise, it is closed by run_wait_server()
|
||||
}
|
||||
close_socket(&server->server_socket);
|
||||
} else {
|
||||
uint32_t attempts = 100;
|
||||
uint32_t delay = 100; // ms
|
||||
@ -449,15 +460,14 @@ server_connect_to(struct server *server) {
|
||||
|
||||
void
|
||||
server_stop(struct server *server) {
|
||||
if (server->server_socket != INVALID_SOCKET
|
||||
&& SDL_AtomicCAS(&server->server_socket_closed, 0, 1)) {
|
||||
close_socket(server->server_socket);
|
||||
if (server->server_socket != INVALID_SOCKET) {
|
||||
close_socket(&server->server_socket);
|
||||
}
|
||||
if (server->video_socket != INVALID_SOCKET) {
|
||||
close_socket(server->video_socket);
|
||||
close_socket(&server->video_socket);
|
||||
}
|
||||
if (server->control_socket != INVALID_SOCKET) {
|
||||
close_socket(server->control_socket);
|
||||
close_socket(&server->control_socket);
|
||||
}
|
||||
|
||||
assert(server->process != PROCESS_NONE);
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <SDL2/SDL_atomic.h>
|
||||
#include <SDL2/SDL_thread.h>
|
||||
|
||||
#include "config.h"
|
||||
@ -15,7 +14,7 @@ struct server {
|
||||
char *serial;
|
||||
process_t process;
|
||||
SDL_Thread *wait_server_thread;
|
||||
SDL_atomic_t server_socket_closed;
|
||||
int pipe_intr[2]; // to wake up blocking accept() on process exit
|
||||
socket_t server_socket; // only used if !tunnel_forward
|
||||
socket_t video_socket;
|
||||
socket_t control_socket;
|
||||
@ -28,8 +27,6 @@ struct server {
|
||||
#define SERVER_INITIALIZER { \
|
||||
.serial = NULL, \
|
||||
.process = PROCESS_NONE, \
|
||||
.wait_server_thread = NULL, \
|
||||
.server_socket_closed = {0}, \
|
||||
.server_socket = INVALID_SOCKET, \
|
||||
.video_socket = INVALID_SOCKET, \
|
||||
.control_socket = INVALID_SOCKET, \
|
||||
|
@ -1,16 +1,22 @@
|
||||
#include "net.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <SDL2/SDL_platform.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
# include <io.h>
|
||||
# include <winsock2.h>
|
||||
typedef int socklen_t;
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/select.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/types.h>
|
||||
# include <netinet/in.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <unistd.h>
|
||||
@ -145,3 +151,38 @@ net_close(socket_t socket) {
|
||||
return !close(socket);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
net_select_interruptible(int fd, int fd_intr) {
|
||||
fd_set rfds;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
FD_SET(fd_intr, &rfds);
|
||||
|
||||
int nfds = MAX(fd, fd_intr) + 1;
|
||||
|
||||
// use select() because it's available on supported platforms
|
||||
int r = select(nfds, &rfds, NULL, NULL, NULL);
|
||||
if (r == -1) {
|
||||
// failure
|
||||
return false;
|
||||
}
|
||||
assert(r > 0);
|
||||
if (FD_ISSET(fd_intr, &rfds)) {
|
||||
// interrupted is set
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(FD_ISSET(fd, &rfds));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
net_pipe(int fds[static 2]) {
|
||||
#ifdef __WINDOWS__
|
||||
return !_pipe(fds, 4096, 0);
|
||||
#else
|
||||
return !pipe(fds);
|
||||
#endif
|
||||
}
|
||||
|
@ -54,4 +54,12 @@ net_shutdown(socket_t socket, int how);
|
||||
bool
|
||||
net_close(socket_t socket);
|
||||
|
||||
// wait for fd or fd_intr to be readable
|
||||
// return true if fd is readable and fd_intr is not
|
||||
bool
|
||||
net_select_interruptible(int fd, int fd_intr);
|
||||
|
||||
bool
|
||||
net_pipe(int fd[static 2]);
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user