Skip to content

Commit 67088ee

Browse files
committed
tcp reactor transition
1 parent 0deef90 commit 67088ee

6 files changed

Lines changed: 569 additions & 19 deletions

File tree

src/zingcore/2.5/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ OBJ := \
6969
$(BUILD)/zi_hopabi25.o \
7070
$(BUILD)/zi_telemetry.o
7171

72-
TESTS := test_caps test_async_registry test_zingcore25_api test_problem test_telemetry_jsonl test_sysabi25_min_core test_sysabi25_ctl_caps_list test_sysabi25_file_cap test_sysabi25_tcp_cap test_sysabi25_http_cap test_sysabi25_http_loop_cap test_sysabi25_argv_cap test_sysabi25_env_cap test_sysabi25_hopper_cap test_sysabi25_event_bus_cap test_sysabi25_sys_info_cap test_sysabi25_sys_loop_cap test_bus_rpc_v1 test_hopabi25_basic
72+
TESTS := test_caps test_async_registry test_zingcore25_api test_problem test_telemetry_jsonl test_sysabi25_min_core test_sysabi25_ctl_caps_list test_sysabi25_file_cap test_sysabi25_tcp_cap test_sysabi25_tcp_loop_connect_cap test_sysabi25_http_cap test_sysabi25_http_loop_cap test_sysabi25_argv_cap test_sysabi25_env_cap test_sysabi25_hopper_cap test_sysabi25_event_bus_cap test_sysabi25_sys_info_cap test_sysabi25_sys_loop_cap test_bus_rpc_v1 test_hopabi25_basic
7373

7474
EXAMPLES := stdio_caps_demo all_caps_demo hopabi_guest_demo
7575

src/zingcore/2.5/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,14 @@ Open semantics:
226226
- `u32 port` (1..65535)
227227
- `u32 flags` (reserved; must be 0)
228228

229+
I/O semantics:
230+
231+
- TCP handles use nonblocking sockets.
232+
- While the connection is still being established, `zi_read`/`zi_write` MAY return `ZI_E_AGAIN`.
233+
Guests SHOULD wait via `sys/loop` watching the TCP handle for `writable` readiness, then retry.
234+
- For data transfer, `zi_read`/`zi_write` return `ZI_E_AGAIN` on would-block; guests SHOULD wait via
235+
`sys/loop` watching `readable`/`writable` readiness, then retry.
236+
229237
Sandboxing via `ZI_NET_ALLOW`:
230238

231239
- If `ZI_NET_ALLOW` is unset/empty: only loopback hosts are allowed (`localhost`, `127.0.0.1`, `::1`).

