From cworth at cworth.org Fri Feb 1 17:17:43 2008 From: cworth at cworth.org (Carl Worth) Date: Sat, 02 Feb 2008 12:17:43 +1100 Subject: [Nickle] [RFC] Socket::create: Add support for Unix domain sockets Message-ID: <87zluktavc.wl%cworth@cworth.org> --- This fills an obvious hole in the implementation. The biggest potential concern here is that this breaks all existing socket-using nickle programs, (since create now accepts two arguments instead of one). So we probably don't even want to apply this in its current state. Keith's idea for an improved patch is one that would provide a varargs create with enums for family and type so that one could pass them in either order, or omit the family to have it default to AF_INET. That could potentially avoid breaking the interface for existing programs. Also, it would be nice to add a test or two to the test suite for the socket interface. builtin-sockets.c | 127 +++++++++++++++++++++++++++++++++++----------------- 1 files changed, 85 insertions(+), 42 deletions(-) diff --git a/builtin-sockets.c b/builtin-sockets.c index 32cec13..47b6ab1 100644 --- a/builtin-sockets.c +++ b/builtin-sockets.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -32,7 +34,7 @@ NamespacePtr SocketNamespace; Type *typeSockaddr; -Value do_Socket_create (Value type); +Value do_Socket_create (Value family, Value type); Value do_Socket_connect (Value s, Value host, Value port); Value do_Socket_bind (Value s, Value host, Value port); Value do_Socket_listen (Value s, Value backlog); @@ -55,12 +57,6 @@ import_Socket_namespace() }; static const struct fbuiltin_1 funcs_1[] = { - { do_Socket_create, "create", "f", "i", "\n" - " file create (int type)\n" - "\n" - " Create a socket where 'type' is one of:\n" - " SOCK_STREAM: a stream socket.\n" - " SOCK_DGRAM: a datagram socket.\n" }, { do_Socket_accept, "accept", "f", "f", "\n" " file accept (file listen)\n" "\n" @@ -73,6 +69,16 @@ import_Socket_namespace() }; static const struct fbuiltin_2 funcs_2[] = { + { do_Socket_create, "create", "f", "ii", "\n" + " file create (int type)\n" + "\n" + " Create a socket where 'family' is one of:\n" + " AF_UNIX: Local communication.\n" + " AF_INET: IPv4 Internet protocols.\n" + " AF_INET6: IPv6 Internet protocols.\n" + " and where 'type' is one of:\n" + " SOCK_STREAM: a stream socket.\n" + " SOCK_DGRAM: a datagram socket.\n" }, { do_Socket_listen, "listen", "v", "fi", "\n" " void listen (file socket, int length)\n" "\n" @@ -100,6 +106,11 @@ import_Socket_namespace() }; static const struct ibuiltin ivars[] = { + { AF_UNIX, "AF_UNIX", &SocketNamespace }, + { AF_INET, "AF_INET", &SocketNamespace }, +#ifdef AF_INET6 + { AF_INET6, "AF_INET6", &SocketNamespace }, +#endif { SOCK_STREAM, "SOCK_STREAM", &SocketNamespace }, { SOCK_DGRAM, "SOCK_DGRAM", &SocketNamespace }, { SHUT_RD, "SHUT_RD", &SocketNamespace }, @@ -143,21 +154,32 @@ import_Socket_namespace() /* File::file do_Socket_create ({SOCK_STREAM,SOCK_DGRAM} type); */ Value -do_Socket_create (Value type) +do_Socket_create (Value family, Value type) { ENTER (); - int itype, s; + int ifamily, itype, s; + ifamily = IntPart (family, "Illegal address family"); itype = IntPart (type, "Illegal socket type"); if (aborting) RETURN (Void); - s = socket (PF_INET, itype, 0); + s = socket (ifamily, itype, 0); if (s == -1) RETURN (Void); RETURN (FileCreate (s, FileReadable|FileWritable)); } -static Bool address_lookup (Value hostname, Value portname, - struct sockaddr_in *addr) +typedef union { + struct sockaddr_in in; + struct sockaddr_un un; + struct sockaddr addr; + struct { + struct sockaddr_un un; + char path[PATH_MAX]; + } align; +} sockaddr_all_t; + +static Bool address_lookup (int s, Value hostname, Value portname, + sockaddr_all_t *addr, socklen_t *len) { struct hostent *host; struct servent *port; @@ -176,32 +198,51 @@ static Bool address_lookup (Value hostname, Value portname, if (*hostchars == '\0' || *portchars == '\0') return False; /* FIXME: more here? */ - addr->sin_family = AF_INET; - - /* host lookup */ - host = gethostbyname (hostchars); - if (host == 0) - { - herror ("address_lookup"); - return False; /* FIXME: more here? */ - } - memcpy (&addr->sin_addr.s_addr, host->h_addr_list[0], sizeof addr->sin_addr.s_addr); + /* Read the address family back from the kernel. */ + *len = sizeof (*addr); + if (getsockname (s, &addr->addr, len) < 0) + return False; - /* port lookup */ - portnum = strtol (portchars, &endptr, /* base */ 10); - if (*endptr != '\0') /* non-numeric port specification */ - { - /* FIXME: this should not always be "tcp"! */ - port = getservbyname (portchars, "tcp"); - if (port == 0) - return False; /* FIXME: more here? */ - addr->sin_port = port->s_port; - } - else - { - if (portnum <= 0 || portnum >= (1 << 16)) + switch (addr->addr.sa_family) { + case AF_UNIX: + if (strlen (portchars) > PATH_MAX) + return False; + *len = SUN_LEN (&addr->un); + strcpy (addr->un.sun_path, portchars); + break; + case AF_INET: + /* host lookup */ + host = gethostbyname (hostchars); + if (host == 0) + { + herror ("address_lookup"); return False; /* FIXME: more here? */ - addr->sin_port = htons (portnum); + } + memcpy (&addr->in.sin_addr.s_addr, host->h_addr_list[0], sizeof addr->in.sin_addr.s_addr); + + /* port lookup */ + portnum = strtol (portchars, &endptr, /* base */ 10); + + if (*endptr != '\0') /* non-numeric port specification */ + { + /* FIXME: this should not always be "tcp"! */ + port = getservbyname (portchars, "tcp"); + if (port == 0) + return False; /* FIXME: more here? */ + addr->in.sin_port = port->s_port; + } + else + { + if (portnum <= 0 || portnum >= (1 << 16)) + return False; /* FIXME: more here? */ + addr->in.sin_port = htons (portnum); + } + break; +#if AF_INET6 + case AF_INET6: +#endif + default: + return False; } return True; @@ -212,9 +253,10 @@ Value do_Socket_connect (Value s, Value host, Value port) { ENTER (); - struct sockaddr_in addr; + sockaddr_all_t addr; + socklen_t len; - if (!address_lookup (host, port, &addr)) + if (!address_lookup (s->file.fd, host, port, &addr, &len)) RETURN (Void); if (!running->thread.partial) @@ -230,7 +272,7 @@ do_Socket_connect (Value s, Value host, Value port) (char *) &one, sizeof (int)); } #endif - n = connect (s->file.fd, (struct sockaddr *) &addr, sizeof addr); + n = connect (s->file.fd, &addr.addr, len); flags &= ~O_NONBLOCK; fcntl (s->file.fd, F_SETFL, flags); err = errno; @@ -266,9 +308,10 @@ Value do_Socket_bind (Value s, Value host, Value port) { ENTER (); - struct sockaddr_in addr; + sockaddr_all_t addr; + socklen_t len; - if (!address_lookup (host, port, &addr)) + if (!address_lookup (s->file.fd, host, port, &addr, &len)) RETURN (Void); #ifdef SO_REUSEADDR @@ -277,7 +320,7 @@ do_Socket_bind (Value s, Value host, Value port) setsockopt (s->file.fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof (int)); } #endif - if (bind (s->file.fd, (struct sockaddr *) &addr, sizeof addr) == -1) + if (bind (s->file.fd, &addr.addr, len) == -1) { RaiseStandardException (exception_io_error, FileGetErrorMessage (errno), -- 1.5.3.2 -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080202/1e4f8cd4/attachment.pgp From cworth at cworth.org Fri Feb 1 17:26:53 2008 From: cworth at cworth.org (Carl Worth) Date: Sat, 02 Feb 2008 12:26:53 +1100 Subject: [Nickle] SIGINT fails to raise an exception Message-ID: <87y7a4tag2.wl%cworth@cworth.org> Here's a simple-enough program: twixt (printf ("Thanks, "); printf ("I needed that\n") ) { while (true) sleep (1000); } that currently fails. It appears that giving a nickle responds to SIGINT by simply printing "" and exiting rather than raising an exception so that my twixt can handle things the way it wants to. -Carl -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080202/55de5883/attachment.pgp From cworth at cworth.org Fri Feb 1 17:07:07 2008 From: cworth at cworth.org (Carl Worth) Date: Sat, 02 Feb 2008 12:07:07 +1100 Subject: [Nickle] Artificial distinction between import and autoimport Message-ID: <873ascupxg.wl%cworth@cworth.org> So I started writing a nickle program today[*] and ended up with the following code: import Socket; import File; autoimport Process; I contend that the distinction of "core" namespaces like Socket and File and "non core" namespaces like Process is arbitrary and totally uninteresting to the user. So forcing the user to remember which is which is just not nice. Keith explains that the distinction comes down to whether the core of nickle uses the namespace or not, (and there's actually a desire to keep the core as small as possible so that nickle can load fast). So there's even the potential for namespaces to move from core to non-core. Again, not nice for the user. So Keith says "There's no harm in autoloading a core namespace, why don't you just do that?". But if I'm going to always type "autoload" for all namespaces, why don't we just settle on the "import" name and make it do exactly what autoimport does today. Bart, I'm informed that this all takes place in fairly twisty passages of the implementation, and that you are likely to be much more successful at finding your way through them than I am. Care to take a whack at it? -Carl PS. Same applies to "load" and "autoload" of course. [*] 60 lines of code and 1 nickle bug[**] found so far. [**] twixt (statement; /* nothing here */) was segfaulting---keithp will push the fix soon -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080202/eeed11c7/attachment.pgp From cworth at cworth.org Fri Feb 1 17:11:25 2008 From: cworth at cworth.org (Carl Worth) Date: Sat, 02 Feb 2008 12:11:25 +1100 Subject: [Nickle] Scope for condition of do while Message-ID: <871w7wupqa.wl%cworth@cworth.org> Should this be a legitimate thing to do? do { putchar (int c = getc (socket)); } while (c != '.'); That is, should the while condition share scope with the block above even though it's syntactically outside? Keith whipped up a special-case implementation of this, but he's definitely not sure that it's not a horrible idea. He's planning to cogitate a bit more, so I'm just sending this to remind him to type his thoughts at some point, and also to solicit any other thoughts others might have. -Carl -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080202/bb7b795e/attachment.pgp From keithp at keithp.com Fri Feb 1 17:44:12 2008 From: keithp at keithp.com (Keith Packard) Date: Fri, 1 Feb 2008 17:44:12 -0800 (PST) Subject: [Nickle] nickle: Branch 'master' - 2 commits Message-ID: <20080202014412.A02E328001@keithp.com> builtin-file.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nickle.h | 5 ++ sched.c | 1 3 files changed, 122 insertions(+), 1 deletion(-) New commits: commit 20a5db3ef34d47fdff2dd98052b577db80f76ffc Author: Keith Packard Date: Sat Feb 2 12:32:45 2008 +1100 Add unlink, rename, mkdir, rmdir builtins. diff --git a/builtin-file.c b/builtin-file.c index fbb149b..f5751f1 100644 --- a/builtin-file.c +++ b/builtin-file.c @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include "builtin.h" NamespacePtr FileNamespace; @@ -88,6 +91,14 @@ import_File_namespace() "\n" " Return whether 'f' is associated with an interactive\n" " terminal device\n" }, + { do_File_unlink, "unlink", "v", "s", "\n" + " void unlink (string name)\n" + "\n" + " Delete the filename 'name'\n" }, + { do_File_rmdir, "rmdir", "v", "s", "\n" + " void rmdir (string name)\n" + "\n" + " Delete the directory 'name'\n" }, { 0 } }; @@ -127,6 +138,14 @@ import_File_namespace() " int ungetc (int c, file f)\n" "\n" " Pushes the character 'c' back on file 'f'.\n" }, + { do_File_rename, "rename", "v", "ss", "\n" + " void rename (string oldname, string newname)\n" + "\n" + " Renames a file\n" }, + { do_File_mkdir, "mkdir", "v", "si", "\n" + " void mkdir (string name, int mode)\n" + "\n" + " Create the new directory 'name'\n" }, { 0 } }; @@ -181,6 +200,13 @@ import_File_namespace() " io_error (string message, error_type error, file f)\n" "\n" " Raised when an i/o error occurs.\n" }, + {"name_error", exception_name_error, "sEs", "\n" + " name_error (string message, error_type error, string name)\n" + "\n" + " Raised when an operation involving a filename fails.\n" + " 'message' is a printable error string.\n" + " 'error' is a symbolic error code.\n" + " 'name' is the filename which failed.\n" }, {0, 0 }, }; @@ -618,3 +644,94 @@ do_File_setbuf (Value f, Value v) FileSetBuffer (f, i); RETURN (v); } + +Value +do_File_unlink (Value name) +{ + ENTER (); + char *n; + int ret; + + n = StrzPart (name, "invalid file name"); + if (!n) + RETURN (Void); + ret = unlink (n); + if (ret < 0) { + int err = errno; + RaiseStandardException (exception_name_error, + FileGetErrorMessage (err), + 2, FileGetError (err), name); + RETURN (Void); + } + RETURN (Void); +} + +Value +do_File_rename (Value old, Value new) +{ + ENTER (); + char *o, *n; + int ret; + + o = StrzPart (old, "invalid file name"); + if (!o) + RETURN (Void); + n = StrzPart (new, "invalid file name"); + if (!n) + RETURN (Void); + ret = rename (o, n); + if (ret < 0) { + int err = errno; + RaiseStandardException (exception_name_error, + FileGetErrorMessage (err), + 2, FileGetError (err), new); + RETURN (Void); + } + RETURN (Void); +} + +Value +do_File_mkdir (Value name, Value mode) +{ + ENTER (); + char *n; + int m; + int ret; + + n = StrzPart (name, "invalid file name"); + if (!n) + RETURN (Void); + m = IntPart (mode, "invalid file mode"); + if (aborting) + RETURN (Void); + ret = mkdir (n, m); + if (ret < 0) { + int err = errno; + RaiseStandardException (exception_name_error, + FileGetErrorMessage (err), + 2, FileGetError (err), name); + RETURN (Void); + } + RETURN (Void); +} + +Value +do_File_rmdir (Value name) +{ + ENTER (); + char *n; + int ret; + + n = StrzPart (name, "invalid file name"); + if (!n) + RETURN (Void); + ret = rmdir (n); + if (ret < 0) { + int err = errno; + RaiseStandardException (exception_name_error, + FileGetErrorMessage (err), + 2, FileGetError (err), name); + RETURN (Void); + } + RETURN (Void); +} diff --git a/nickle.h b/nickle.h index 903ae2f..c5ad821 100644 --- a/nickle.h +++ b/nickle.h @@ -762,6 +762,7 @@ typedef enum _standardException { exception_invalid_unop_value, /* string poly */ exception_open_error, /* string integer string */ exception_io_error, /* string integer file */ + exception_name_error, /* string integer string */ _num_standard_exceptions } StandardException; @@ -912,6 +913,8 @@ Value do_File_string_read (Value); Value do_File_string_string (Value); Value do_File_isatty (Value); Value do_File_status (Value); +Value do_File_unlink (Value); +Value do_File_rmdir (Value); Value do_String_length (Value); Value do_String_new (Value); Value do_Primitive_random (Value); @@ -940,6 +943,8 @@ Value do_File_putc (Value, Value); Value do_File_ungetb (Value, Value); Value do_File_ungetc (Value, Value); Value do_File_setbuf (Value, Value); +Value do_File_rename (Value, Value); +Value do_File_mkdir (Value, Value); Value do_String_index (Value, Value); Value do_setjmp (Value, Value); Value do_setdims (Value, Value); commit b5830866a10e7eed272c52e7aafc17d3593c2dac Author: Keith Packard Date: Sat Feb 2 12:29:52 2008 +1100 Twixt mark function shouldn't reference leave instruction. Instructions cannot be referenced directly, only the object block containing them. The twixt continuation already has a reference to the object, so the extra MemReference on the leave instruction is spurious (and causes crashing). diff --git a/sched.c b/sched.c index 7445608..573dce3 100644 --- a/sched.c +++ b/sched.c @@ -1097,7 +1097,6 @@ TwixtMark (void *object) TwixtPtr twixt = object; ContinuationMark (&twixt->continuation); - MemReference (twixt->leave); } DataType TwixtType = { TwixtMark, 0, "TwixtType" }; From bart at po8.org Fri Feb 1 19:22:25 2008 From: bart at po8.org (Bart Massey) Date: Fri, 01 Feb 2008 19:22:25 -0800 Subject: [Nickle] [RFC] Socket::create: Add support for Unix domain sockets In-Reply-To: <87zluktavc.wl%cworth@cworth.org> References: <87zluktavc.wl%cworth@cworth.org> Message-ID: Yeah, just make this patch be for _create and do all the varargs magic in Nickle. Thanks for the patch! Bart In message <87zluktavc.wl%cworth at cworth.org> you wrote: > This fills an obvious hole in the implementation. The biggest > potential concern here is that this breaks all existing socket-using > nickle programs, (since create now accepts two arguments instead of > one). > > So we probably don't even want to apply this in its current state. > > Keith's idea for an improved patch is one that would provide a > varargs create with enums for family and type so that one could pass > them in either order, or omit the family to have it default to > AF_INET. That could potentially avoid breaking the interface for > existing programs. > > Also, it would be nice to add a test or two to the test suite for the > socket interface. From bart at po8.org Fri Feb 1 19:41:04 2008 From: bart at po8.org (Bart Massey) Date: Fri, 01 Feb 2008 19:41:04 -0800 Subject: [Nickle] Artificial distinction between import and autoimport In-Reply-To: <873ascupxg.wl%cworth@cworth.org> References: <873ascupxg.wl%cworth@cworth.org> Message-ID: These keywords need clear documentation. load filename: brings an arbitrary file into memory import namespace-name: brings the contents of the given namespace, which must already be in memory, into scope autoload namespace-name: checks to see if the given namespace is already in memory. if not, constructs a filename from the namespace name, tries to find this filename on disk, and if found, loads it, regardless of its contents. autoimport namespace-name: checks to see if the given namespace is already in memory. if not, autoload the given namespace. then import the given namespace. "autoimport" is semantically equivalent to "autoload and then import". In a Nickle program intended to be invoked externally, you'd typically just say autoimport Socket, File, Process; and be happy when these namespaces were imported from wherever it is they came from. If you're typing around debugging your namespace from the Nickle prompt, though, autoimport doesn't do what you want at all after the first time, because autoload doesn't. You might start your session with autoimport NS; but if you change ns.5c you'd better type load "ns.5c"; import NS; or you'll not bring your changes into memory and then into scope. We could add a command that was autoreimport NS; or somesuch to combine these actions---if anyone cared. Hope that clarifies the confusion, and that there's no need for change now. If not, let me know. Bart In message <873ascupxg.wl%cworth at cworth.org> you wrote: > So I started writing a nickle program today[*] and ended up with the > following code: > > import Socket; > import File; > autoimport Process; > > I contend that the distinction of "core" namespaces like Socket and > File and "non core" namespaces like Process is arbitrary and totally > uninteresting to the user. So forcing the user to remember which is > which is just not nice. > > Keith explains that the distinction comes down to whether the core of > nickle uses the namespace or not, (and there's actually a desire to > keep the core as small as possible so that nickle can load fast). So > there's even the potential for namespaces to move from core to > non-core. Again, not nice for the user. > > So Keith says "There's no harm in autoloading a core namespace, why > don't you just do that?". But if I'm going to always type "autoload" > for all namespaces, why don't we just settle on the "import" name and > make it do exactly what autoimport does today. > > Bart, I'm informed that this all takes place in fairly twisty passages > of the implementation, and that you are likely to be much more > successful at finding your way through them than I am. Care to take a > whack at it? > > -Carl From bart at po8.org Fri Feb 1 19:56:17 2008 From: bart at po8.org (Bart Massey) Date: Fri, 01 Feb 2008 19:56:17 -0800 Subject: [Nickle] Scope for condition of do while In-Reply-To: <871w7wupqa.wl%cworth@cworth.org> References: <871w7wupqa.wl%cworth@cworth.org> Message-ID: In message <871w7wupqa.wl%cworth at cworth.org> you wrote: > Should this be a legitimate thing to do? > > do { > putchar (int c = getc (socket)); > } while (c != '.'); > > That is, should the while condition share scope with the block above > even though it's syntactically outside? > > Keith whipped up a special-case implementation of this, but he's > definitely not sure that it's not a horrible idea. He's planning to > cogitate a bit more, so I'm just sending this to remind him to type > his thoughts at some point, and also to solicit any other thoughts > others might have. > > -Carl Note that if you replace '.' with EOF in your example, it breaks it. Normally one would have to write something like while (true) { int c = getc(socket); if (c != '.') break; putchar(c); } On the other hand, our try scoping hack set the precedent that it's OK to continue the scope a ways after the block. try { int c = getc(socket); } catch io_error(e,s,f) {} if (c != '.') putchar(c); The above is legal. Maybe do should be special too. Bart From keithp at keithp.com Sat Feb 2 01:09:02 2008 From: keithp at keithp.com (Keith Packard) Date: Sat, 02 Feb 2008 20:09:02 +1100 Subject: [Nickle] Artificial distinction between import and autoimport In-Reply-To: References: <873ascupxg.wl%cworth@cworth.org> Message-ID: <1201943342.19008.47.camel@koto.keithp.com> On Fri, 2008-02-01 at 19:41 -0800, Bart Massey wrote: > In a Nickle program intended to be invoked externally, you'd > typically just say > > autoimport Socket, File, Process; Sure, but why 'autoimport' instead of just 'import'? There's no reason to make the user know what is included in the base system and which parts are external. We should be able to uniformly replace 'autoimport' with 'import' and have things 'just work'. Similarly, it would be nice to have 'load' have the same trick of looking for the conventional file for a specific namespace. -- keith.packard at intel.com -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part Url : /pipermail/nickle/attachments/20080202/753d5390/attachment.pgp From keithp at keithp.com Sat Feb 2 01:14:13 2008 From: keithp at keithp.com (Keith Packard) Date: Sat, 02 Feb 2008 20:14:13 +1100 Subject: [Nickle] Scope for condition of do while In-Reply-To: References: <871w7wupqa.wl%cworth@cworth.org> Message-ID: <1201943653.19008.50.camel@koto.keithp.com> On Fri, 2008-02-01 at 19:56 -0800, Bart Massey wrote: > On the other hand, our try scoping hack set the precedent > that it's OK to continue the scope a ways after the block. I'd completely forgotten that hack, and didn't even see it in the grammar. I did add more productions to make do ... while work the same way. -- keith.packard at intel.com -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part Url : /pipermail/nickle/attachments/20080202/1cd101f9/attachment.pgp From bart at po8.org Sat Feb 2 23:56:13 2008 From: bart at po8.org (Bart Massey) Date: Sat, 02 Feb 2008 23:56:13 -0800 Subject: [Nickle] Artificial distinction between import and autoimport In-Reply-To: <1201943342.19008.47.camel@koto.keithp.com> References: <873ascupxg.wl%cworth@cworth.org> <1201943342.19008.47.camel@koto.keithp.com> Message-ID: In message <1201943342.19008.47.camel at koto.keithp.com> you wrote: > On Fri, 2008-02-01 at 19:41 -0800, Bart Massey wrote: > > In a Nickle program intended to be invoked externally, you'd > > typically just say > > > > autoimport Socket, File, Process; > > Sure, but why 'autoimport' instead of just 'import'? There's no reason > to make the user know what is included in the base system and which > parts are external. We should be able to uniformly replace 'autoimport' > with 'import' and have things 'just work'. Similarly, it would be nice > to have 'load' have the same trick of looking for the conventional file > for a specific namespace. I'm not getting it. The important distinction between autoimport and import is that autoimport won't clobber an existing namespace from an external file, but import will. This is also the distinction between autoload and load. If you want to have a version of load that takes a namespace-name instead of a filename, I suppose that's useful; I'd prefer to give it yet another name, though, rather than be polymorphic. But I'm not getting what you propose to have your revised import or load do if their namespace target already exists. Should they replace the namespace, or stop? Both behaviors have sensible use cases I described previously, so I'm not sure how to make it "just work". Perhaps you want load and import to behave differently at the command prompt vs in a loaded file? Bart From keithp at keithp.com Sun Feb 3 20:00:51 2008 From: keithp at keithp.com (Keith Packard) Date: Sun, 3 Feb 2008 20:00:51 -0800 (PST) Subject: [Nickle] nickle: Branch 'master' - 4 commits Message-ID: <20080204040051.6F353E8025@keithp.com> Makefile.am | 3 alarm.c | 3 builtin-namespaces.h | 1 builtin-process.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++ builtin-thread.c | 18 ++- builtin.c | 1 builtin.h | 6 + execute.c | 15 +-- main.c | 2 nickle.h | 8 - sched.c | 202 ++++++++++++++++++++++-------------------- test/Makefile.am | 1 test/signal.5c | 20 ++++ value.h | 7 - 14 files changed, 412 insertions(+), 117 deletions(-) New commits: commit 116b96de8e915d8fd8c48d6bbc2334e4641c5e7f Author: Keith Packard Date: Sun Feb 3 15:31:45 2008 -0800 Add pid/gid/uid functions in Process namespace diff --git a/Makefile.am b/Makefile.am index fd9266c..cded6fa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -49,7 +49,8 @@ nickle_SOURCES = \ builtin-command.c builtin-debug.c builtin-environ.c \ builtin-file.c builtin-math.c builtin-namespaces.h \ builtin-semaphore.c builtin-sockets.c builtin-string.c \ - builtin-thread.c builtin-toplevel.c builtin.c builtin.h \ + builtin-thread.c builtin-toplevel.c builtin-process.c \ + builtin.c builtin.h \ builtin-foreign.c gram.y lex.l nickle_LDFLAGS=$(NICKLE_LDFLAGS) diff --git a/builtin-namespaces.h b/builtin-namespaces.h index 37d13e4..3356343 100644 --- a/builtin-namespaces.h +++ b/builtin-namespaces.h @@ -23,3 +23,4 @@ extern void import_Gcd_namespace(void); extern void import_Environ_namespace(void); extern void import_Socket_namespace(void); extern void import_Foreign_namespace(void); +extern void import_Process_namespace(void); diff --git a/builtin-process.c b/builtin-process.c new file mode 100644 index 0000000..0a935ab --- /dev/null +++ b/builtin-process.c @@ -0,0 +1,242 @@ +/* + * Copyright ?? 1988-2008 Keith Packard and Bart Massey. + * All Rights Reserved. See the file COPYING in this directory + * for licensing information. + */ + +/* + * builtin-process.c + * + * provide builtin functions for the Process namespace + */ + +#include +#include +#include +#include + +#include "builtin.h" + +NamespacePtr ProcessNamespace; + +static Value +do_Process_getuid (void) +{ + ENTER (); + RETURN (NewInt (getuid())); +} + +static Value +do_Process_geteuid (void) +{ + ENTER (); + RETURN (NewInt (geteuid())); +} + +static Value +do_Process_getgid (void) +{ + ENTER (); + RETURN (NewInt (getgid())); +} + +static Value +do_Process_getegid (void) +{ + ENTER (); + RETURN (NewInt (getegid())); +} + +static Value +do_Process_getgroups (void) +{ + ENTER (); + int n; + gid_t *list; + Value ret; + int i; + + n = getgroups (0, NULL); + list = AllocateTemp (n * sizeof (gid_t)); + getgroups (n, list); + ret = NewArray (False, False, typePrim[rep_integer], 1, &n); + for (i = 0; i < n; i++) + ArrayValueSet(&ret->array, i, NewInt (list[i])); + RETURN (ret); +} + +static Value +do_Process_getpid (void) +{ + ENTER (); + RETURN (NewInt (getpid())); +} + +static Value +error (Value value) +{ + int err = errno; + + RaiseStandardException (exception_system_error, + FileGetErrorMessage (err), + 2, NewInt (err), value); + return Void; +} + +static Value +do_Process_setuid (Value uid) +{ + ENTER (); + int u = IntPart (uid, "Invalid uid"); + if (aborting) + RETURN(Void); + + if (setuid (u) < 0) + RETURN (error (uid)); + + RETURN (Void); +} + +static Value +do_Process_seteuid (Value euid) +{ + ENTER (); + int u = IntPart (euid, "Invalid euid"); + if (aborting) + RETURN(Void); + + if (seteuid (u) < 0) + RETURN (error (euid)); + + RETURN (Void); +} + +static Value +do_Process_setgid (Value gid) +{ + ENTER (); + int u = IntPart (gid, "Invalid gid"); + if (aborting) + RETURN(Void); + + if (setgid (u) < 0) + RETURN (error (gid)); + + RETURN (Void); +} + +static Value +do_Process_setegid (Value egid) +{ + ENTER (); + int u = IntPart (egid, "Invalid egid"); + if (aborting) + RETURN(Void); + + if (setegid (u) < 0) + RETURN (error (egid)); + + RETURN (Void); +} + +static Value +do_Process_setgroups (Value groups) +{ + ENTER (); + int n; + int i; + gid_t *g; + + n = ArrayLimits (&groups->array)[0]; + g = AllocateTemp (n * sizeof (gid_t)); + for (i = 0; i < n; i++) { + g[i] = IntPart (ArrayValueGet (&groups->array, i), "Invalid gid"); + if (aborting) + RETURN(Void); + } + + if (setgroups (n, g) < 0) + RETURN (error (groups)); + + RETURN (Void); +} + +void +import_Process_namespace (void) +{ + ENTER (); + + static const struct fbuiltin_0 funcs_0[] = { + { do_Process_getuid, "getuid", "i", "", "\n" + " int getuid ()\n" + "\n" + " Return the current uid\n" }, + { do_Process_geteuid, "geteuid", "i", "", "\n" + " int geteuid ()\n" + "\n" + " Return the current effective uid\n" }, + { do_Process_getgid, "getgid", "i", "", "\n" + " int getgid ()\n" + "\n" + " Return the current gid\n" }, + { do_Process_getegid, "getegid", "i", "", "\n" + " int getegid ()\n" + "\n" + " Return the current effective gid\n" }, + { do_Process_getgroups, "getgroups", "Ai", "", "\n" + " int[*] getgroups ()\n" + "\n" + " Return the list of additional groups\n" }, + { do_Process_getpid, "getpid", "i", "", "\n" + " int getpid ()\n" + "\n" + " Return the current process id." }, + { 0 } + }; + static const struct fbuiltin_1 funcs_1[] = { + { do_Process_setuid, "setuid", "v", "i", "\n" + " void setuid (int uid)\n" + "\n" + " Set the current uid." }, + { do_Process_seteuid, "seteuid", "v", "i", "\n" + " void seteuid (int euid)\n" + "\n" + " Set the current euid." }, + { do_Process_setgid, "setgid", "v", "i", "\n" + " void setgid (int gid)\n" + "\n" + " Set the current gid." }, + { do_Process_setegid, "setegid", "v", "i", "\n" + " void setegid (int egid)\n" + "\n" + " Set the current egid." }, + { do_Process_setgroups, "setgroups", "v", "Ai", "\n" + " void setgroups (int[*] groups)\n" + "\n" + " Set the list of additional groups." }, + { 0 } + }; + + static const struct ebuiltin excepts[] = { + {"system_error", exception_system_error, "sEp", "\n" + " system_error (string message, error_type error, poly value)\n" + "\n" + " Raised when a system function fails.\n" + " 'message' is a printable error string.\n" + " 'error' is a symbolic error code.\n" + " 'value' is the value which failed.\n" }, + { 0, 0 }, + }; + const struct ebuiltin *e; + + ProcessNamespace = BuiltinNamespace (/*parent*/ 0, "Process")->namespace.namespace; + + for (e = excepts; e->name; e++) + BuiltinAddException (&ProcessNamespace, e->exception, e->name, e->args, e->doc); + + BuiltinFuncs0 (&ProcessNamespace, funcs_0); + BuiltinFuncs1 (&ProcessNamespace, funcs_1); + EXIT (); +} + + diff --git a/builtin.c b/builtin.c index 90bf617..40bc238 100644 --- a/builtin.c +++ b/builtin.c @@ -329,6 +329,7 @@ BuiltinInit (void) import_Environ_namespace(); import_Socket_namespace(); import_Foreign_namespace (); + import_Process_namespace (); /* Import builtin strings with predefined values */ BuiltinStrings (svars); diff --git a/nickle.h b/nickle.h index f2a53cf..294cf46 100644 --- a/nickle.h +++ b/nickle.h @@ -761,6 +761,7 @@ typedef enum _standardException { exception_io_error, /* string integer file */ exception_name_error, /* string integer string */ exception_signal, /* integer */ + exception_system_error, /* string integer poly */ _num_standard_exceptions } StandardException; commit 2ee7d1bc95bb2bbbbc5792ed11618a229133eb22 Author: Keith Packard Date: Sun Feb 3 14:36:06 2008 -0800 Signal exception arguments were not getting forwarded. Exception arguments must land in thread value register so that they can be passed to the exception handler (if any). Also, add a test for signaling. diff --git a/sched.c b/sched.c index 203a973..458ce80 100644 --- a/sched.c +++ b/sched.c @@ -29,6 +29,7 @@ _ThreadInsert (Value thread) prev = &stopped; break; case ThreadFinished: + default: return; } for (; (t = *prev); prev = &t->thread.next) @@ -51,6 +52,7 @@ _ThreadRemove (Value thread) prev = &stopped; break; case ThreadFinished: + default: return; } for (; *prev != thread; prev = &(*prev)->thread.next); @@ -1225,6 +1227,7 @@ SignalThread (Value thread, Value signal, Bool executing) InstPtr next; RaiseException (thread, except, args, &next); + thread->thread.continuation.value = args; thread->thread.continuation.pc = next; if (thread->thread.state == ThreadSuspended) { thread->thread.sleep = 0; diff --git a/test/Makefile.am b/test/Makefile.am index a40c63c..f3d78e4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -6,6 +6,7 @@ check_SCRIPTS=gcdtest.5c \ reftest.5c \ modtest.5c \ hashtest.5c \ + signal.5c \ round.5c TESTS_ENVIRONMENT=NICKLESTART=$(top_srcdir)/builtin.5c NICKLEPATH=$(top_srcdir) ../nickle diff --git a/test/signal.5c b/test/signal.5c new file mode 100644 index 0000000..d23f185 --- /dev/null +++ b/test/signal.5c @@ -0,0 +1,20 @@ +int done = 0; + +void child () { + try { + while (true) + sleep (100); + } catch Thread::signal (int i) { + done = i; + } +} + +void check () { + thread t = fork child(); + sleep (100); + Thread::send_signal (t, 12); + sleep (100); + assert (done == 12, "signal not delivered"); +} + +check (); commit 6adae78f5e70be0031718e0e7ebeff81996951e3 Author: Keith Packard Date: Sun Feb 3 07:22:28 2008 -0800 Add signal exceptions, treat SIGINT as raise signal(2) SIGINT used to interrupt the current thread and enter the debugger with the thread 'interrupted'; in this state, the thread could be continued. However, this means that threads cannot catch SIGINT and cleanup at process exit. This commit changes all this around so that SIGINT simply raises a signal exception in all threads. This patch also keeps the interpreter running until all threads have exited. diff --git a/alarm.c b/alarm.c index 243be18..dad7f44 100644 --- a/alarm.c +++ b/alarm.c @@ -120,7 +120,8 @@ static Bool _sleepDone (void *closure) { Value thread = closure; - ThreadClearState (thread, ThreadSuspended); + if (thread->thread.state == ThreadSuspended) + ThreadSetState (thread, ThreadRunning); return False; } diff --git a/builtin-thread.c b/builtin-thread.c index 5b16b4a..45acc5b 100644 --- a/builtin-thread.c +++ b/builtin-thread.c @@ -24,10 +24,6 @@ import_Thread_namespace() { ENTER (); static const struct fbuiltin_0 funcs_0[] = { - { do_Thread_cont, "cont", "i", "", "\n" - " int cont ()\n" - "\n" - " Restarts any interrupted threads.\n" }, { do_Thread_current, "current", "t", "", "\n" " thread current ()\n" "\n" diff --git a/execute.c b/execute.c index 7724469..fde8ef0 100644 --- a/execute.c +++ b/execute.c @@ -963,9 +963,7 @@ ThreadsRun (Value thread, Value lex) if (signalInterrupt) { signalInterrupt = False; - if (thread) - ThreadSetState (thread, ThreadInterrupted); - (void) write (2, "\n", 8); + ThreadsSignal (NewInt (SIGINT)); } if (signalTimer) { @@ -985,10 +983,15 @@ ThreadsRun (Value thread, Value lex) if (lex && !(lex->file.flags & (FileInputBlocked|FileOutputBlocked))) break; } - else if (thread && !Runnable (thread)) + else if (thread && thread->thread.state == ThreadFinished) break; else if (!running) + { + /* when all threads are done, and all input is read, time to go */ + if (!lex && !stopped) + break; ThreadsBlock (); + } else { ENTER (); @@ -1442,7 +1445,8 @@ ThreadsRun (Value thread, Value lex) if (signalSuspend) { signalSuspend = False; - ThreadSetState (thread, ThreadSuspended); + if (thread->thread.state == ThreadRunning) + ThreadSetState (thread, ThreadSuspended); } if (signalFinished) { @@ -1458,7 +1462,6 @@ ThreadsRun (Value thread, Value lex) if (signalError) { signalError = False; - ThreadFinish (thread, True); } if (signalProfile) signalProfile = False; diff --git a/main.c b/main.c index 491b390..2456c13 100644 --- a/main.c +++ b/main.c @@ -107,6 +107,8 @@ main (int argc, char **argv) exit(1); } (void) yyparse (); + /* Wait for any running threads to execute */ + ThreadsRun (0, 0); IoFini (); FileFini (); return lastThreadError; diff --git a/nickle.h b/nickle.h index 9eaef9c..f2a53cf 100644 --- a/nickle.h +++ b/nickle.h @@ -598,10 +598,10 @@ void ThreadStepped (Value thread); void ThreadsWakeup (Value sleep, WakeKind wake); void ThreadsRun (Value thread, Value lex); void ThreadsInterrupt (void); +void ThreadsSignal (Value signal); void ThreadsContinue (void); void ThreadFinish (Value thread, Bool error); void ThreadSetState (Value thread, ThreadState state); -void ThreadClearState (Value thread, ThreadState state); void ThreadInit (void); void TraceFunction (Value file, FramePtr frame, CodePtr code, ExprPtr name); void TraceFrame (Value file, FramePtr frame, ObjPtr obj, InstPtr pc, int depth); @@ -631,12 +631,9 @@ InstPtr JumpStart (Value thread, ContinuationPtr continuation, Value ret); JumpPtr NewJump (TwixtPtr leave, TwixtPtr enter, TwixtPtr parent, ContinuationPtr continuation, Value ret); -#define Runnable(t) (((t)->thread.state & ~(ThreadSuspended)) == 0) - extern Value running; /* current thread */ extern Value stopped; /* stopped threads */ extern Bool complete; /* must complete current inst */ -extern int runnable; /* number of non-broken threads */ extern Bool profiling; /* profiling is active */ void InstDump (InstPtr inst, int indent, int i, int *branch, int maxbranch); diff --git a/sched.c b/sched.c index c34bd65..203a973 100644 --- a/sched.c +++ b/sched.c @@ -12,7 +12,6 @@ Value running; Value stopped; -int runnable; Bool signalException; extern void dumpSleep (void), dumpThreads (void); @@ -22,14 +21,16 @@ _ThreadInsert (Value thread) { Value *prev, t; - if (Runnable (thread)) - ++runnable; - if (thread->thread.state == ThreadRunning) + switch (thread->thread.state) { + case ThreadRunning: prev = &running; - else if (thread->thread.state & ThreadFinished) - return; - else + break; + case ThreadSuspended: prev = &stopped; + break; + case ThreadFinished: + return; + } for (; (t = *prev); prev = &t->thread.next) if (t->thread.priority < thread->thread.priority) break; @@ -42,14 +43,16 @@ _ThreadRemove (Value thread) { Value *prev; - if (Runnable (thread)) - --runnable; - if (thread->thread.state == ThreadRunning) + switch (thread->thread.state) { + case ThreadRunning: prev = &running; - else if (thread->thread.state & ThreadFinished) - return; - else + break; + case ThreadSuspended: prev = &stopped; + break; + case ThreadFinished: + return; + } for (; *prev != thread; prev = &(*prev)->thread.next); *prev = thread->thread.next; } @@ -57,17 +60,12 @@ _ThreadRemove (Value thread) void ThreadSetState (Value thread, ThreadState state) { - _ThreadRemove (thread); - thread->thread.state |= state; - _ThreadInsert (thread); -} - -void -ThreadClearState (Value thread, ThreadState state) -{ - _ThreadRemove (thread); - thread->thread.state &= ~state; - _ThreadInsert (thread); + if (state != thread->thread.state) + { + _ThreadRemove (thread); + thread->thread.state = state; + _ThreadInsert (thread); + } } void @@ -99,11 +97,11 @@ ThreadsWakeup (Value sleep, WakeKind kind) for (thread = stopped; thread; thread = next) { next = thread->thread.next; - if ((thread->thread.state & ThreadSuspended) && + if ((thread->thread.state == ThreadSuspended) && thread->thread.sleep == sleep) { thread->thread.sleep = 0; - ThreadClearState (thread, ThreadSuspended); + ThreadSetState (thread, ThreadRunning); if (kind == WakeOne) break; } @@ -115,54 +113,13 @@ Bool lastThreadError; void ThreadFinish (Value thread, Bool error) { - ThreadSetState (thread, ThreadFinished); - ThreadsWakeup (thread, WakeAll); - lastThreadError = error; -} - -void -ThreadsInterrupt (void) -{ - Value thread, next; - Value t; - - if (running) - t = running; - else - t = stopped; - for (thread = stopped; thread; thread = next) + if (thread->thread.state != ThreadFinished) { - next = thread->thread.next; - if (Runnable (thread)) - --runnable; - thread->thread.state |= ThreadInterrupted; - } - for (thread = running; thread; thread = next) - { - next = thread->thread.next; - ThreadSetState (thread, ThreadInterrupted); + ThreadSetState (thread, ThreadFinished); + ThreadsWakeup (thread, WakeAll); + lastThreadError = error; } } - -static int -ThreadContinue (void) -{ - Value thread, next; - int n; - - n = 0; - for (thread = stopped; thread; thread = next) - { - next = thread->thread.next; - - if (thread->thread.state & ThreadInterrupted) - { - n++; - ThreadClearState (thread, ThreadInterrupted); - } - } - return n; -} Value do_Thread_join (Value target) @@ -175,7 +132,7 @@ do_Thread_join (Value target) 2, target, Void); RETURN (Void); } - if ((target->thread.state & ThreadFinished) == 0) + if (target->thread.state != ThreadFinished) { ThreadSleep (running, target, PrioritySync); RETURN (Void); @@ -186,18 +143,16 @@ do_Thread_join (Value target) static void ThreadListState (Value thread) { - int state = thread->thread.state; - - if (state == ThreadRunning) + switch (thread->thread.state) { + case ThreadRunning: FilePuts (FileStdout, " running"); - else - { - if (state & ThreadSuspended) - FilePuts (FileStdout, " suspended"); - if (state & ThreadInterrupted) - FilePuts (FileStdout, " interrupted"); - if (state & ThreadFinished) - FilePuts (FileStdout, " finished"); + break; + case ThreadSuspended: + FilePuts (FileStdout, " suspended"); + break; + case ThreadFinished: + FilePuts (FileStdout, " finished"); + break; } } @@ -293,15 +248,6 @@ do_Thread_get_priority (Value thread) RETURN (NewInt (thread->thread.priority)); } -Value -do_Thread_cont (void) -{ - int n; - - n = ThreadContinue (); - return NewInt (n); -} - static int KillThread (Value thread) { @@ -314,7 +260,7 @@ KillThread (Value thread) 2, thread, Void); return 0; } - if (thread->thread.state & ThreadFinished) + if (thread->thread.state == ThreadFinished) ret = 0; else ret = 1; @@ -894,8 +840,11 @@ JumpUnhandledException (Value thread) { Value continuation = STACK_POP (thread->thread.continuation.stack); + /* make exec loop reschedule */ + if (thread == running) + SetSignalError (); DebugStart (continuation); - SetSignalError (); + ThreadFinish (thread, True); } /* @@ -1257,7 +1206,7 @@ JumpStandardException (Value thread, InstPtr *next) } static void -SignalThread (Value thread, Value signal) +SignalThread (Value thread, Value signal, Bool executing) { ENTER (); int i = 1; @@ -1265,7 +1214,7 @@ SignalThread (Value thread, Value signal) SymbolPtr except = standardExceptions[exception_signal]; ArrayValueSet (&args->array, 0, signal); - if (thread == running) + if (thread == running && executing) { standardException = exception_signal; standardExceptionArgs = args; @@ -1277,18 +1226,38 @@ SignalThread (Value thread, Value signal) RaiseException (thread, except, args, &next); thread->thread.continuation.pc = next; - if (thread->thread.state & ThreadSuspended) { + if (thread->thread.state == ThreadSuspended) { thread->thread.sleep = 0; - ThreadClearState (thread, ThreadSuspended); + ThreadSetState (thread, ThreadRunning); } } EXIT (); } +void +ThreadsSignal (Value signal) +{ + ENTER (); + Value thread, next; + + /* do running first -- signalling makes threads run */ + for (thread = running; thread; thread = next) + { + next = thread->thread.next; + SignalThread (thread, signal, False); + } + for (thread = stopped; thread; thread = next) + { + next = thread->thread.next; + SignalThread (thread, signal, False); + } + EXIT (); +} + Value do_Thread_signal (Value thread, Value signal) { ENTER (); - SignalThread (thread, signal); + SignalThread (thread, signal, True); RETURN (Void); } diff --git a/value.h b/value.h index fbebfe8..e31e1ae 100644 --- a/value.h +++ b/value.h @@ -743,10 +743,9 @@ typedef struct _continuation { } Continuation; typedef enum _ThreadState { - ThreadRunning = 0, - ThreadSuspended = 1, - ThreadInterrupted = 2, - ThreadFinished = 4 + ThreadRunning, + ThreadSuspended, + ThreadFinished } ThreadState; typedef struct _thread { commit 689ea14db8ca29f3338a03e71be5865089972488 Author: Keith Packard Date: Sat Feb 2 22:07:54 2008 +1100 Add 'signal' exception and 'send_signal' function. The 'send_signal' function raises a 'signal' exception in another thread, causing it to abort processing immediately. diff --git a/builtin-thread.c b/builtin-thread.c index 01d5040..5b16b4a 100644 --- a/builtin-thread.c +++ b/builtin-thread.c @@ -61,6 +61,11 @@ import_Thread_namespace() "\n" " Set 't's scheduling priority to 'priority'.\n" " Return 'priority.\n" }, + { do_Thread_signal, "send_signal", "v", "ti", "\n" + " void signal (thread t, int signal)\n" + "\n" + " Raise the signal exception in thread 't'\n" + " passing 'signal' as the argument\n" }, { 0 } }; @@ -75,11 +80,20 @@ import_Thread_namespace() { 0 } }; + static const struct ebuiltin excepts[] = { + {"signal", exception_signal, "i", "\n" + " signal (int signal)\n" + "\n" + " Sent from the Thread::send_signal function.\n" }, + { 0 }, + }; + ThreadNamespace = BuiltinNamespace (/*parent*/ 0, "Thread")->namespace.namespace; BuiltinFuncs0 (&ThreadNamespace, funcs_0); BuiltinFuncs1 (&ThreadNamespace, funcs_1); BuiltinFuncs2 (&ThreadNamespace, funcs_2); BuiltinFuncsV (&ThreadNamespace, funcs_v); + BuiltinExceptions (&ThreadNamespace, excepts); EXIT (); } diff --git a/builtin.h b/builtin.h index 09ed4ee..f90a893 100644 --- a/builtin.h +++ b/builtin.h @@ -108,6 +108,12 @@ BuiltinAddFunction (NamespacePtr *namespacep, char *name, char *ret_format, char *doc; \ } +#define BuiltinExceptions(ns, e) do {\ + const struct ebuiltin *ei; \ + for (ei = (e); ei->name; ei++) { \ + BuiltinAddException ((ns), ei->exception, ei->name, ei->args, ei->doc); \ + } } while (0) + #define BuiltinFuncsGeneric(ns, funcs, stype, bitem, jump) do { \ BuiltinFunc f; const struct stype *fi; \ for (fi = (funcs); fi->name; fi++) { \ diff --git a/nickle.h b/nickle.h index c5ad821..9eaef9c 100644 --- a/nickle.h +++ b/nickle.h @@ -763,6 +763,7 @@ typedef enum _standardException { exception_open_error, /* string integer string */ exception_io_error, /* string integer file */ exception_name_error, /* string integer string */ + exception_signal, /* integer */ _num_standard_exceptions } StandardException; @@ -932,6 +933,7 @@ Value do_profile (Value); /* two argument builtins */ Value do_Thread_set_priority (Value, Value); +Value do_Thread_signal (Value, Value); Value do_File_open (Value, Value); Value do_gcd (Value, Value); Value do_xor (Value, Value); diff --git a/sched.c b/sched.c index 573dce3..c34bd65 100644 --- a/sched.c +++ b/sched.c @@ -1233,9 +1233,8 @@ RaiseStandardException (StandardException se, i = argc + 1; args = NewArray (False, False, typePoly, 1, &i); ArrayValueSet (&args->array, 0, NewStrString (string)); - if (argc) - for (i = 0; i < argc; i++) - ArrayValueSet (&args->array, i + 1, va_arg (va, Value)); + for (i = 0; i < argc; i++) + ArrayValueSet (&args->array, i + 1, va_arg (va, Value)); standardException = se; standardExceptionArgs = args; SetSignalException (); @@ -1256,3 +1255,40 @@ JumpStandardException (Value thread, InstPtr *next) standardExceptionArgs = 0; RETURN (args); } + +static void +SignalThread (Value thread, Value signal) +{ + ENTER (); + int i = 1; + Value args = NewArray (False, False, typePoly, 1, &i); + SymbolPtr except = standardExceptions[exception_signal]; + + ArrayValueSet (&args->array, 0, signal); + if (thread == running) + { + standardException = exception_signal; + standardExceptionArgs = args; + SetSignalException (); + } + else if (except) + { + InstPtr next; + + RaiseException (thread, except, args, &next); + thread->thread.continuation.pc = next; + if (thread->thread.state & ThreadSuspended) { + thread->thread.sleep = 0; + ThreadClearState (thread, ThreadSuspended); + } + } + EXIT (); +} + +Value +do_Thread_signal (Value thread, Value signal) +{ + ENTER (); + SignalThread (thread, signal); + RETURN (Void); +} From keithp at keithp.com Tue Feb 5 20:50:47 2008 From: keithp at keithp.com (Keith Packard) Date: Tue, 05 Feb 2008 20:50:47 -0800 Subject: [Nickle] SIGINT and thread termination rules Message-ID: <1202273447.7800.89.camel@koto.keithp.com> Carl Worth and I were hacking up some Unix domain socket support in Nickle as a part of a project to implement Telepathy support on top of DBus. Carl discovered that when an application created a Unix domain socket and bound it to a filesystem name, the related inode would persist past the life of the application. He wanted to clean up on exit so that the next run of the application would be able to use the same name. Unfortunately, nickle would often exit unceremoniously, either as a result of SIGINT from ^C or at the end of the parser input. I made two changes and thought they deserved a bit of discussion before I released them. The simpler change was to make the runtime system persist until all threads were terminated, instead of exiting when the parser input was exhausted. That seems fairly uncontroversial to me: diff --git a/main.c b/main.c index 491b390..2456c13 100644 --- a/main.c +++ b/main.c @@ -107,6 +107,8 @@ main (int argc, char **argv) exit(1); } (void) yyparse (); + /* Wait for any running threads to execute */ + ThreadsRun (0, 0); IoFini (); FileFini (); return lastThreadError; The second, and far more pervasive, change was to convert SIGINT into an exception delivered to every thread. This eliminates the ability for SIGINT to suspend thread execution so that they can be inspected by a debugger and restarted. Instead, each thread receives a 'signal' exception and is free to catch it and deal with it however they like. Exceptions which are not caught terminate the thread with an error message: > while (true); (press ^C here)... Unhandled exception signal (2) :1: while (true) > This certainly seems more useful for application development as applications can expect that twixt blocks will *always* get executed, even if the application receives a SIGINT or the parser sees EOF. However, if 'exit' is called, the threads do not get a chance to clean up -- the process exits immediately taking all threads down with it. -- keith.packard at intel.com -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part Url : /pipermail/nickle/attachments/20080205/5294b1fb/attachment.pgp From bart at po8.org Wed Feb 6 00:47:24 2008 From: bart at po8.org (Bart Massey) Date: Wed, 06 Feb 2008 00:47:24 -0800 Subject: [Nickle] SIGINT and thread termination rules In-Reply-To: <1202273447.7800.89.camel@koto.keithp.com> References: <1202273447.7800.89.camel@koto.keithp.com> Message-ID: I'm confused. How do I get into the debugger with this change? Bart In message <1202273447.7800.89.camel at koto.keithp.com> you wrote: > The second, and far more pervasive, change was to convert SIGINT into an > exception delivered to every thread. This eliminates the ability for > SIGINT to suspend thread execution so that they can be inspected by a > debugger and restarted. Instead, each thread receives a 'signal' > exception and is free to catch it and deal with it however they like. > Exceptions which are not caught terminate the thread with an error > message: > > > while (true); > (press ^C here)... > Unhandled exception signal (2) > :1: while (true) > >=20 > > This certainly seems more useful for application development as > applications can expect that twixt blocks will *always* get executed, > even if the application receives a SIGINT or the parser sees EOF. > > However, if 'exit' is called, the threads do not get a chance to clean > up -- the process exits immediately taking all threads down with it. From cworth at cworth.org Wed Feb 6 08:58:53 2008 From: cworth at cworth.org (Carl Worth) Date: Wed, 06 Feb 2008 08:58:53 -0800 Subject: [Nickle] Artificial distinction between import and autoimport In-Reply-To: References: <873ascupxg.wl%cworth@cworth.org> <1201943342.19008.47.camel@koto.keithp.com> Message-ID: <87k5linhrm.wl%cworth@cworth.org> On Sat, 02 Feb 2008 23:56:13 -0800, Bart Massey wrote: > But I'm not getting what you > propose to have your revised import or load do if their > namespace target already exists. Should they replace the > namespace, or stop? Both behaviors have sensible use cases > I described previously, so I'm not sure how to make it "just > work". Perhaps you want load and import to behave > differently at the command prompt vs in a loaded file? I think I'd recommend dumping the "auto" prefix universally and recommend that programs use "import" and make that do the right thing there. As for the case of the command prompt where someone wants to reimport something that's being hacked on currently---that's the special case so it should get the more specialized name, and "reimport" seems to fit quite well. If one wanted to do magic where "import" would actually mean "reimport" at the prompt, then that would be possible, (but it sounds a bit too DWIM for my tastes). -Carl -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080206/5ba22580/attachment-0001.pgp From cworth at cworth.org Wed Feb 6 09:05:02 2008 From: cworth at cworth.org (Carl Worth) Date: Wed, 06 Feb 2008 09:05:02 -0800 Subject: [Nickle] [RFC] Socket::create: Add support for Unix domain sockets In-Reply-To: References: <87zluktavc.wl%cworth@cworth.org> Message-ID: <87ir12nhhd.wl%cworth@cworth.org> On Fri, 01 Feb 2008 19:22:25 -0800, Bart Massey wrote: > Yeah, just make this patch be for _create and do all the > varargs magic in Nickle. Thanks for the patch! So here's my extended version. It adds some varargs support so that the address family can be omitted, (defaulting to AF_INET), which is much better than my previous patch since this won't break existing code. It also doesn't move to an enum for the stream type to avoid breaking existing code. It also then goes on to provide a varargs bind and connect so that when using AF_UNIX one does not have to pass a useless hostname argument. This is a complete series suitable for applying as is, (it replaces my previous patch). I still didn't get as far as adding a test case yet. -Carl -------------- next part -------------- A non-text attachment was scrubbed... Name: socket.patchset Type: application/octet-stream Size: 0 bytes Desc: not available Url : /pipermail/nickle/attachments/20080206/119f9bfd/attachment-0001.obj From cworth at cworth.org Fri Feb 1 22:19:15 2008 From: cworth at cworth.org (Carl Worth) Date: Sat, 2 Feb 2008 17:19:15 +1100 Subject: [PATCH] Socket::create: Make the family argument optional Message-ID: --- builtin-sockets.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 files changed, 34 insertions(+), 15 deletions(-) diff --git a/builtin-sockets.c b/builtin-sockets.c index 47b6ab1..8ff1879 100644 --- a/builtin-sockets.c +++ b/builtin-sockets.c @@ -34,7 +34,7 @@ NamespacePtr SocketNamespace; Type *typeSockaddr; -Value do_Socket_create (Value family, Value type); +Value do_Socket_create (int num, Value *args); Value do_Socket_connect (Value s, Value host, Value port); Value do_Socket_bind (Value s, Value host, Value port); Value do_Socket_listen (Value s, Value backlog); @@ -69,16 +69,6 @@ import_Socket_namespace() }; static const struct fbuiltin_2 funcs_2[] = { - { do_Socket_create, "create", "f", "ii", "\n" - " file create (int type)\n" - "\n" - " Create a socket where 'family' is one of:\n" - " AF_UNIX: Local communication.\n" - " AF_INET: IPv4 Internet protocols.\n" - " AF_INET6: IPv6 Internet protocols.\n" - " and where 'type' is one of:\n" - " SOCK_STREAM: a stream socket.\n" - " SOCK_DGRAM: a datagram socket.\n" }, { do_Socket_listen, "listen", "v", "fi", "\n" " void listen (file socket, int length)\n" "\n" @@ -105,6 +95,20 @@ import_Socket_namespace() { 0 } }; + static const struct fbuiltin_v funcs_v[] = { + { do_Socket_create, "create", "f", ".i", "\n" + " file create ([int family], int type)\n" + "\n" + " Create a socket where the optional 'family' is one of:\n" + " AF_UNIX: Local communication.\n" + " AF_INET (default): IPv4 Internet protocols.\n" + " AF_INET6: IPv6 Internet protocols.\n" + " and where 'type' is one of:\n" + " SOCK_STREAM: a stream socket.\n" + " SOCK_DGRAM: a datagram socket.\n" }, + { 0 } + }; + static const struct ibuiltin ivars[] = { { AF_UNIX, "AF_UNIX", &SocketNamespace }, { AF_INET, "AF_INET", &SocketNamespace }, @@ -145,6 +149,7 @@ import_Socket_namespace() BuiltinFuncs1 (&SocketNamespace, funcs_1); BuiltinFuncs2 (&SocketNamespace, funcs_2); BuiltinFuncs3 (&SocketNamespace, funcs_3); + BuiltinFuncsV (&SocketNamespace, funcs_v); BuiltinIntegers (ivars); BuiltinStrings (svars); @@ -154,12 +159,26 @@ import_Socket_namespace() /* File::file do_Socket_create ({SOCK_STREAM,SOCK_DGRAM} type); */ Value -do_Socket_create (Value family, Value type) +do_Socket_create (int num, Value *args) { ENTER (); - int ifamily, itype, s; - ifamily = IntPart (family, "Illegal address family"); - itype = IntPart (type, "Illegal socket type"); + int ifamily, itype, type_index, s; + + if (num == 0 || num > 2) { + RaiseStandardException (exception_invalid_argument, + "create must have one or two arguments", + 2, NewInt (0), NewInt (num)); + RETURN (Void); + } + + if (num > 1) { + ifamily = IntPart (args[0], "Illegal address family"); + type_index = 1; + } else { + ifamily = AF_INET; + type_index = 0; + } + itype = IntPart (args[type_index], "Illegal socket type"); if (aborting) RETURN (Void); s = socket (ifamily, itype, 0); -- 1.5.3.2 From cworth at cworth.org Sat Feb 2 02:31:26 2008 From: cworth at cworth.org (Carl Worth) Date: Sat, 2 Feb 2008 21:31:26 +1100 Subject: [PATCH] Socket: Store address family in file structure Message-ID: This is more straightforward than using getsockname to read the address family back out from the kernel. --- builtin-sockets.c | 19 +++++++++---------- file.c | 1 + test/README | 2 +- value.h | 1 + 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/builtin-sockets.c b/builtin-sockets.c index 8ff1879..3ad61e4 100644 --- a/builtin-sockets.c +++ b/builtin-sockets.c @@ -163,6 +163,7 @@ do_Socket_create (int num, Value *args) { ENTER (); int ifamily, itype, type_index, s; + Value ret; if (num == 0 || num > 2) { RaiseStandardException (exception_invalid_argument, @@ -184,7 +185,9 @@ do_Socket_create (int num, Value *args) s = socket (ifamily, itype, 0); if (s == -1) RETURN (Void); - RETURN (FileCreate (s, FileReadable|FileWritable)); + ret = FileCreate (s, FileReadable|FileWritable); + ret->file.sock_family = ifamily; + RETURN (ret); } typedef union { @@ -197,7 +200,7 @@ typedef union { } align; } sockaddr_all_t; -static Bool address_lookup (int s, Value hostname, Value portname, +static Bool address_lookup (Value s, Value hostname, Value portname, sockaddr_all_t *addr, socklen_t *len) { struct hostent *host; @@ -217,12 +220,7 @@ static Bool address_lookup (int s, Value hostname, Value portname, if (*hostchars == '\0' || *portchars == '\0') return False; /* FIXME: more here? */ - /* Read the address family back from the kernel. */ - *len = sizeof (*addr); - if (getsockname (s, &addr->addr, len) < 0) - return False; - - switch (addr->addr.sa_family) { + switch (s->file.sock_family) { case AF_UNIX: if (strlen (portchars) > PATH_MAX) return False; @@ -237,6 +235,7 @@ static Bool address_lookup (int s, Value hostname, Value portname, herror ("address_lookup"); return False; /* FIXME: more here? */ } + *len = sizeof (addr->in); memcpy (&addr->in.sin_addr.s_addr, host->h_addr_list[0], sizeof addr->in.sin_addr.s_addr); /* port lookup */ @@ -275,7 +274,7 @@ do_Socket_connect (Value s, Value host, Value port) sockaddr_all_t addr; socklen_t len; - if (!address_lookup (s->file.fd, host, port, &addr, &len)) + if (!address_lookup (s, host, port, &addr, &len)) RETURN (Void); if (!running->thread.partial) @@ -330,7 +329,7 @@ do_Socket_bind (Value s, Value host, Value port) sockaddr_all_t addr; socklen_t len; - if (!address_lookup (s->file.fd, host, port, &addr, &len)) + if (!address_lookup (s, host, port, &addr, &len)) RETURN (Void); #ifdef SO_REUSEADDR diff --git a/file.c b/file.c index 0628cfa..64b7c41 100644 --- a/file.c +++ b/file.c @@ -653,6 +653,7 @@ FileCreate (int fd, int flags) file->file.flags |= FileIsPipe; if (fd >= 3) FileSetFd (fd); + file->file.sock_family = 0; RETURN (file); } diff --git a/test/README b/test/README index 214487d..44644c9 100644 --- a/test/README +++ b/test/README @@ -1,4 +1,4 @@ These files are used to make sure that everything in Nickle is working as it should be, or at least how we hope it should work. Each file tests specific items within Nickle such as order of operations. optest.5c - tests the operators to make sure they are calculating properly -orderofoptest.5c - tests to make sure that Nickle evaluates in the correct order +orderofoptest.5c - tests to make sure that Nickle evaluates in the correct order \ No newline at end of file diff --git a/value.h b/value.h index fbebfe8..c55d89e 100644 --- a/value.h +++ b/value.h @@ -645,6 +645,7 @@ typedef struct _file { int error; FileChainPtr input; FileChainPtr output; + int sock_family; } File; #define FileBufferSize 4096 -- 1.5.3.2 From cworth at cworth.org Sat Feb 2 04:36:50 2008 From: cworth at cworth.org (Carl Worth) Date: Sat, 2 Feb 2008 23:36:50 +1100 Subject: [PATCH] Socket: Provide varargs versions of bind and connect Message-ID: With this change, the hostname is now not-required, (and even not allowed), for an AF_UNIX socket. Also, for an AF_INET socket the port can now be an integer as well as a string service or port. --- builtin-sockets.c | 204 ++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 138 insertions(+), 66 deletions(-) diff --git a/builtin-sockets.c b/builtin-sockets.c index 3ad61e4..934d03a 100644 --- a/builtin-sockets.c +++ b/builtin-sockets.c @@ -35,8 +35,8 @@ NamespacePtr SocketNamespace; Type *typeSockaddr; Value do_Socket_create (int num, Value *args); -Value do_Socket_connect (Value s, Value host, Value port); -Value do_Socket_bind (Value s, Value host, Value port); +Value do_Socket_connect (int num, Value *args); +Value do_Socket_bind (int numb, Value *args); Value do_Socket_listen (Value s, Value backlog); Value do_Socket_accept (Value s); Value do_Socket_shutdown (Value s, Value how); @@ -83,18 +83,6 @@ import_Socket_namespace() { 0 } }; - static const struct fbuiltin_3 funcs_3[] = { - { do_Socket_connect, "connect", "v", "fss", "\n" - " void connect (file socket, string host, string port)\n" - "\n" - " Connect 'socket' to 'host', 'port'.\n" }, - { do_Socket_bind, "bind", "v", "fss", "\n" - " void bind (file socket, string host, string port)\n" - "\n" - " Bind 'socket' to 'host', 'port'.\n" }, - { 0 } - }; - static const struct fbuiltin_v funcs_v[] = { { do_Socket_create, "create", "f", ".i", "\n" " file create ([int family], int type)\n" @@ -106,6 +94,24 @@ import_Socket_namespace() " and where 'type' is one of:\n" " SOCK_STREAM: a stream socket.\n" " SOCK_DGRAM: a datagram socket.\n" }, + { do_Socket_bind, "bind", "v", "f.p", "\n" + " void bind (file socket, string host, string port)\n" + " void bind (file socket, string host, int port)\n" + "\n" + " Bind AF_INET 'socket' to 'host', 'port'.\n" + "\n" + " void bind (file socket, string local_socket)\n" + "\n" + " Bind AF_UNIX 'socket' to 'localhost'.\n" }, + { do_Socket_connect, "connect", "v", "f.p", "\n" + " void connect (file socket, string host, string port)\n" + " void connect (file socket, string host, int port)\n" + "\n" + " Connect AF_INET 'socket' to 'host', 'port'.\n" + "\n" + " void connect (file socket, string local_socket)\n" + "\n" + " Connect AF_UNIX 'socket' to 'local_socket'.\n" }, { 0 } }; @@ -148,7 +154,6 @@ import_Socket_namespace() BuiltinFuncs0 (&SocketNamespace, funcs_0); BuiltinFuncs1 (&SocketNamespace, funcs_1); BuiltinFuncs2 (&SocketNamespace, funcs_2); - BuiltinFuncs3 (&SocketNamespace, funcs_3); BuiltinFuncsV (&SocketNamespace, funcs_v); BuiltinIntegers (ivars); @@ -200,81 +205,144 @@ typedef union { } align; } sockaddr_all_t; -static Bool address_lookup (Value s, Value hostname, Value portname, - sockaddr_all_t *addr, socklen_t *len) +#define VerifyArgumentCount(arg, condition, error) \ +if (! (condition)) { \ + RaiseStandardException (exception_invalid_argument, \ + (error), 2, NewInt (0), NewInt (arg)); \ +} + +/* Supports the following args from both bind and connect: + * (File::file s, String local_socket) + */ +static Bool address_lookup_af_unix (int num, Value *args, + struct sockaddr_un *addr, socklen_t *len) { - struct hostent *host; - struct servent *port; + char *local_socket; + Value s = args[0]; + + VerifyArgumentCount (num, num == 2, + "must have 2 arguments for an AF_UNIX socket"); + if (aborting) + return False; + + local_socket = StrzPart (args[1], "invalid local_socket"); + if (!local_socket || *local_socket == '\0') + return False; + + if (strlen (local_socket) > PATH_MAX) + return False; + + addr->sun_family = s->file.sock_family; + strcpy (addr->sun_path, local_socket); + *len = SUN_LEN (addr); + + return True; +} + +/* Supports the following args from both bind and connect: + * (File::file s, String host, String port) + * (File::file s, String host, int port) + */ +static Bool address_lookup_af_inet (int num, Value *args, + struct sockaddr_in *addr, socklen_t *len) +{ + Value host, port; char *hostchars; - char *portchars; - char *endptr = 0; + struct hostent *hostent; long int portnum; + struct servent *portent; + char *endptr; + Value s = args[0]; - hostchars = StrzPart (hostname, "invalid hostname"); - if (!hostchars) + VerifyArgumentCount (num, num == 3, + "must have 3 arguments for an AF_INET socket"); + if (aborting) return False; - portchars = StrzPart (portname, "invalid portname"); - if (!portchars) + + host = args[1]; + port = args[2]; + + hostchars = StrzPart (host, "invalid hostname"); + if (!hostchars || *hostchars == '\0') return False; - - if (*hostchars == '\0' || *portchars == '\0') - return False; /* FIXME: more here? */ - switch (s->file.sock_family) { - case AF_UNIX: - if (strlen (portchars) > PATH_MAX) + if (ValueIsString (port)) { + char *portchars = StrzPart (port, "invalid port string"); + if (!portchars || *portchars == '\0') return False; - *len = SUN_LEN (&addr->un); - strcpy (addr->un.sun_path, portchars); - break; - case AF_INET: - /* host lookup */ - host = gethostbyname (hostchars); - if (host == 0) - { - herror ("address_lookup"); - return False; /* FIXME: more here? */ - } - *len = sizeof (addr->in); - memcpy (&addr->in.sin_addr.s_addr, host->h_addr_list[0], sizeof addr->in.sin_addr.s_addr); - - /* port lookup */ portnum = strtol (portchars, &endptr, /* base */ 10); - if (*endptr != '\0') /* non-numeric port specification */ { /* FIXME: this should not always be "tcp"! */ - port = getservbyname (portchars, "tcp"); - if (port == 0) + portent = getservbyname (portchars, "tcp"); + if (portent == 0) return False; /* FIXME: more here? */ - addr->in.sin_port = port->s_port; + addr->sin_port = portent->s_port; } - else - { - if (portnum <= 0 || portnum >= (1 << 16)) - return False; /* FIXME: more here? */ - addr->in.sin_port = htons (portnum); - } - break; -#if AF_INET6 + if (portnum <= 0 || portnum >= (1 << 16)) + return False; /* FIXME: more here? */ + } else { + portnum = IntPart (port, "invalid port value"); + if (portnum <= 0 || portnum >= (1 << 16)) + return False; /* FIXME: more here? */ + } + + addr->sin_family = s->file.sock_family; + addr->sin_port = htons (portnum); + + /* host lookup */ + hostent = gethostbyname (hostchars); + if (hostent == 0) + { + herror ("address_lookup"); + return False; /* FIXME: more here? */ + } + + *len = sizeof (*addr); + memcpy (&addr->sin_addr.s_addr, hostent->h_addr_list[0], + sizeof (addr->sin_addr.s_addr)); + + return True; +} + +/* Supports the following args from both bind and connect: + * + * (File::file s, String host, String port) + * (File::file s, String host, int port) + * (File::file s, String local_socket) + */ +static Bool address_lookup (int num, Value *args, + sockaddr_all_t *addr, socklen_t *len) +{ + Value s = args[0]; + + switch (s->file.sock_family) { + case AF_UNIX: + return address_lookup_af_unix (num, args, &addr->un, len); + case AF_INET: + return address_lookup_af_inet (num, args, &addr->in, len); +#ifdef AF_INET6 case AF_INET6: + /* FIXME */ #endif default: return False; } - - return True; } -/* void do_Socket_connect (File::file s, String host, String port); */ +/* void do_Socket_connect (File::file s, String host, String port); + * void do_Socket_connect (File::file s, String host, int port); + * void do_Socket_connect (File::file s, String local_socket; + */ Value -do_Socket_connect (Value s, Value host, Value port) +do_Socket_connect (int num, Value *args) { ENTER (); sockaddr_all_t addr; socklen_t len; + Value s = args[0]; - if (!address_lookup (s, host, port, &addr, &len)) + if (!address_lookup (num, args, &addr, &len)) RETURN (Void); if (!running->thread.partial) @@ -321,15 +389,19 @@ do_Socket_connect (Value s, Value host, Value port) RETURN (Void); } -/* void do_Socket_bind (File::file s, String host, String port); */ +/* void do_Socket_bind (File::file s, String host, String port); + * void do_Socket_bind (File::file s, String host, int port); + * void do_Socket_bind (File::file s, String local_socket; + */ Value -do_Socket_bind (Value s, Value host, Value port) +do_Socket_bind (int num, Value *args) { ENTER (); sockaddr_all_t addr; socklen_t len; + Value s = args[0]; - if (!address_lookup (s, host, port, &addr, &len)) + if (!address_lookup (num, args, &addr, &len)) RETURN (Void); #ifdef SO_REUSEADDR -- 1.5.3.2 --Multipart_Wed_Feb__6_09:05:02_2008-1-- --pgp-sign-Multipart_Wed_Feb__6_09:05:02_2008-1 Content-Type: application/pgp-signature Content-Transfer-Encoding: 7bit -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQBHqei+6JDdNq8qSWgRAsYEAJ0aw6SmEqP4ejvcKweRBGLVWO4XXQCfej9c W2cyEZ6JPreOh9UbNd+zFUg= =U3Pg -----END PGP SIGNATURE----- --pgp-sign-Multipart_Wed_Feb__6_09:05:02_2008-1-- From bart at po8.org Wed Feb 6 10:42:31 2008 From: bart at po8.org (Bart Massey) Date: Wed, 06 Feb 2008 10:42:31 -0800 Subject: [Nickle] Artificial distinction between import and autoimport In-Reply-To: <87k5linhrm.wl%cworth@cworth.org> References: <873ascupxg.wl%cworth@cworth.org> <1201943342.19008.47.camel@koto.keithp.com> <87k5linhrm.wl%cworth@cworth.org> Message-ID: In message <87k5linhrm.wl%cworth at cworth.org> you wrote: > On Sat, 02 Feb 2008 23:56:13 -0800, Bart Massey wrote: > > But I'm not getting what you > > propose to have your revised import or load do if their > > namespace target already exists. Should they replace the > > namespace, or stop? Both behaviors have sensible use cases > > I described previously, so I'm not sure how to make it "just > > work". Perhaps you want load and import to behave > > differently at the command prompt vs in a loaded file? > > I think I'd recommend dumping the "auto" prefix universally and > recommend that programs use "import" and make that do the right thing > there. "Do the right thing." Check. :-) Let me try one more time to explain what's going on currently, with some use cases, and see if I can make sense of the proposed new world: Current: load New: load As far as I know, nobody's proposing changing this at all. Current: autoload If namespace is not in scope, try to load a file whose filename is constructed from . New: load name Same as current semantics. I'm not sure whether it's important to anyone to get rid of "auto" here, so feedback would be welcome. Current: import Bring all the names that are part of namespace into scope, giving an error if is not a namespace currently in scope. Current: autoimport If name does not exist, try to autoload ; if the autoload fails, give an error. If namespace is now in scope, import ; otherwise give an error. New: import Becomes an alias for autoimport ? It appears to me that this change is a dangerous one. In particular, if I typo the name of a namespace that I just defined, or if I forget to load a file I meant to load, I might get an error on import, or I might get the contents of an appropriately named and namespaced file that happens to be lying around---this could also be a security hole, depending on the situation. What's the advantage of getting rid of "auto", again? Is it just too ugly to live with? I'll admit that it might not have been the most fortuitous choice, but do we really hate it enough to make it go away? > As for the case of the command prompt where someone wants to reimport > something that's being hacked on currently---that's the special case > so it should get the more specialized name, and "reimport" seems to > fit quite well. It would be quite convenient to have a single reimport command; I've added that. (I also fixed a minor bug in autoload / autoimport that no one ever noticed before.) > If one wanted to do magic where "import" would actually mean > "reimport" at the prompt, then that would be possible, (but it sounds > a bit too DWIM for my tastes). I agree---I've just called it "reimport". It's a bit of a misnomer, though, as you can use it to do the initial import as well; it doesn't really care whether the namespace is already in scope. Bart From bart at po8.org Wed Feb 6 11:16:02 2008 From: bart at po8.org (bart at po8.org) Date: Wed, 06 Feb 2008 11:16:02 -0800 Subject: [Nickle] Nickle annoyance fixed Message-ID: There's a buglet that's been bothering me for years. The print command, when passed a bogus name, would print all the public names. I've changed it so that all public names are printed when print is invoked with no arguments. When passed a faulty argument, print will complain and refuse. In the process, I cleaned up some old crufty code. Bart From keithp at keithp.com Wed Feb 6 12:10:45 2008 From: keithp at keithp.com (Keith Packard) Date: Wed, 06 Feb 2008 12:10:45 -0800 Subject: [Nickle] SIGINT and thread termination rules In-Reply-To: References: <1202273447.7800.89.camel@koto.keithp.com> Message-ID: <1202328645.7800.107.camel@koto.keithp.com> On Wed, 2008-02-06 at 00:47 -0800, Bart Massey wrote: > I'm confused. How do I get into the debugger with this > change? Raising an un-caught exception will still enter the debugger, and you'll be sitting at a continuation where the exception was raised, but all of the unwinding operations will have been executed, and you won't be able to restart the program from that point. -- keith.packard at intel.com -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part Url : /pipermail/nickle/attachments/20080206/110c5fd2/attachment.pgp From bart at po8.org Wed Feb 6 13:01:51 2008 From: bart at po8.org (Bart Massey) Date: Wed, 06 Feb 2008 13:01:51 -0800 Subject: [Nickle] Artificial distinction between import and autoimport In-Reply-To: <87bq6top6y.wl%cworth@cworth.org> References: <873ascupxg.wl%cworth@cworth.org> <1201943342.19008.47.camel@koto.keithp.com> <87k5linhrm.wl%cworth@cworth.org> <87bq6top6y.wl%cworth@cworth.org> Message-ID: In message <87bq6top6y.wl%cworth at cworth.org> you wrote: > > New: import > > Becomes an alias for autoimport ? > > > It appears to me that this change is a dangerous one. > > I don't follow what about the change introduces danger. There are no > new semantics being introduced, but simply new names for existing > semantics. > > Or are you just saying that the semantics of the current "autoimport" > are dangerous and that people would be wise not to use it? If so, then > whatever the imagined security problem is there, it should be fixed. Right now, you can use "import" when you think the namespace whose contents you're trying to bring into scope is itself already in scope. You'll get an error if you were wrong. For example, I sometimes write code like namespace Weird { public void f() {} } import Wierd If "import" were replaced by "autoimport", this would have the surprising behavior that my code would look for a file called "wierd.5c" and load its contents. In any situation I can think of, this would be caught the first time the program was tested, which is all "import" would do anyway. It still makes me quite nervous. > For example, does nickle search the current working directory by > default for things to autoload? I could definitely see that being a > problem, (as opposed to searching the directory in which the script > lives, or the system directory, or directories explicitly listed on > some search path). Nickle searches only its compiled-in library directory, with two exceptions. If the environment variable "NICKLEPATH" or the Nickle variable "nickle_path" has been set it searches that path. If it is lexing standard input, it adds "." to the current path and also autoloads any ".nicklerc" it finds. A mess, admittedly, but seems to work well in practice. Bart From bart at po8.org Wed Feb 6 13:05:58 2008 From: bart at po8.org (Bart Massey) Date: Wed, 06 Feb 2008 13:05:58 -0800 Subject: [Nickle] SIGINT and thread termination rules In-Reply-To: <1202328645.7800.107.camel@koto.keithp.com> References: <1202273447.7800.89.camel@koto.keithp.com> <1202328645.7800.107.camel@koto.keithp.com> Message-ID: In message <1202328645.7800.107.camel at koto.keithp.com> you wrote: > On Wed, 2008-02-06 at 00:47 -0800, Bart Massey wrote: > > I'm confused. How do I get into the debugger with this > > change? > > Raising an un-caught exception will still enter the debugger, and you'll > be sitting at a continuation where the exception was raised, but all of > the unwinding operations will have been executed, and you won't be able > to restart the program from that point. Oh. That's kind of sad, but I can live with it, I guess---I hardly ever actually do that. I still don't get what goes wrong if we leave the unwinding undone until the debugger is exited or an exception is handled. It would require a little machinery, I guess, to run unwindings for a handled exception in the proper context... Bart From bart at po8.org Wed Feb 6 13:08:40 2008 From: bart at po8.org (Bart Massey) Date: Wed, 06 Feb 2008 13:08:40 -0800 Subject: [Nickle] More about "autoimport" Message-ID: Forgot to Cc the Nickle list with this. -------------- next part -------------- An embedded message was scrubbed... From: Bart Massey Subject: Re: [Nickle] Artificial distinction between import and autoimport Date: Wed, 06 Feb 2008 12:46:01 -0800 Size: 5431 Url: /pipermail/nickle/attachments/20080206/e0e54491/attachment.eml From keithp at keithp.com Wed Feb 6 13:27:26 2008 From: keithp at keithp.com (Keith Packard) Date: Wed, 06 Feb 2008 13:27:26 -0800 Subject: [Nickle] SIGINT and thread termination rules In-Reply-To: References: <1202273447.7800.89.camel@koto.keithp.com> <1202328645.7800.107.camel@koto.keithp.com> Message-ID: <1202333246.7800.117.camel@koto.keithp.com> On Wed, 2008-02-06 at 13:05 -0800, Bart Massey wrote: > Oh. That's kind of sad, but I can live with it, I guess---I > hardly ever actually do that. Ok, that's what I figured. I don't actually remember the last time I 'restarted' a thread after interrupting it. The current debugger isn't very capable in any case; it would be nice to fix that. > I still don't get what goes wrong if we leave the unwinding > undone until the debugger is exited or an exception is > handled. It would require a little machinery, I guess, to > run unwindings for a handled exception in the proper > context... At this point, the debugger is launched precisely where the thread would have been terminated, which has to occur after the unwinding stage. Making sure that exiting the debugger would always lead to running the unwind code might be difficult. -- keith.packard at intel.com -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part Url : /pipermail/nickle/attachments/20080206/25232348/attachment.pgp From cworth at cworth.org Wed Feb 6 11:10:18 2008 From: cworth at cworth.org (Carl Worth) Date: Wed, 06 Feb 2008 11:10:18 -0800 Subject: [Nickle] nickle: Branch 'master' - 4 commits In-Reply-To: References: Message-ID: <87ejbpoq91.wl%cworth@cworth.org> Harsh! Would someone care to fix the commit mail scripts/nickle mailing list setup? Presumably the script could mail with something like: From: nickle-commit at nickle.org (Carl Worth) and the mailing list could have that address on its whitelist. -Carl On Wed, 06 Feb 2008 10:02:29 -0800, nickle-owner wrote: > You are not allowed to post to this mailing list, and your message has > been automatically rejected. If you think that your messages are > being rejected in error, contact the mailing list owner at > nickle-owner at nickle.org. > > [2 ] > From: cworth at keithp.com (Carl Worth) > Subject: nickle: Branch 'master' - 4 commits > To: nickle at nickle.org > Date: Wed, 6 Feb 2008 10:01:52 -0800 (PST) > > builtin-sockets.c | 263 ++++++++++++++++++++++++++++++++++++++++-------------- > file.c | 1 > test/README | 2 > value.h | 1 > 4 files changed, 201 insertions(+), 66 deletions(-) > > New commits: > commit d28531507f1064d5eea4b21988817ead2012fe7a > Author: Carl Worth > Date: Sat Feb 2 23:36:50 2008 +1100 > > Socket: Provide varargs versions of bind and connect > > With this change, the hostname is now not-required, (and even not > allowed), for an AF_UNIX socket. Also, for an AF_INET socket the > port can now be an integer as well as a string service or port. ... -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080206/c0013812/attachment-0001.pgp From cworth at cworth.org Wed Feb 6 11:19:10 2008 From: cworth at cworth.org (Carl Worth) Date: Wed, 06 Feb 2008 11:19:10 -0800 Subject: [Nickle] Artificial distinction between import and autoimport In-Reply-To: References: <873ascupxg.wl%cworth@cworth.org> <1201943342.19008.47.camel@koto.keithp.com> <87k5linhrm.wl%cworth@cworth.org> Message-ID: <87d4r9opu9.wl%cworth@cworth.org> On Wed, 06 Feb 2008 10:42:31 -0800, Bart Massey wrote: > What's the advantage of getting rid of "auto", again? From the beginning, then: I started learning nickle and started writing programs like this: import File; import Socket; and that seemed a reasonable-enough syntax and it worked. I can't say exactly where I picked up the "import" keyword. Then, eventually I hit this: import Process; :4: non-existant namespace Process and got totally confused. Keith straightened me out and showed me that I could make it work with: autoimport Process; and that's where I was left trying to learn whether I should say "autoimport" or "import" for any given namespace. Now, you seem to be arguing for users to just use "autoimport" everywhere, but that's a bad idea for several reasons: 1. As a keyword, it's particularly bad since it's not a word. 2. It's obviously a derivative of "import" so users are eventually going to type that, (whether intentionally or accidentally), and that will lead to confusion, (because it will sometimes work). > just too ugly to live with? I'll admit that it might not > have been the most fortuitous choice, but do we really hate > it enough to make it go away? I do, yes. :-) I'm not actually reading through all the semantic discussions now, but please provide new users with a better keyword than "autoimport" as the standard-practice means of importing namespaces into their programs. -Carl -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080206/5e20c098/attachment-0001.pgp From cworth at cworth.org Wed Feb 6 11:33:09 2008 From: cworth at cworth.org (Carl Worth) Date: Wed, 06 Feb 2008 11:33:09 -0800 Subject: [Nickle] Artificial distinction between import and autoimport In-Reply-To: References: <873ascupxg.wl%cworth@cworth.org> <1201943342.19008.47.camel@koto.keithp.com> <87k5linhrm.wl%cworth@cworth.org> Message-ID: <87bq6top6y.wl%cworth@cworth.org> On Wed, 06 Feb 2008 10:42:31 -0800, Bart Massey wrote: > Let me try one more time to explain what's going on > currently, with some use cases, and see if I can make sense > of the proposed new world: ... Thanks for the detailed explanation of the current workings. That's helpful. > New: import > Becomes an alias for autoimport ? > It appears to me that this change is a dangerous one. I don't follow what about the change introduces danger. There are no new semantics being introduced, but simply new names for existing semantics. Or are you just saying that the semantics of the current "autoimport" are dangerous and that people would be wise not to use it? If so, then whatever the imagined security problem is there, it should be fixed. For example, does nickle search the current working directory by default for things to autoload? I could definitely see that being a problem, (as opposed to searching the directory in which the script lives, or the system directory, or directories explicitly listed on some search path). -Carl -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080206/971011dd/attachment-0001.pgp From cworth at cworth.org Wed Feb 6 12:03:24 2008 From: cworth at cworth.org (Carl Worth) Date: Wed, 06 Feb 2008 12:03:24 -0800 Subject: [Nickle] Nickle annoyance fixed In-Reply-To: References: Message-ID: <87abmdonsj.wl%cworth@cworth.org> On Wed, 06 Feb 2008 11:16:02 -0800, wrote: > I've changed it so that all public names are printed when > print is invoked with no arguments. When passed a faulty > argument, print will complain and refuse. Hurrah! When I asked Keith last week how to print all public names, his reply was "print " and that worked but was quite unsatisfying. It's nice to see a proper fix for this now. -Carl -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: not available Url : /pipermail/nickle/attachments/20080206/7cfce15f/attachment.pgp From keithp at keithp.com Thu Feb 7 21:32:41 2008 From: keithp at keithp.com (Keith Packard) Date: Thu, 07 Feb 2008 21:32:41 -0800 Subject: [Nickle] Exception for reading past EOF? Message-ID: <1202448761.5053.13.camel@koto.keithp.com> So, I'm thinking that reading past EOF on a file should generate an exception instead of returning -1 from getc. This would mean that anyone using -1 to detect EOF would suddenly start crashing instead, but it would also mean that applications accidentally failing to check would get terminated instead of looping. We love semantic changes; is this a good one? -- keith.packard at intel.com -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part Url : /pipermail/nickle/attachments/20080207/5681a0a6/attachment.pgp