1
0
mirror of https://github.com/janet-lang/janet synced 2025-08-03 20:43:55 +00:00
This commit is contained in:
Calvin Rose 2025-02-15 23:22:12 -06:00
parent 53bcc15207
commit 2e6001316a
4 changed files with 61 additions and 14 deletions

View File

@ -3877,8 +3877,8 @@
(compwhen (dyn 'net/listen) (compwhen (dyn 'net/listen)
(defn net/server (defn net/server
"Start a server asynchronously with `net/listen` and `net/accept-loop`. Returns the new server stream." "Start a server asynchronously with `net/listen` and `net/accept-loop`. Returns the new server stream."
[host port &opt handler type] [host port &opt handler type no-reuse]
(def s (net/listen host port type)) (def s (net/listen host port type no-reuse))
(if handler (if handler
(ev/go (fn [] (net/accept-loop s handler)))) (ev/go (fn [] (net/accept-loop s handler))))
s)) s))

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024 Calvin Rose * Copyright (c) 2025 Calvin Rose
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to * of this software and associated documentation files (the "Software"), to
@ -1789,6 +1789,22 @@ void janet_stream_edge_triggered(JanetStream *stream) {
} }
void janet_stream_level_triggered(JanetStream *stream) { void janet_stream_level_triggered(JanetStream *stream) {
/* On macos, we seem to need to delete any registered events before re-registering without
* EV_CLEAR, otherwise the new event will still have EV_CLEAR set erroneously. This could be a
* kernel bug, but unfortunately the specification is vague here, esp. in regards to where and when
* EV_CLEAR is set automatically. */
struct kevent kevs[2];
int length = 0;
if (stream->flags & (JANET_STREAM_READABLE | JANET_STREAM_ACCEPTABLE)) {
EV_SETx(&kevs[length++], stream->handle, EVFILT_READ, EV_DELETE, 0, 0, stream);
}
if (stream->flags & JANET_STREAM_WRITABLE) {
EV_SETx(&kevs[length++], stream->handle, EVFILT_WRITE, EV_DELETE, 0, 0, stream);
}
int status;
do {
status = kevent(janet_vm.kq, kevs, length, NULL, 0, NULL);
} while (status == -1 && errno == EINTR);
janet_register_stream_impl(stream, 0); janet_register_stream_impl(stream, 0);
} }

View File