src/zingcore/2.5/zingcore/include/zi_net_tcp25.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ extern "C" {
1717
// This cap is opened via zi_cap_open() and yields a stream handle usable with:
1818
// zi_read / zi_write / zi_end
1919
//
20+
// Sockets are nonblocking:
21+
// - zi_read/zi_write return ZI_E_AGAIN on would-block.
22+
// - While connect is still in progress, zi_read/zi_write MAY return ZI_E_AGAIN;
23+
// guests should wait for writability via sys/loop and retry.
24+
//
2025
// Open params are a packed little-endian struct (20 bytes):
2126
// u64 host_ptr (UTF-8 host bytes, not NUL-terminated)
2227
// u32 host_len

src/zingcore/2.5/zingcore/src/zi_net_tcp25.c

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
typedef struct {
2121
int fd;
22+
int connecting;
2223
} zi_tcp_stream;
2324

2425
static void set_nonblocking_best_effort(int fd) {
@@ -36,6 +37,43 @@ static int tcp_get_fd(void *ctx, int *out_fd) {
3637
return 1;
3738
}
3839

40+
static int32_t map_errno_to_zi(int e);
41+
42+
static int32_t tcp_ensure_connected(zi_tcp_stream *s) {
43+
if (!s) return ZI_E_INTERNAL;
44+
if (!s->connecting) return 0;
45+
if (s->fd < 0) return ZI_E_CLOSED;
46+
47+
int so_err = 0;
48+
socklen_t len = (socklen_t)sizeof(so_err);
49+
if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &so_err, &len) != 0) {
50+
return map_errno_to_zi(errno);
51+
}
52+
if (so_err == 0) {
53+
// Some platforms may report SO_ERROR=0 before the connection is fully established.
54+
// Confirm connectivity via getpeername.
55+
struct sockaddr_storage ss;
56+
socklen_t slen = (socklen_t)sizeof(ss);
57+
if (getpeername(s->fd, (struct sockaddr *)&ss, &slen) == 0) {
58+
s->connecting = 0;
59+
return 0;
60+
}
61+
if (errno == ENOTCONN) return ZI_E_AGAIN;
62+
return map_errno_to_zi(errno);
63+
}
64+
65+
// Still in progress.
66+
if (so_err == EINPROGRESS
67+
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EINPROGRESS)
68+
|| so_err == EWOULDBLOCK
69+
#endif
70+
) {
71+
return ZI_E_AGAIN;
72+
}
73+
74+
return map_errno_to_zi(so_err);
75+
}
76+
3977
static int32_t map_errno_to_zi(int e) {
4078
switch (e) {
4179
case EAGAIN:
@@ -64,6 +102,9 @@ static int32_t tcp_read(void *ctx, zi_ptr_t dst_ptr, zi_size32_t cap) {
64102
if (!s) return ZI_E_INTERNAL;
65103
if (cap == 0) return 0;
66104

105+
int32_t cr = tcp_ensure_connected(s);
106+
if (cr != 0) return cr;
107+
67108
const zi_mem_v1 *mem = zi_runtime25_mem();
68109
if (!mem || !mem->map_rw) return ZI_E_NOSYS;
69110
if (dst_ptr == 0) return ZI_E_BOUNDS;
@@ -81,6 +122,9 @@ static int32_t tcp_write(void *ctx, zi_ptr_t src_ptr, zi_size32_t len) {
81122
if (!s) return ZI_E_INTERNAL;
82123
if (len == 0) return 0;
83124

125+
int32_t cr = tcp_ensure_connected(s);
126+
if (cr != 0) return cr;
127+
84128
const zi_mem_v1 *mem = zi_runtime25_mem();
85129
if (!mem || !mem->map_ro) return ZI_E_NOSYS;
86130
if (src_ptr == 0) return ZI_E_BOUNDS;
@@ -331,38 +375,52 @@ zi_handle_t zi_net_tcp25_open_from_params(zi_ptr_t params_ptr, zi_size32_t param
331375
continue;
332376
}
333377

378+
// Make connect + subsequent I/O nonblocking so callers can rely on ZI_E_AGAIN.
379+
set_nonblocking_best_effort(fd);
380+
334381
#if defined(__APPLE__) && defined(SO_NOSIGPIPE)
335382
{
336383
int one = 1;
337384
(void)setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, (socklen_t)sizeof(one));
338385
}
339386
#endif
340387

341-
if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) {
342-
last_zi = 0;
388+
for (;;) {
389+
if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) {
390+
last_zi = 0;
391+
break;
392+
}
393+
394+
if (errno == EINTR) continue;
395+
if (errno == EINPROGRESS
396+
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EINPROGRESS)
397+
|| errno == EWOULDBLOCK
398+
#endif
399+
) {
400+
last_zi = ZI_E_AGAIN;
401+
break;
402+
}
403+
404+
last_zi = map_errno_to_zi(errno);
405+
(void)close(fd);
406+
fd = -1;
343407
break;
344408
}
345409

346-
last_zi = map_errno_to_zi(errno);
347-
(void)close(fd);
348-
fd = -1;
410+
if (fd >= 0) break;
349411
}
350412

351413
freeaddrinfo(ai);
352414

353-
if (fd < 0) {
354-
return (zi_handle_t)last_zi;
355-
}
356-
357-
// Make subsequent I/O nonblocking so callers can rely on ZI_E_AGAIN.
358-
set_nonblocking_best_effort(fd);
415+
if (fd < 0) return (zi_handle_t)last_zi;
359416

360417
zi_tcp_stream *s = (zi_tcp_stream *)calloc(1, sizeof(*s));
361418
if (!s) {
362419
(void)close(fd);
363420
return (zi_handle_t)ZI_E_OOM;
364421
}
365422
s->fd = fd;
423+
s->connecting = (last_zi == ZI_E_AGAIN) ? 1 : 0;
366424

367425
zi_handle_t h = zi_handle25_alloc_with_poll(&tcp_ops, &tcp_poll_ops, s, ZI_H_READABLE | ZI_H_WRITABLE | ZI_H_ENDABLE);
368426
if (h == 0) {

0 commit comments

Comments
 (0)