Author: Reinhard Max <max@suse.com>
Author: Harald Oehlmann <oehhar@users.sourceforge.net>
Author: Reinhard Max <max@tclers.tk>
State: Final
Type: Project
Vote: Done
Created: 16-Mar-2014
Post-History:
Keywords: async socket connect,introspection,IPV6
Tcl-Version: 8.6.4
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.