@ -554,7 +554,10 @@ JANET_CORE_FN(cfun_net_connect,
int err = WSAGetLastError(); int err = WSAGetLastError();
freeaddrinfo(ai); freeaddrinfo(ai);
#else #else
int status = connect(sock, addr, addrlen); int status;
do {
status = connect(sock, addr, addrlen);
} while (status == -1 && errno == EINTR);
int err = errno; int err = errno;
if (is_unix) { if (is_unix) {
janet_free(ai); janet_free(ai);

View File

@ -1,4 +1,4 @@
# Copyright (c) 2023 Calvin Rose & contributors # Copyright (c) 2025 Calvin Rose & contributors
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to # of this software and associated documentation files (the "Software"), to
@ -199,7 +199,7 @@
(assert s "made server 1") (assert s "made server 1")
(defn test-echo [msg] (defn test-echo [msg]
(with [conn (net/connect test-host test-port)] (with [conn (assert (net/connect test-host test-port))]
(net/write conn msg) (net/write conn msg)
(def res (net/read conn 1024)) (def res (net/read conn 1024))
(assert (= (string res) msg) (string "echo " msg)))) (assert (= (string res) msg) (string "echo " msg))))
@ -213,6 +213,7 @@
# Test on both server and client # Test on both server and client
# 504411e # 504411e
(var iterations 0)
(defn names-handler (defn names-handler
[stream] [stream]
(defer (:close stream) (defer (:close stream)
@ -220,21 +221,26 @@
(ev/read stream 1) (ev/read stream 1)
(def [host port] (net/localname stream)) (def [host port] (net/localname stream))
(assert (= host test-host) "localname host server") (assert (= host test-host) "localname host server")
(assert (= port (scan-number test-port)) "localname port server"))) (assert (= port (scan-number test-port)) "localname port server")
(++ iterations)
(ev/write stream " ")))
# Test localname and peername # Test localname and peername
# 077bf5eba # 077bf5eba
(repeat 10 (repeat 10
(with [s (net/server test-host test-port names-handler)] (with [s (net/server test-host test-port names-handler)]
(repeat 10 (repeat 10
(with [conn (net/connect test-host test-port)] (with [conn (assert (net/connect test-host test-port))]
(def [host port] (net/peername conn)) (def [host port] (net/peername conn))
(assert (= host test-host) "peername host client ") (assert (= host test-host) "peername host client ")
(assert (= port (scan-number test-port)) "peername port client") (assert (= port (scan-number test-port)) "peername port client")
# let server close (++ iterations)
(ev/write conn " ")))) (ev/write conn " ")
(ev/read conn 1))))
(gccollect)) (gccollect))
(assert (= iterations 200) "localname and peername not enough checks")
# Create pipe # Create pipe
# 12f09ad2d # 12f09ad2d
(var pipe-counter 0) (var pipe-counter 0)
@ -422,7 +428,7 @@
(assert (= result text) (string/format "expected %v, got %v" text result))) (assert (= result text) (string/format "expected %v, got %v" text result)))
# Now do our telnet chat # Now do our telnet chat
(def bob (net/connect test-host test-port :stream)) (def bob (assert (net/connect test-host test-port :stream)))
(expect-read bob "Whats your name?\n") (expect-read bob "Whats your name?\n")
(if (= :mingw (os/which)) (if (= :mingw (os/which))
(net/write bob "bob") (net/write bob "bob")
@ -432,7 +438,7 @@
(file/flush fbob) (file/flush fbob)
(:close fbob))) (:close fbob)))
(expect-read bob "Welcome bob\n") (expect-read bob "Welcome bob\n")
(def alice (net/connect test-host test-port)) (def alice (assert (net/connect test-host test-port)))
(expect-read alice "Whats your name?\n") (expect-read alice "Whats your name?\n")
(net/write alice "alice") (net/write alice "alice")
(expect-read alice "Welcome alice\n") (expect-read alice "Welcome alice\n")
@ -446,7 +452,7 @@
(expect-read bob "[alice]:hi\n") (expect-read bob "[alice]:hi\n")
# Ted joins the chat server # Ted joins the chat server
(def ted (net/connect test-host test-port)) (def ted (assert (net/connect test-host test-port)))
(expect-read ted "Whats your name?\n") (expect-read ted "Whats your name?\n")
(net/write ted "ted") (net/write ted "ted")
(expect-read ted "Welcome ted\n") (expect-read ted "Welcome ted\n")
@ -485,9 +491,31 @@
(ev/chan-close c) (ev/chan-close c)
# soreuseport on unix domain sockets # soreuseport on unix domain sockets
(compwhen (= :linux (os/which)) (compwhen (or (= :macos (os/which)) (= :linux (os/which)))
(assert-no-error "unix-domain socket reuseaddr" (assert-no-error "unix-domain socket reuseaddr"
(let [s (net/listen :unix "./unix-domain-socket" :stream)] (let [s (net/listen :unix "./unix-domain-socket" :stream)]
(:close s)))) (:close s))))
# net/accept-loop level triggering
(gccollect)
(def maxconn 50)
(var connect-count 0)
(defn level-trigger-handling
[conn &]
(with [conn conn]
(ev/write conn (ev/read conn 4096))
(++ connect-count)))
(def s (assert (net/server test-host test-port level-trigger-handling)))
(def cons @[])
(repeat maxconn (array/push cons (assert (net/connect test-host test-port))))
(assert (= maxconn (length cons)))
(defn do-connect [i]
(with [c (get cons i)]
(ev/write c "abc123")
(ev/read c 4096)))
(for i 0 maxconn (ev/spawn (do-connect i)))
(ev/sleep 0.1)
(assert (= maxconn connect-count))
(:close s)
(end-suite) (end-suite)