localsocket: fix backlog implementation

This commit is contained in:
Matheus
2026-06-15 21:57:11 -03:00
parent 6eaa3223d1
commit 00f37d5228
+59 -45
View File
@@ -27,10 +27,8 @@ typedef struct {
struct localpair_t *pair; struct localpair_t *pair;
// listening server // listening server
struct binding_t *binding; struct binding_t *binding;
struct localpair_t **backlog; // shared with binding ringbuffer_t backlog; // shared with binding
int backlogsize; // shared with binding bool listening; // shared with binding
uintmax_t backlogcurrentwrite; // shared with binding
uintmax_t backlogcurrentread; // shared with binding
char *bindpath; char *bindpath;
ringbuffer_t fds; // shared with pair ringbuffer_t fds; // shared with pair
uintmax_t barriercurrent; // shared with pair uintmax_t barriercurrent; // shared with pair
@@ -65,15 +63,23 @@ typedef struct binding_t {
mutex_t mutex; mutex_t mutex;
} binding_t; } binding_t;
static inline bool backlog_has_data(localsocket_t *server) {
return RINGBUFFER_DATACOUNT(&server->backlog) >= sizeof(localpair_t *);
}
static inline bool backlog_has_space(localsocket_t *server) {
return RINGBUFFER_FREESPACE(&server->backlog) >= sizeof(localpair_t *);
}
static inline void pushbacklog(localsocket_t *server, localpair_t *pair) { static inline void pushbacklog(localsocket_t *server, localpair_t *pair) {
__assert(server->backlogcurrentwrite < server->backlogcurrentread + server->backlogsize); __assert(backlog_has_space(server));
server->backlog[server->backlogcurrentwrite++] = pair; __assert(ringbuffer_write(&server->backlog, &pair, sizeof(pair)) == sizeof(pair));
} }
static inline localpair_t *popbacklog(localsocket_t *server) { static inline localpair_t *popbacklog(localsocket_t *server) {
__assert(server->backlogcurrentread < server->backlogcurrentwrite); __assert(backlog_has_data(server));
localpair_t *pair = server->backlog[server->backlogcurrentread]; localpair_t *pair = NULL;
server->backlog[server->backlogcurrentread++] = NULL; __assert(ringbuffer_read(&server->backlog, &pair, sizeof(pair)) == sizeof(pair));
return pair; return pair;
} }
@@ -159,11 +165,11 @@ static int datapoll(localsocket_t *local, localsocket_t *peer, int events) {
static int listeningpoll(localsocket_t *localsocket, int events) { static int listeningpoll(localsocket_t *localsocket, int events) {
int revents = 0; int revents = 0;
// can insert into backlog? // can insert into backlog?
if (localsocket->backlogcurrentwrite < localsocket->backlogcurrentread + localsocket->backlogsize) if (backlog_has_space(localsocket))
revents |= (events & POLLOUT) ? POLLOUT : 0; revents |= (events & POLLOUT) ? POLLOUT : 0;
// can accept()? // can accept()?
if (localsocket->backlogcurrentwrite != localsocket->backlogcurrentread) if (backlog_has_data(localsocket))
revents |= (events & POLLIN) ? POLLIN : 0; revents |= (events & POLLIN) ? POLLIN : 0;
return revents; return revents;
@@ -179,7 +185,7 @@ static int clientpoll(localsocket_t *localsocket, int events) {
// the client has one more possible poll check: // the client has one more possible poll check:
// it can still be connecting with the other end // it can still be connecting with the other end
// so check if server has a backlog. if so, nothing can be done yet // so check if server has a backlog. if so, nothing can be done yet
if (server->backlog) if (server->listening)
return 0; return 0;
return datapoll(localsocket, server, events); return datapoll(localsocket, server, events);
@@ -201,8 +207,8 @@ static int internalpoll(socket_t *socket, polldata_t *data, int events) {
int revents = 0; int revents = 0;
// 4 possible cases: // 4 possible cases:
// socket is listening // socket is listening
if (localsocket->backlog) if (localsocket->listening)
revents = listeningpoll(localsocket, events); revents = listeningpoll(localsocket, events);
// socket is client // socket is client
else if (localsocket->pair && localsocket->pair->client == localsocket) else if (localsocket->pair && localsocket->pair->client == localsocket)
@@ -626,13 +632,13 @@ static int localsock_accept(socket_t *_server, socket_t *_clientconnection, sock
int error; int error;
if (binding == NULL) { if (binding == NULL) {
error = EINVAL; MUTEX_RELEASE(&server->socket.mutex);
goto leave; return EINVAL;
} }
MUTEX_ACQUIRE(&server->binding->mutex); MUTEX_ACQUIRE(&server->binding->mutex);
if (server->backlog == NULL) { if (server->listening == false) {
error = EINVAL; error = EINVAL;
goto leave; goto leave;
} }
@@ -679,7 +685,7 @@ static int localsock_accept(socket_t *_server, socket_t *_clientconnection, sock
poll_destroydesc(&desc); poll_destroydesc(&desc);
if (error) if (error)
goto leave; goto leave_nobinding;
MUTEX_ACQUIRE(&binding->mutex); MUTEX_ACQUIRE(&binding->mutex);
} }
@@ -699,9 +705,8 @@ static int localsock_accept(socket_t *_server, socket_t *_clientconnection, sock
MUTEX_RELEASE(&pair->mutex); MUTEX_RELEASE(&pair->mutex);
leave: leave:
if (binding) MUTEX_RELEASE(&binding->mutex);
MUTEX_RELEASE(&binding->mutex); leave_nobinding:
MUTEX_RELEASE(&server->socket.mutex); MUTEX_RELEASE(&server->socket.mutex);
return error; return error;
} }
@@ -715,7 +720,7 @@ static int localsock_connect(socket_t *socket, sockaddr_t *addr, uintmax_t flags
MUTEX_ACQUIRE(&socket->mutex); MUTEX_ACQUIRE(&socket->mutex);
// listening/bound socket // listening/bound socket
if (localsocket->backlog || localsocket->binding) { if (localsocket->listening || localsocket->binding) {
error = EOPNOTSUPP; error = EOPNOTSUPP;
goto leave; goto leave;
} }
@@ -766,7 +771,7 @@ static int localsock_connect(socket_t *socket, sockaddr_t *addr, uintmax_t flags
// and if the server has not closed the socket and is listening // and if the server has not closed the socket and is listening
MUTEX_ACQUIRE(&binding->mutex); MUTEX_ACQUIRE(&binding->mutex);
localsocket_t *server = binding->server; localsocket_t *server = binding->server;
if (server == NULL || server->backlog == NULL) { if (server == NULL || server->listening == false) {
error = ECONNREFUSED; error = ECONNREFUSED;
goto leave; goto leave;
} }
@@ -801,7 +806,8 @@ static int localsock_connect(socket_t *socket, sockaddr_t *addr, uintmax_t flags
} }
// to make sure the binding doesn't get destroyed once we stop locking it, reference the vnode // to make sure the binding doesn't get destroyed once we stop locking it, reference the vnode
VOP_HOLD(binding->vnode); vnode_t *vn = binding->vnode;
VOP_HOLD(vn);
MUTEX_RELEASE(&binding->mutex); MUTEX_RELEASE(&binding->mutex);
error = poll_dowait(&desc, 0); error = poll_dowait(&desc, 0);
@@ -809,19 +815,33 @@ static int localsock_connect(socket_t *socket, sockaddr_t *addr, uintmax_t flags
poll_leave(&desc); poll_leave(&desc);
poll_destroydesc(&desc); poll_destroydesc(&desc);
if (error) if (error) {
goto leave; VOP_RELEASE(vn);
binding = NULL;
goto leave_nobinding;
}
MUTEX_ACQUIRE(&binding->mutex); MUTEX_ACQUIRE(&binding->mutex);
if (binding->server == NULL) { server = binding->server;
if (server == NULL || server->listening == false) {
// server closed while we waited! // server closed while we waited!
// release the vnode so it can get deleted and set the binding to NULL as we can't be sure on its state anymore. // release the vnode so it can get deleted and set the binding to NULL as we can't be sure on its state anymore.
VOP_RELEASE(binding->vnode); MUTEX_RELEASE(&binding->mutex);
VOP_RELEASE(vn);
binding = NULL; binding = NULL;
error = ECONNREFUSED; error = ECONNREFUSED;
goto leave; goto leave;
} }
VOP_RELEASE(binding->vnode);
if (server->seqpacket != localsocket->seqpacket) {
MUTEX_RELEASE(&binding->mutex);
VOP_RELEASE(vn);
binding = NULL;
error = EPROTOTYPE;
goto leave;
}
VOP_RELEASE(vn);
} }
localpair_t *pair = alloc(sizeof(localpair_t)); localpair_t *pair = alloc(sizeof(localpair_t));
@@ -873,13 +893,11 @@ static int localsock_connect(socket_t *socket, sockaddr_t *addr, uintmax_t flags
poll_leave(&desc); poll_leave(&desc);
poll_destroydesc(&desc); poll_destroydesc(&desc);
if (error)
goto leave;
leave: leave:
if (binding) if (binding)
MUTEX_RELEASE(&binding->mutex); MUTEX_RELEASE(&binding->mutex);
leave_nobinding:
if (result) if (result)
VOP_RELEASE(result); VOP_RELEASE(result);
@@ -896,7 +914,7 @@ static int localsock_listen(socket_t *socket, int backlogsize) {
int error = 0; int error = 0;
// already listening // already listening
if (localsocket->backlog) if (localsocket->listening)
goto leave; goto leave;
// connected/unbound socket // connected/unbound socket
@@ -913,14 +931,13 @@ static int localsock_listen(socket_t *socket, int backlogsize) {
MUTEX_ACQUIRE(&localsocket->binding->mutex); MUTEX_ACQUIRE(&localsocket->binding->mutex);
localsocket->backlog = alloc(backlogsize * sizeof(localpair_t *)); error = ringbuffer_init(&localsocket->backlog, backlogsize * sizeof(localpair_t *));
if (localsocket->backlog == NULL) { if (error) {
MUTEX_RELEASE(&localsocket->binding->mutex); MUTEX_RELEASE(&localsocket->binding->mutex);
error = ENOMEM;
goto leave; goto leave;
} }
localsocket->backlogsize = backlogsize; localsocket->listening = true;
MUTEX_RELEASE(&localsocket->binding->mutex); MUTEX_RELEASE(&localsocket->binding->mutex);
@@ -969,12 +986,9 @@ static void localsock_destroy(socket_t *socket) {
} }
// destroy backlog (for listening sockets) // destroy backlog (for listening sockets)
if (localsocket->backlog) { if (localsocket->listening) {
for (int i = 0; i < localsocket->backlogsize; ++i) { localpair_t *pair = NULL;
localpair_t *pair = localsocket->backlog[i]; while (ringbuffer_read(&localsocket->backlog, &pair, sizeof(pair)) == sizeof(pair)) {
if (pair == NULL)
continue;
MUTEX_ACQUIRE(&pair->mutex); MUTEX_ACQUIRE(&pair->mutex);
if (pair->client == NULL) { if (pair->client == NULL) {
// client gave up // client gave up
@@ -986,8 +1000,8 @@ static void localsock_destroy(socket_t *socket) {
} }
} }
free(localsocket->backlog); ringbuffer_destroy(&localsocket->backlog);
localsocket->backlog = NULL; localsocket->listening = false;
} }
poll_event(&socket->pollheader, POLLHUP); poll_event(&socket->pollheader, POLLHUP);