feat(raft-drive-async): bound peer connect (O_NONBLOCK+poll), close tasks 1.3/1.4/1.5
socket_connect sets O_NONBLOCK before connect and bounds the attempt with poll(timeout); new connect_timeout_ms param (0=DEFAULT_CONNECT_TIMEOUT_MS); all 5 callers updated (ha.c x3, client.c, xcectx.c). Revalidation Trigger: shared netio helper signature changed. Broad build links all components; xcelib_test 278/0, controller_unit_test 194/0. 1.4/1.5 unit tests satisfied by the TDD output of 1.2/1.3 (test_slowlane.c, test_netio_bounded_connect.c).
This commit is contained in:
parent
5650acd375
commit
0781c6c027
9 changed files with 289 additions and 18 deletions
102
src/lib/netio.c
102
src/lib/netio.c
|
|
@ -4,6 +4,7 @@
|
|||
#include <netdb.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -13,6 +14,7 @@
|
|||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "dataq.h"
|
||||
#include "netio.h"
|
||||
#include "request.h"
|
||||
|
|
@ -279,34 +281,115 @@ static int socket_create(int domain, int type, int port, int backlog)
|
|||
return sock;
|
||||
}
|
||||
|
||||
static int socket_connect(int sock, char* host, int port)
|
||||
/*
|
||||
* socket_connect — bounded, non-blocking TCP connect (raft-drive-async R2.3).
|
||||
*
|
||||
* Sets O_NONBLOCK *before* connect() and, on EINPROGRESS, bounds the wait with
|
||||
* poll() up to connect_timeout_ms (0 => DEFAULT_CONNECT_TIMEOUT_MS). This stops
|
||||
* an unreachable peer from pinning a caller (notably the delaymgr heartbeat
|
||||
* thread) for the multi-second OS connect timeout. The fd is LEFT non-blocking
|
||||
* on success — endpoint_connect relies on that for the SSL handshake.
|
||||
*
|
||||
* Mirrors the xce_probe_config_tag bounded-connect pattern (config_repl.c).
|
||||
*
|
||||
* Returns:
|
||||
* 0 — connected (fd left non-blocking)
|
||||
* -ETIMEDOUT — poll bound expired before the connect completed
|
||||
* -<errno> — name resolution / socket / connect error (e.g. -ECONNREFUSED)
|
||||
*/
|
||||
int socket_connect(int sock, char* host, int port, int connect_timeout_ms)
|
||||
{
|
||||
struct sockaddr_in server;
|
||||
struct hostent* hp;
|
||||
int rc;
|
||||
int flags;
|
||||
int timeout_ms;
|
||||
|
||||
server.sin_family = AF_INET;
|
||||
hp = gethostbyname(host);
|
||||
if (hp == NULL) {
|
||||
SET_ERROR(errno);
|
||||
|
||||
rc = -errno;
|
||||
|
||||
return rc;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memcpy((char*)&server.sin_addr, (char*)hp->h_addr, (int)hp->h_length);
|
||||
|
||||
server.sin_port = htons(port);
|
||||
|
||||
rc = connect(sock, (struct sockaddr*)&server, sizeof(server));
|
||||
if (rc < 0) {
|
||||
timeout_ms = (connect_timeout_ms > 0) ? connect_timeout_ms
|
||||
: DEFAULT_CONNECT_TIMEOUT_MS;
|
||||
|
||||
/* Set non-blocking BEFORE connect so an unreachable peer returns
|
||||
* EINPROGRESS immediately instead of blocking. */
|
||||
flags = fcntl(sock, F_GETFL, 0);
|
||||
if (flags < 0) {
|
||||
SET_ERROR(errno);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
SET_ERROR(errno);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return rc;
|
||||
rc = connect(sock, (struct sockaddr*)&server, sizeof(server));
|
||||
if (rc == 0) {
|
||||
/* Immediate completion (e.g. loopback). Leave fd non-blocking. */
|
||||
return 0;
|
||||
}
|
||||
if (errno != EINPROGRESS) {
|
||||
SET_ERROR(errno);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Connect in progress — bound the wait with poll(). */
|
||||
{
|
||||
struct pollfd pfd;
|
||||
int pr;
|
||||
|
||||
pfd.fd = sock;
|
||||
pfd.events = POLLOUT | POLLERR | POLLHUP;
|
||||
pfd.revents = 0;
|
||||
|
||||
do {
|
||||
pr = poll(&pfd, 1, timeout_ms);
|
||||
} while (pr < 0 && errno == EINTR);
|
||||
|
||||
if (pr == 0) {
|
||||
/* Bound expired: report timeout instead of waiting for the OS. */
|
||||
SET_ERROR(ETIMEDOUT);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (pr < 0) {
|
||||
SET_ERROR(errno);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* poll returned ready — confirm the connect actually succeeded. */
|
||||
{
|
||||
int so_err = 0;
|
||||
socklen_t elen = sizeof(so_err);
|
||||
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_err, &elen) < 0) {
|
||||
SET_ERROR(errno);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
if (so_err != 0) {
|
||||
SET_ERROR(so_err);
|
||||
|
||||
return -so_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Connected; fd left non-blocking for the caller's SSL handshake. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socket_accept(int sock)
|
||||
|
|
@ -694,12 +777,13 @@ int endpoint_init(endpoint_t* ep, int domain, int type, int port, int backlog)
|
|||
return fd;
|
||||
}
|
||||
|
||||
int endpoint_connect(endpoint_t* ep, char* host, int port)
|
||||
int endpoint_connect(endpoint_t* ep, char* host, int port,
|
||||
int connect_timeout_ms)
|
||||
{
|
||||
int rc = -1;
|
||||
iomgr_t* iomgr = (iomgr_t*)ep->iomgr;
|
||||
|
||||
rc = socket_connect(ep->fd, host, port);
|
||||
rc = socket_connect(ep->fd, host, port, connect_timeout_ms);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue