Skip to content

Commit 553ce4d

Browse files
committed
emulate blocking i/o on fd 0-2
see the comment in nbio.h.
1 parent ea2a601 commit 553ce4d

File tree

6 files changed

+232
-88
lines changed

6 files changed

+232
-88
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ set(lib_sources
187187
"lib/instance.c"
188188
"lib/leb128.c"
189189
"lib/module.c"
190+
"lib/nbio.c"
190191
"lib/report.c"
191192
"lib/timeutil.c"
192193
"lib/type.c"

cli/repl.c

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "load_context.h"
2525
#include "module.h"
2626
#include "module_writer.h"
27+
#include "nbio.h"
2728
#include "repl.h"
2829
#include "report.h"
2930
#include "type.h"
@@ -345,7 +346,7 @@ print_trap(const struct exec_context *ctx, const struct trap_info *trap)
345346
if (trapmsg == NULL) {
346347
trapmsg = "no message";
347348
}
348-
printf("Error: [trap] %s (%u): %s\n", msg, id, trapmsg);
349+
nbio_printf("Error: [trap] %s (%u): %s\n", msg, id, trapmsg);
349350
}
350351

351352
static int
@@ -383,9 +384,9 @@ repl_load_from_buf(struct repl_state *state, const char *modname,
383384
&ctx);
384385
if (ctx.report.msg != NULL) {
385386
xlog_error("load/validation error: %s", ctx.report.msg);
386-
printf("load/validation error: %s\n", ctx.report.msg);
387+
nbio_printf("load/validation error: %s\n", ctx.report.msg);
387388
} else if (ret != 0) {
388-
printf("load/validation error: no message\n");
389+
nbio_printf("load/validation error: no message\n");
389390
}
390391
load_context_clear(&ctx);
391392
if (ret != 0) {
@@ -412,9 +413,9 @@ repl_load_from_buf(struct repl_state *state, const char *modname,
412413
&report);
413414
if (report.msg != NULL) {
414415
xlog_error("instance_create: %s", report.msg);
415-
printf("instantiation error: %s\n", report.msg);
416+
nbio_printf("instantiation error: %s\n", report.msg);
416417
} else if (ret != 0) {
417-
printf("instantiation error: no message\n");
418+
nbio_printf("instantiation error: no message\n");
418419
}
419420
report_clear(&report);
420421
if (ret != 0) {
@@ -622,42 +623,42 @@ repl_print_result(const struct resulttype *rt, const struct val *vals)
622623
uint32_t i;
623624
int ret = 0;
624625
if (rt->ntypes == 0) {
625-
printf("Result: <Empty Stack>\n");
626+
nbio_printf("Result: <Empty Stack>\n");
626627
return 0;
627628
}
628-
printf("Result: ");
629+
nbio_printf("Result: ");
629630
for (i = 0; i < rt->ntypes; i++) {
630631
enum valtype type = rt->types[i];
631632
const struct val *val = &vals[i];
632633
switch (type) {
633634
case TYPE_i32:
634-
printf("%s%" PRIu32 ":i32", sep, val->u.i32);
635+
nbio_printf("%s%" PRIu32 ":i32", sep, val->u.i32);
635636
break;
636637
case TYPE_f32:
637-
printf("%s%" PRIu32 ":f32", sep, val->u.i32);
638+
nbio_printf("%s%" PRIu32 ":f32", sep, val->u.i32);
638639
break;
639640
case TYPE_i64:
640-
printf("%s%" PRIu64 ":i64", sep, val->u.i64);
641+
nbio_printf("%s%" PRIu64 ":i64", sep, val->u.i64);
641642
break;
642643
case TYPE_f64:
643-
printf("%s%" PRIu64 ":f64", sep, val->u.i64);
644+
nbio_printf("%s%" PRIu64 ":f64", sep, val->u.i64);
644645
break;
645646
case TYPE_FUNCREF:
646647
if (val->u.funcref.func == NULL) {
647-
printf("%snull:funcref", sep);
648+
nbio_printf("%snull:funcref", sep);
648649
} else {
649-
printf("%s%" PRIuPTR ":funcref", sep,
650-
(uintptr_t)val->u.funcref.func);
650+
nbio_printf("%s%" PRIuPTR ":funcref", sep,
651+
(uintptr_t)val->u.funcref.func);
651652
}
652653
break;
653654
case TYPE_EXTERNREF:
654655
if ((uintptr_t)val->u.externref == EXTERNREF_0) {
655-
printf("%s0:externref", sep);
656+
nbio_printf("%s0:externref", sep);
656657
} else if (val->u.externref == NULL) {
657-
printf("%snull:externref", sep);
658+
nbio_printf("%snull:externref", sep);
658659
} else {
659-
printf("%s%" PRIuPTR ":externref", sep,
660-
(uintptr_t)val->u.externref);
660+
nbio_printf("%s%" PRIuPTR ":externref", sep,
661+
(uintptr_t)val->u.externref);
661662
}
662663
break;
663664
default:
@@ -668,7 +669,7 @@ repl_print_result(const struct resulttype *rt, const struct val *vals)
668669
}
669670
sep = ", ";
670671
}
671-
printf("\n");
672+
nbio_printf("\n");
672673
return ret;
673674
}
674675

@@ -921,70 +922,70 @@ repl_global_get(struct repl_state *state, const char *modname,
921922
void
922923
toywasm_repl_print_version(void)
923924
{
924-
printf("toywasm wasm interpreter\n");
925+
nbio_printf("toywasm wasm interpreter\n");
925926
#if defined(__clang_version__)
926-
printf("__clang_version__ = %s\n", __clang_version__);
927+
nbio_printf("__clang_version__ = %s\n", __clang_version__);
927928
#endif
928929
#if !defined(__clang__)
929930
#if defined(__GNUC__)
930-
printf("__GNUC__ = %u\n", __GNUC__);
931+
nbio_printf("__GNUC__ = %u\n", __GNUC__);
931932
#endif
932933
#if defined(__GNUC_MINOR__)
933-
printf("__GNUC_MINOR__ = %u\n", __GNUC_MINOR__);
934+
nbio_printf("__GNUC_MINOR__ = %u\n", __GNUC_MINOR__);
934935
#endif
935936
#if defined(__GNUC_PATCHLEVEL__)
936-
printf("__GNUC_PATCHLEVEL__ = %u\n", __GNUC_PATCHLEVEL__);
937+
nbio_printf("__GNUC_PATCHLEVEL__ = %u\n", __GNUC_PATCHLEVEL__);
937938
#endif
938939
#endif /* !defined(__clang__) */
939940
#if defined(__BYTE_ORDER__)
940-
printf("__BYTE_ORDER__ is %u (__ORDER_LITTLE_ENDIAN__ is %u)\n",
941-
__BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__);
941+
nbio_printf("__BYTE_ORDER__ is %u (__ORDER_LITTLE_ENDIAN__ is %u)\n",
942+
__BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__);
942943
#endif
943-
printf("sizeof(void *) = %zu\n", sizeof(void *));
944+
nbio_printf("sizeof(void *) = %zu\n", sizeof(void *));
944945
#if defined(__wasi__)
945-
printf("__wasi__ defined\n");
946+
nbio_printf("__wasi__ defined\n");
946947
#endif
947948
#if defined(__x86_64__)
948-
printf("__x86_64__ defined\n");
949+
nbio_printf("__x86_64__ defined\n");
949950
#endif
950951
#if defined(__aarch64__)
951-
printf("__aarch64__ defined\n");
952+
nbio_printf("__aarch64__ defined\n");
952953
#endif
953954
#if defined(__arm__)
954-
printf("__arm__ defined\n");
955+
nbio_printf("__arm__ defined\n");
955956
#endif
956957
#if defined(__ppc__)
957-
printf("__ppc__ defined\n");
958+
nbio_printf("__ppc__ defined\n");
958959
#endif
959960
#if defined(__riscv)
960-
printf("__riscv defined\n");
961+
nbio_printf("__riscv defined\n");
961962
#endif
962963
#if defined(__s390x__)
963-
printf("__s390x__ defined\n");
964+
nbio_printf("__s390x__ defined\n");
964965
#endif
965966
#if defined(__s390__)
966-
printf("__s390__ defined\n");
967+
nbio_printf("__s390__ defined\n");
967968
#endif
968969
#if defined(__wasm__)
969-
printf("__wasm__ defined\n");
970+
nbio_printf("__wasm__ defined\n");
970971
#endif
971972
#if defined(__wasm32__)
972-
printf("__wasm32__ defined\n");
973+
nbio_printf("__wasm32__ defined\n");
973974
#endif
974975
#if defined(__wasm64__)
975-
printf("__wasm64__ defined\n");
976+
nbio_printf("__wasm64__ defined\n");
976977
#endif
977978
#if defined(__APPLE__)
978-
printf("__APPLE__ defined\n");
979+
nbio_printf("__APPLE__ defined\n");
979980
#endif
980981
#if defined(__NuttX__)
981-
printf("__NuttX__ defined\n");
982+
nbio_printf("__NuttX__ defined\n");
982983
#endif
983984
#if defined(__linux__)
984-
printf("__linux__ defined\n");
985+
nbio_printf("__linux__ defined\n");
985986
#endif
986987
extern const char *toywasm_config_string;
987-
printf("Build-time options:\n%s", toywasm_config_string);
988+
nbio_printf("Build-time options:\n%s", toywasm_config_string);
988989
}
989990

990991
static int
@@ -1055,9 +1056,9 @@ toywasm_repl(struct repl_state *state)
10551056
size_t linecap = 0;
10561057
int ret;
10571058
while (true) {
1058-
printf("%s> ", state->opts.prompt);
1059+
nbio_printf("%s> ", state->opts.prompt);
10591060
fflush(stdout);
1060-
ret = getline(&line, &linecap, stdin);
1061+
ret = nbio_getline(&line, &linecap, stdin);
10611062
if (ret == -1) {
10621063
break;
10631064
}
@@ -1096,7 +1097,7 @@ toywasm_repl(struct repl_state *state)
10961097
continue;
10971098
fail:
10981099
xlog_printf("repl fail with %d\n", ret);
1099-
printf("Error: command '%s' failed with %d\n", cmd, ret);
1100+
nbio_printf("Error: command '%s' failed with %d\n", cmd, ret);
11001101
}
11011102
free(line);
11021103
toywasm_repl_reset(state);

lib/nbio.c

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#define _GNU_SOURCE /* vasprintf, getline */
2+
3+
#include <assert.h>
4+
#include <errno.h>
5+
#include <fcntl.h>
6+
#include <limits.h>
7+
#include <poll.h>
8+
#include <stdbool.h>
9+
#include <stddef.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <unistd.h>
13+
14+
#include "nbio.h"
15+
16+
/* NuttX: https://github.com/apache/incubator-nuttx/pull/6152 */
17+
#if defined(__wasi__) || defined(__NuttX__)
18+
#define flockfile(f)
19+
#define funlockfile(f)
20+
#endif
21+
22+
int
23+
set_nonblocking(int fd, bool nonblocking, bool *orig)
24+
{
25+
int flags = fcntl(fd, F_GETFL, 0);
26+
int newflags = flags & ~O_NONBLOCK;
27+
if (nonblocking) {
28+
newflags |= O_NONBLOCK;
29+
}
30+
int ret = 0;
31+
if (flags != newflags) {
32+
ret = fcntl(fd, F_SETFL, newflags);
33+
if (ret == -1) {
34+
ret = errno;
35+
assert(ret > 0);
36+
}
37+
}
38+
if (orig != NULL) {
39+
*orig = (flags & O_NONBLOCK) != 0;
40+
}
41+
return ret;
42+
}
43+
44+
bool
45+
is_again(int error)
46+
{
47+
/* handle a BSD vs SYSV historical mess */
48+
return (error == EWOULDBLOCK || error == EAGAIN);
49+
}
50+
51+
int
52+
nbio_vfprintf(FILE *fp, const char *fmt, va_list ap)
53+
{
54+
/*
55+
* XXX this implementation is effectively unbuffered.
56+
*/
57+
58+
char *buf;
59+
int ret;
60+
61+
ret = vasprintf(&buf, fmt, ap);
62+
if (ret < 0) {
63+
return ret;
64+
}
65+
size_t len = ret;
66+
int fd = fileno(fp);
67+
68+
flockfile(fp);
69+
ssize_t written = 0;
70+
while (written < len) {
71+
ssize_t ssz = write(fd, buf + written, len - written);
72+
assert(ssz != 0);
73+
if (ssz == -1) {
74+
if (is_again(errno)) {
75+
/*
76+
* probably the fd is non-blocking.
77+
* block with poll.
78+
*/
79+
struct pollfd pfd;
80+
memset(&pfd, 0, sizeof(pfd));
81+
pfd.fd = fd;
82+
pfd.events = POLLOUT;
83+
ret = poll(&pfd, 1, -1);
84+
assert(ret != 0);
85+
if (ret == 1) {
86+
continue;
87+
}
88+
/* poll error, fall through */
89+
}
90+
if (written == 0) {
91+
/* errno is already set by write or poll */
92+
written = -1;
93+
}
94+
break;
95+
}
96+
written += ssz;
97+
}
98+
int saved_errno = errno; /* just in case */
99+
funlockfile(fp);
100+
free(buf);
101+
assert(written <= INT_MAX);
102+
assert(written >= INT_MIN);
103+
errno = saved_errno;
104+
return (int)written;
105+
}
106+
107+
int
108+
nbio_fprintf(FILE *fp, const char *fmt, ...)
109+
{
110+
va_list ap;
111+
int ret;
112+
va_start(ap, fmt);
113+
ret = nbio_vfprintf(fp, fmt, ap);
114+
va_end(ap);
115+
return ret;
116+
}
117+
118+
int
119+
nbio_printf(const char *fmt, ...)
120+
{
121+
va_list ap;
122+
int ret;
123+
va_start(ap, fmt);
124+
ret = nbio_vfprintf(stdout, fmt, ap);
125+
va_end(ap);
126+
return ret;
127+
}
128+
129+
ssize_t
130+
nbio_getline(char **linep, size_t *linecapp, FILE *fp)
131+
{
132+
ssize_t ssz;
133+
int ret;
134+
int fd;
135+
bool orig;
136+
137+
/*
138+
* Note: this relaxed implementation is ok, given our usage in repl.
139+
*/
140+
fd = fileno(fp);
141+
ret = set_nonblocking(fd, false, &orig);
142+
if (ret != 0) {
143+
errno = ret;
144+
return -1;
145+
}
146+
ssz = getline(linep, linecapp, fp);
147+
ret = set_nonblocking(fd, orig, NULL);
148+
assert(ret == 0); /* no good way to recover */
149+
return ssz;
150+
}

0 commit comments

Comments
 (0)