TIP #427: INTROSPECTION OF ASYNCHRONOUS SOCKET CONNECTION =========================================================== Version: $Revision: 1.13 $ Author: Reinhard Max Harald Oehlmann Reinhard Max State: Final Type: Project Tcl-Version: 8.6.4 Vote: Done Created: Sunday, 16 March 2014 URL: https://tip.tcl-lang.org427.html Post-History: ------------------------------------------------------------------------- ABSTRACT ========== This TIP describes a method to introspect the asynchronous connection process by an extension of the *fconfigure* interface in addition to *fconfigure -error*. This will enable better control over the asynchronous connection process, even in cases where the event loop is not in use. RATIONALE =========== The *socket* core command supports two ways to establish a client socket, /synchronous/ and /asynchronous/. In synchronous mode (which is the default) the command does not return until the connection attempt has completed (established or failed). In asynchronous mode (*-async option*) the command returns after DNS lookup and the connection is established in the background. This is useful in situations where it is undesirable that a process or thread blocks for completing a synchronous connection attempt. Classically, an asyncronously connecting socket would indicate that it had connected (or failed to connect) by becoming writeable, which *fileevent writable* can be used to detect. A DNS name may have multiple IP addresses associated, e.g. for IPv4/IPv6 dual stack hosts or for fail safety or load balancing reasons as it is the case for google.com as of this writing. In Tcl 8.5 the socket command only tried to connect to a single IPv4 address that was randomly picked from the list returned by DNS. In Tcl 8.6, the socket command tries to connect to all the IP addresses of a DNS name in turn until one succeeds or all have failed. This caused the following changes to the *socket -async* command from Tcl 8.5 to 8.6: * The socket introspection options to *fconfigure* (i.e., *-error*, *-sockname* and *-peername*) can change between successive invocations while the connection is in progress as they reflect the state of the internal loop over the IP addresses. * The event loop must run in order to allow looping over the various possible IP addresses of a host. The usage of *socket -async* is seen as helpful even without the event loop. An example is an application, which checks a list of hosts for a connection. The application may start many background socket connects, do something else, and then collect the results. Without the event loop (i.e., a *fileevent writable*), there is no non-blocking way to discover if the asynchronous connect has completed. In addition, the following future points may be considered: * The connection process may internally get delegated to its own thread; this would allow the connection process to be asynchronous without requiring the event loop. * A future Windows implementation may use the Vista+ API /WSAConnectByList/ (once we do not support Windows XP any more). Using this, no own looping over the addresses is necessary. It allows the connection process to be a single OS call, but does not allow inspection of the different connection steps. PROPOSED CHANGE ================= CURRENT INTROSPECTION CHANGE ------------------------------ The introspection functions should act as follows during an asynchronous connection process: * *fconfigure -error* will return the empty string (no error) * *fconfigure -sockname* will return the empty string * *fconfigure -peername* will return the empty string INTROSPECTION COMMAND TO INSPECT A RUNNING ASYNCHRONOUS CONNECT ----------------------------------------------------------------- An additional introspection function should inform if the asynchronous connect is running or if it has terminated: *fconfigure* /channel/ *-connecting* This option returns *1* as long as a socket is still in the process of connecting asynchronously and 0 when the asynchronous connection has completed (succeeded or failed) or the socket was opened synchronously. NON-EVENT LOOP OPERATION -------------------------- If the event loop runs, the state machine of a (possibly multiple-address try) async connection proceeds within an internal callback. In addition to that (for the case the event loop does currently not run), it proceeds whenever a channel operation is attempted on the socket_ * nonblocking I/O and [fconfigure] will advance it by one step (i.e. do whatever is doable without waiting). * blocking I/O will advance it to completion, in essence meaning "switch back to synchronous connect, and in case of success, do the blocking I/O I asked for". USE CASE OF THE CONNECTING OPTION =================================== My own use case for the proposed option *-connecting* is as follows. A TCL script is started within Rivet to do two tasks: * Verify many URLs for existance (e.g. open a socket and do a head request). * Do some data base work. I don't use the event loop to get a linear program with controlled order. So the program flow is as follows: * Open async sockets for all links to verify: /foreach host $hosts; set h($host) [socket -async $host 23]/ * Do some data base work which takes time. * Check all open sockets and eventually advance the state machine by calling *-connecting*: /foreach host $hosts; if {[fconfigure $h($host) -connecting]} {Connected $host}/ * Do some more data base work which takes time. * Check *-connecting* and react until there are no open sockets any more. I have cut the data base processing into two parts; I assume there are normally two IP's to try, one IPV6 and one IPV4. ALTERNATIVES ============== Two alternatives for the behavior of *fconfigure -sockname* and *fconfigure -peername* are: * If there is only one destination IP address, return this before the connection is really established. This may also happen if the socket has processed its /bind()/ system call in such a way that it knows it will not attempt another, and is currently processing its /connect()/ to the remote host. * If an asynchronous connect is running, raise an error. These are open to discussion. IMPLEMENTATION ================ The fossil branch /tip-427/ contains an implementation of these extensions. COPYRIGHT =========== This document has been placed in the public domain. ------------------------------------------------------------------------- TIP AutoGenerator - written by Donal K. Fellows