aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2022-08-04 11:13:39 +1000
committerSteve Bennett <steveb@workware.net.au>2022-08-20 15:27:19 +1000
commit6e1d4cc163dc37f0ea5cdae8b3e2b9e4b0a73254 (patch)
tree9596172bde8edbcf87bcdb1d9012fa6d0a948167
parent5af9d6a919b83e28a37ade2db8090a375a93ba53 (diff)
downloadjimtcl-6e1d4cc163dc37f0ea5cdae8b3e2b9e4b0a73254.zip
jimtcl-6e1d4cc163dc37f0ea5cdae8b3e2b9e4b0a73254.tar.gz
jimtcl-6e1d4cc163dc37f0ea5cdae8b3e2b9e4b0a73254.tar.bz2
socket: add support for -async
Very similar to Tcl except that read/write can't be done until writable indicates the socket is connected. Signed-off-by: Steve Bennett <steveb@workware.net.au> Documentation fixes - Co-authored-by: Adrian Ho <the.gromgit@gmail.com>
-rw-r--r--jim-aio.c71
-rw-r--r--jim_tcl.txt40
2 files changed, 87 insertions, 24 deletions
diff --git a/jim-aio.c b/jim-aio.c
index 5769f78..f9f3de9 100644
--- a/jim-aio.c
+++ b/jim-aio.c
@@ -1209,26 +1209,32 @@ static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
}
#ifdef O_NDELAY
+static int aio_set_nonblocking(int fd, int nb)
+{
+ int fmode = fcntl(fd, F_GETFL);
+ if (nb) {
+ fmode |= O_NDELAY;
+ }
+ else {
+ fmode &= ~O_NDELAY;
+ }
+ (void)fcntl(fd, F_SETFL, fmode);
+ return fmode;
+}
+
static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
- int fmode = fcntl(af->fd, F_GETFL);
-
if (argc) {
long nb;
if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
return JIM_ERR;
}
- if (nb) {
- fmode |= O_NDELAY;
- }
- else {
- fmode &= ~O_NDELAY;
- }
- (void)fcntl(af->fd, F_SETFL, fmode);
+ aio_set_nonblocking(af->fd, nb);
}
+ int fmode = fcntl(af->fd, F_GETFL);
Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
return JIM_OK;
}
@@ -2197,21 +2203,38 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
int type = SOCK_STREAM;
Jim_Obj *argv0 = argv[0];
int ipv6 = 0;
+ int async = 0;
- if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
- if (!IPV6) {
- Jim_SetResultString(interp, "ipv6 not supported", -1);
+
+ while (argc > 1 && Jim_String(argv[1])[0] == '-') {
+ static const char * const options[] = { "-async", "-ipv6", NULL };
+ enum { OPT_ASYNC, OPT_IPV6 };
+ int option;
+
+ if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
- ipv6 = 1;
- family = PF_INET6;
+ switch (option) {
+ case OPT_ASYNC:
+ async = 1;
+ break;
+
+ case OPT_IPV6:
+ if (!IPV6) {
+ Jim_SetResultString(interp, "ipv6 not supported", -1);
+ return JIM_ERR;
+ }
+ ipv6 = 1;
+ family = PF_INET6;
+ break;
+ }
+ argc--;
+ argv++;
}
- argc -= ipv6;
- argv += ipv6;
if (argc < 2) {
wrongargs:
- Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
+ Jim_WrongNumArgs(interp, 1, &argv0, "?-async? ?-ipv6? type ?address?");
return JIM_ERR;
}
@@ -2347,6 +2370,9 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
JimAioSetError(interp, NULL);
return JIM_ERR;
}
+ if (async) {
+ aio_set_nonblocking(sock, 1);
+ }
if (bind_addr) {
if (JimParseSocketAddress(interp, family, type, bind_addr, &sa, &salen) != JIM_OK) {
close(sock);
@@ -2367,9 +2393,14 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return JIM_ERR;
}
if (connect(sock, &sa.sa, salen)) {
- Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno));
- close(sock);
- return JIM_ERR;
+ if (async && errno == EINPROGRESS) {
+ /* OK */
+ }
+ else {
+ Jim_SetResultFormatted(interp, "%s: connect: %s", connect_addr, strerror(errno));
+ close(sock);
+ return JIM_ERR;
+ }
}
}
if (do_listen) {
diff --git a/jim_tcl.txt b/jim_tcl.txt
index f1ab2cd..2738d0b 100644
--- a/jim_tcl.txt
+++ b/jim_tcl.txt
@@ -5135,18 +5135,18 @@ Various socket types may be created.
+*socket unix.dgram.server* 'path'+::
A unix domain socket datagram server server listening on 'path'
-+*socket ?-ipv6? stream* 'addr:port'+::
++*socket ?-async? ?-ipv6? stream* 'addr:port'+::
A TCP socket client. (See the forms for +'addr'+ below)
-+*socket ?-ipv6? stream.server* '?addr:?port'+::
++*socket ?-async? ?-ipv6? stream.server* '?addr:?port'+::
A TCP socket server (+'addr'+ defaults to +0.0.0.0+ for IPv4 or +[::]+ for IPv6).
-+*socket ?-ipv6? dgram* ?'addr:port'?+::
++*socket ?-async? ?-ipv6? dgram* ?'addr:port'?+::
A UDP socket client. If the address is not specified,
the client socket will be unbound and 'sendto' must be used
to indicated the destination.
-+*socket ?-ipv6? dgram.server* 'addr:port'+::
++*socket ?-async? ?-ipv6? dgram.server* 'addr:port'+::
A UDP socket server.
+*socket pipe*+::
@@ -5209,6 +5209,38 @@ The path for Unix domain sockets is automatically removed when the socket
is closed. Use `close -nodelete` in the rare case where this behaviour
should be avoided (e.g. after `os.fork`).
+If '+-async+' is specified, the socket is opened in non-blocking (ndelay) mode
+and 'connect' is non-blocking (applies to 'stream'). In this case, a 'writable' handler
+should be used that will fire when the connect succeeds or fails, and calling 'peername' on the handle
+will succeed if connected or fail if connect failed. Typical usage is as follows:
+
+----
+ set s [socket -async stream host:port]
+
+ $s writable {
+ # Remove writable either way
+ $s writable {}
+ try {
+ $s peername
+ } on error msg {
+ # Connect failed
+ incr done
+ return
+ }
+
+ $s readable {
+ set buf [$s read]
+ if {[$s eof]} {
+ # Closed connection
+ incr done
+ }
+ ...
+ }
+ }
+
+ vwait done
+----
+
syslog
~~~~~~
+*syslog* '?options? ?priority? message'+