Skip to content

Commit c087718

Browse files
committed
Add poll for async call
1 parent 000182a commit c087718

File tree

3 files changed

+136
-36
lines changed

3 files changed

+136
-36
lines changed

examples/http_client.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function must(result) {
1111
/** @param {os.FileDescriptor} fd @param {string[]} lines */
1212
function sendLines(fd, lines) {
1313
const buf = Uint8Array.from(lines.join('\r\n'), c => c.charCodeAt(0));
14-
os.write(fd, buf.buffer, 0, buf.byteLength);
14+
os.send(fd, buf.buffer);
1515
}
1616
const [host = "example.com", port = "80"] = scriptArgs.slice(1);
1717
const ai = os.getaddrinfo(host, port).filter(ai => ai.family == os.AF_INET && ai.port); // TODO too much/invalid result
@@ -21,6 +21,6 @@ must(os.connect(sockfd, ai[0]))
2121
sendLines(sockfd, ["GET / HTTP/1.0", `Host: ${host}`, "Connection: close", "", ""]);
2222

2323
const chunk = new Uint8Array(4096);
24-
while (os.read(sockfd, chunk.buffer, 0, chunk.byteLength) > 0) {
24+
while (os.recv(sockfd, chunk.buffer) > 0) {
2525
console.log(String.fromCharCode(...chunk));
2626
}

examples/http_server.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function must(result) {
2424
function* recvLines(fd) {
2525
const chunk = new Uint8Array(1);
2626
let line = '';
27-
while (os.read(fd, chunk.buffer, 0, chunk.byteLength) > 0) {
27+
while (os.recv(fd, chunk.buffer) > 0) {
2828
const char = String.fromCharCode(...chunk);
2929
if (char == '\n') {
3030
yield line;
@@ -36,7 +36,7 @@ function* recvLines(fd) {
3636
/** @param {os.FileDescriptor} fd @param {string[]} lines */
3737
function sendLines(fd, lines) {
3838
const buf = Uint8Array.from(lines.join('\r\n'), c => c.charCodeAt(0));
39-
os.write(fd, buf.buffer, 0, buf.byteLength);
39+
os.send(fd, buf.buffer);
4040
}
4141
//USAGE: qjs http_server.js [PORT=8080 [HOST=localhost]]
4242
const [port = "8080", host = "localhost"] = scriptArgs.slice(1);
@@ -47,9 +47,10 @@ const sock_srv = must(os.socket(os.AF_INET, os.SOCK_STREAM));
4747
must(os.setsockopt(sock_srv, os.SO_REUSEADDR, new Uint32Array([1]).buffer));
4848
must(os.bind(sock_srv, ai[0]));
4949
must(os.listen(sock_srv));
50-
51-
console.log(`Listening on http://${ai[0].addr}:${ai[0].port} ...`);
52-
50+
//os.signal(os.SIGINT, ()=>os.close(sock_srv)); // don't work
51+
console.log(`Listening on http://${host}:${port} (${ai[0].addr}:${ai[0].port}) ...`);
52+
const openCmd = { linux: "xdg-open", darwin: "open", win32: "start" }[os.platform];
53+
if (openCmd) os.exec([openCmd, `http://${host}:${port}`]);
5354
while (true) { // TODO: break on SIG*
5455
const [sock_cli] = os.accept(sock_srv);
5556

@@ -86,11 +87,9 @@ while (true) { // TODO: break on SIG*
8687
const fd = must(os.open(safe_path));
8788
const fbuf = new Uint8Array(4096);
8889
for (let got = 0; (got = os.read(fd, fbuf.buffer, 0, fbuf.byteLength)) > 0;) {
89-
os.write(sock_cli, fbuf.buffer, 0, got);
90+
os.send(sock_cli, fbuf.buffer, got);
9091
}
9192
}
9293

9394
os.close(sock_cli);
9495
}
95-
96-
os.close(sock_srv);

quickjs-libc.c

Lines changed: 127 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include <netinet/in.h>
5353
#include <netinet/ip.h>
5454
#include <arpa/inet.h>
55+
#include <poll.h>
5556
#define __USE_XOPEN2K // for addrinfo
5657
#include <netdb.h>
5758

@@ -1618,6 +1619,9 @@ static const JSCFunctionListEntry js_std_error_props[] = {
16181619
DEF(EPERM),
16191620
DEF(EPIPE),
16201621
DEF(EBADF),
1622+
DEF(EAGAIN),
1623+
DEF(EINPROGRESS),
1624+
DEF(EWOULDBLOCK),
16211625
#undef DEF
16221626
};
16231627

@@ -2401,7 +2405,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
24012405

24022406
#if defined(_WIN32)
24032407

2404-
static int js_os_poll(JSContext *ctx)
2408+
static int js_os_event_poll(JSContext *ctx)
24052409
{
24062410
JSRuntime *rt = JS_GetRuntime(ctx);
24072411
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
@@ -2496,7 +2500,7 @@ static int js_os_poll(JSContext *ctx)
24962500

24972501
#else
24982502

2499-
static int js_os_poll(JSContext *ctx)
2503+
static int js_os_event_poll(JSContext *ctx)
25002504
{
25012505
JSRuntime *rt = JS_GetRuntime(ctx);
25022506
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
@@ -3410,7 +3414,7 @@ static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val,
34103414

34113415
#endif /* !_WIN32 */
34123416

3413-
static int JS_toSockaddrStruct(JSContext *ctx, JSValue addr,
3417+
static int JS_ToSockaddrStruct(JSContext *ctx, JSValue addr,
34143418
struct sockaddr_in *sockaddr)
34153419
{
34163420
JSValue val;
@@ -3438,13 +3442,13 @@ static int JS_toSockaddrStruct(JSContext *ctx, JSValue addr,
34383442
val = JS_GetPropertyStr(ctx, addr, "port");
34393443
ret = JS_ToUint32(ctx, &port, val);
34403444
JS_FreeValue(ctx, val);
3441-
if(ret)
3445+
if (ret)
34423446
return -1;
34433447
sockaddr->sin_port = htons(port);
34443448
return 0;
34453449
}
34463450

3447-
static JSValue js_toSockaddrObj(JSContext *ctx, struct sockaddr_in *sockaddr)
3451+
static JSValue JS_ToSockaddrObj(JSContext *ctx, struct sockaddr_in *sockaddr)
34483452
{
34493453
JSValue obj, prop;
34503454
char ip_str[INET_ADDRSTRLEN];
@@ -3481,7 +3485,10 @@ static JSValue js_os_socket(JSContext *ctx, JSValueConst this_val,
34813485
return JS_EXCEPTION;
34823486
if (argc >= 3 && JS_ToInt32(ctx, &protocol, argv[2]))
34833487
return JS_EXCEPTION;
3484-
ret = js_get_errno(socket(domain, type, protocol));
3488+
ret = js_get_errno(socket(domain, type & ~SOCK_NONBLOCK, protocol));
3489+
if (ret >=0 && (type & SOCK_NONBLOCK)) {
3490+
fcntl(ret, F_SETFL, fcntl(ret, F_GETFL, 0) | O_NONBLOCK);
3491+
}
34853492
return JS_NewInt32(ctx, ret);
34863493
}
34873494

@@ -3526,13 +3533,13 @@ static JSValue js_os_getaddrinfo(JSContext *ctx, JSValueConst this_val,
35263533
goto fail;
35273534

35283535
ret = js_get_errno(getaddrinfo(node, service, NULL, &ai));
3529-
if(ret)
3536+
if (ret)
35303537
goto fail;
35313538

35323539
obj = JS_NewArray(ctx);
35333540
for (objLen = 0, it = ai; it; it = it->ai_next) {
35343541
for (len = 0; len < it->ai_addrlen; len++) {
3535-
addrObj = js_toSockaddrObj(ctx,(struct sockaddr_in *)&it->ai_addr[len]);
3542+
addrObj = JS_ToSockaddrObj(ctx,(struct sockaddr_in *)&it->ai_addr[len]);
35363543
JS_SetPropertyUint32(ctx,obj,objLen++,addrObj);
35373544
}
35383545
}
@@ -3554,7 +3561,7 @@ static JSValue js_os_bind(JSContext *ctx, JSValueConst this_val,
35543561

35553562
if (JS_ToInt32(ctx, &sockfd, argv[0]))
35563563
return JS_EXCEPTION;
3557-
if (JS_toSockaddrStruct(ctx, argv[1], &saddr))
3564+
if (JS_ToSockaddrStruct(ctx, argv[1], &saddr))
35583565
return JS_EXCEPTION;
35593566
ret = js_get_errno(bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)));
35603567
return JS_NewInt32(ctx, ret);
@@ -3586,7 +3593,7 @@ static JSValue js_os_accept(JSContext *ctx, JSValueConst this_val,
35863593
return JS_EXCEPTION;
35873594
ret = js_get_errno(accept(sockfd, (struct sockaddr *)&client_addr, &addr_len));
35883595

3589-
sockaddr_obj = js_toSockaddrObj(ctx, &client_addr);
3596+
sockaddr_obj = JS_ToSockaddrObj(ctx, &client_addr);
35903597
if (JS_IsException(sockaddr_obj))
35913598
return sockaddr_obj; // shall we return ?
35923599

@@ -3598,6 +3605,7 @@ static JSValue js_os_accept(JSContext *ctx, JSValueConst this_val,
35983605
JS_DefinePropertyValueUint32(ctx, arr, 1, sockaddr_obj, JS_PROP_C_W_E);
35993606
return arr;
36003607
}
3608+
36013609
static JSValue js_os_connect(JSContext *ctx, JSValueConst this_val,
36023610
int argc, JSValueConst *argv)
36033611
{
@@ -3608,14 +3616,80 @@ static JSValue js_os_connect(JSContext *ctx, JSValueConst this_val,
36083616
if (JS_ToInt32(ctx, &sockfd, argv[0]))
36093617
return JS_EXCEPTION;
36103618

3611-
if (JS_toSockaddrStruct(ctx, argv[1], &client_addr) < 0)
3619+
if (JS_ToSockaddrStruct(ctx, argv[1], &client_addr) < 0)
36123620
return JS_EXCEPTION;
36133621

36143622
ret = js_get_errno(connect(sockfd, (struct sockaddr *)&client_addr, client_len));
36153623

36163624
return JS_NewInt32(ctx, ret);
36173625
}
36183626

3627+
3628+
static JSValue js_os_poll(JSContext *ctx, JSValueConst this_val,
3629+
int argc, JSValueConst *argv)
3630+
{
3631+
JSValue val, prop;
3632+
struct pollfd pfds[32] = {}; // XXX: use js_mallocz() ?
3633+
uint32_t i, nbfd, events;
3634+
int ret, timeoutms = -1; // infinite timeout by default
3635+
3636+
val = JS_GetPropertyStr(ctx, argv[0], "length");
3637+
if (JS_IsException(val))
3638+
return JS_EXCEPTION;
3639+
ret = JS_ToUint32(ctx, &nbfd, val);
3640+
JS_FreeValue(ctx, val);
3641+
if (ret)
3642+
return JS_EXCEPTION;
3643+
if (nbfd > sizeof(pfds)/sizeof(*pfds))
3644+
return JS_EXCEPTION;
3645+
3646+
for (i = 0; i < nbfd; i++) {
3647+
val = JS_GetPropertyUint32(ctx, argv[0], i);
3648+
if (JS_IsException(val)) {
3649+
return JS_EXCEPTION;
3650+
}
3651+
prop = JS_GetPropertyStr(ctx, val, "fd");
3652+
if (JS_IsException(prop)) {
3653+
JS_FreeValue(ctx, val);
3654+
return JS_EXCEPTION;
3655+
}
3656+
ret = JS_ToInt32(ctx, &pfds[i].fd, prop);
3657+
JS_FreeValue(ctx, prop);
3658+
if (ret) {
3659+
JS_FreeValue(ctx, val);
3660+
return JS_EXCEPTION;
3661+
}
3662+
prop = JS_GetPropertyStr(ctx, val, "events");
3663+
if (JS_IsException(prop)) {
3664+
return JS_EXCEPTION;
3665+
}
3666+
ret = JS_ToUint32(ctx, &events, prop);
3667+
JS_FreeValue(ctx, prop);
3668+
if (ret) {
3669+
JS_FreeValue(ctx, val);
3670+
return JS_EXCEPTION;
3671+
}
3672+
pfds[i].events = events;
3673+
JS_FreeValue(ctx, val);
3674+
}
3675+
3676+
if (argc >= 2 && JS_ToInt32(ctx, &timeoutms, argv[1]))
3677+
return JS_EXCEPTION;
3678+
3679+
if (poll(pfds, nbfd, timeoutms) <= 0)
3680+
return JS_EXCEPTION;
3681+
3682+
val = JS_NewArray(ctx);
3683+
if (JS_IsException(val))
3684+
return val;
3685+
3686+
for (i = 0; i < nbfd; i++) {
3687+
JS_DefinePropertyValueUint32(ctx, val, i, JS_NewInt32(ctx, pfds[i].revents), JS_PROP_C_W_E);
3688+
}
3689+
3690+
return val;
3691+
}
3692+
36193693
static JSValue js_os_recv_send(JSContext *ctx, JSValueConst this_val,
36203694
int argc, JSValueConst *argv, int magic)
36213695
{
@@ -3634,35 +3708,49 @@ static JSValue js_os_recv_send(JSContext *ctx, JSValueConst this_val,
36343708
buf = JS_GetArrayBuffer(ctx, &size, argv[1]);
36353709
if (!buf)
36363710
return JS_EXCEPTION;
3637-
if (JS_ToIndex(ctx, &len, argv[2]))
3711+
if (argc <= 2)
3712+
len = size;
3713+
else if (JS_ToIndex(ctx, &len, argv[2]))
36383714
return JS_EXCEPTION;
36393715
if (len > size)
36403716
return JS_ThrowRangeError(ctx, "recv/send array buffer overflow");
3641-
if ((magic == 2 || magic == 3) && JS_toSockaddrStruct(ctx, argv[1], &saddr))
3717+
if ((magic == 2 || magic == 3) && JS_ToSockaddrStruct(ctx, argv[1], &saddr))
36423718
return JS_EXCEPTION;
3643-
36443719
if (magic == 0)
3645-
ret = js_get_errno(send(fd, buf, len, 0));
3646-
if (magic == 1)
36473720
ret = js_get_errno(recv(fd, buf, len, 0));
3648-
if (magic == 2)
3649-
ret = js_get_errno(sendto(fd, buf, len, 0, (const struct sockaddr *)&saddr, saddrlen));
3650-
if (magic == 3) { // recvfrom returns recvLen + sockaddrRecvFrom
3721+
if (magic == 1)
3722+
ret = js_get_errno(send(fd, buf, len, 0));
3723+
if (magic == 2) { // recvfrom returns recvLen + sockaddrRecvFrom
36513724
ret = js_get_errno(recvfrom(fd, buf, len, 0, (struct sockaddr *)&saddr, &saddrlen));
3652-
addr = js_toSockaddrObj(ctx, &saddr);
3725+
addr = JS_ToSockaddrObj(ctx, &saddr);
36533726
if (JS_IsException(addr))
36543727
return addr; // shall we return ?
36553728
addr_ret = JS_NewArray(ctx);
36563729
if (JS_IsException(addr_ret))
36573730
return addr_ret;
3658-
JS_DefinePropertyValueUint32(ctx, addr_ret, 0, addr, JS_PROP_C_W_E);
3659-
JS_DefinePropertyValueUint32(ctx, addr_ret, 1, JS_NewInt64(ctx, ret),
3660-
JS_PROP_C_W_E);
3731+
JS_DefinePropertyValueUint32(ctx, addr_ret, 0, JS_NewInt64(ctx, ret), JS_PROP_C_W_E);
3732+
JS_DefinePropertyValueUint32(ctx, addr_ret, 1, addr, JS_PROP_C_W_E);
36613733
return addr_ret;
36623734
}
3735+
if (magic == 3)
3736+
ret = js_get_errno(sendto(fd, buf, len, 0, (const struct sockaddr *)&saddr, saddrlen));
36633737
return JS_NewInt64(ctx, ret);
36643738
}
36653739

3740+
static JSValue js_os_shutdown(JSContext *ctx, JSValueConst this_val,
3741+
int argc, JSValueConst *argv)
3742+
{
3743+
int sockfd, how, ret;
3744+
3745+
if (JS_ToInt32(ctx, &sockfd, argv[0]))
3746+
return JS_EXCEPTION;
3747+
if (JS_ToInt32(ctx, &how, argv[1]))
3748+
return JS_EXCEPTION;
3749+
3750+
ret = js_get_errno(shutdown(sockfd, how));
3751+
return JS_NewInt32(ctx, ret);
3752+
}
3753+
36663754
#ifdef USE_WORKER
36673755

36683756
/* Worker */
@@ -4204,25 +4292,38 @@ static const JSCFunctionListEntry js_os_funcs[] = {
42044292
JS_CFUNC_MAGIC_DEF("setsockopt", 3, js_os_get_setsockopt, 1 ),
42054293
JS_CFUNC_DEF("getaddrinfo", 2, js_os_getaddrinfo ),
42064294
JS_CFUNC_DEF("bind", 2, js_os_bind ),
4295+
JS_CFUNC_DEF("poll", 2, js_os_poll ),
42074296
JS_CFUNC_DEF("listen", 1, js_os_listen ),
42084297
JS_CFUNC_DEF("accept", 1, js_os_accept ),
42094298
JS_CFUNC_DEF("connect", 1, js_os_connect ),
4210-
JS_CFUNC_MAGIC_DEF("recv", 4, js_os_recv_send, 0 ),
4211-
JS_CFUNC_MAGIC_DEF("send", 4, js_os_recv_send, 1 ),
4299+
JS_CFUNC_MAGIC_DEF("recv", 3, js_os_recv_send, 0 ),
4300+
JS_CFUNC_MAGIC_DEF("send", 3, js_os_recv_send, 1 ),
42124301
JS_CFUNC_MAGIC_DEF("recvfrom", 4, js_os_recv_send, 2 ),
42134302
JS_CFUNC_MAGIC_DEF("sendto", 4, js_os_recv_send, 3 ),
4303+
JS_CFUNC_DEF("shutdown", 2, js_os_shutdown ),
42144304
OS_FLAG(AF_INET),
42154305
OS_FLAG(AF_INET6),
42164306
OS_FLAG(SOCK_STREAM),
42174307
OS_FLAG(SOCK_DGRAM),
42184308
OS_FLAG(SOCK_RAW),
4309+
OS_FLAG(SOCK_NONBLOCK),
42194310
OS_FLAG(SO_REUSEADDR),
42204311
OS_FLAG(SO_RCVBUF),
4312+
OS_FLAG(SO_ERROR),
4313+
OS_FLAG(SHUT_RD),
4314+
OS_FLAG(SHUT_WR),
4315+
OS_FLAG(SHUT_RDWR),
4316+
OS_FLAG(POLLIN),
4317+
OS_FLAG(POLLPRI),
4318+
OS_FLAG(POLLOUT),
4319+
OS_FLAG(POLLERR),
4320+
OS_FLAG(POLLHUP),
4321+
OS_FLAG(POLLNVAL),
42214322
};
42224323

42234324
static int js_os_init(JSContext *ctx, JSModuleDef *m)
42244325
{
4225-
os_poll_func = js_os_poll;
4326+
os_poll_func = js_os_event_poll;
42264327

42274328
#ifdef USE_WORKER
42284329
{

0 commit comments

Comments
 (0)