Over the past three days I've put real new features into PuTTY, which is pretty unusual these days.
A few weeks ago I discovered that the serial port on my Linux box was kaput. It may have blown up as part of this summer's general spree of all my hardware going pop, or it may have been defunct for longer than that and I just hadn't noticed; but whatever the cause, it meant I was forced to plug the serial console of my net4501 firewall into my Windows machine instead. My lack of any particularly sensible serial comms software for Windows thus caused me to develop a strong motivation for finally getting round to putting a serial-
The primary difficulty with this work is that Windows draws a fundamental distinction between network sockets and any other type of file handle, so it's fiddly and annoying to do the equivalent of select(1) in a way that handles both. However, with fresh supplies of motivation and energy, I hacked together a wrapper API which stores Windows HANDLE values, presents client code with a convenient means of reading and writing them, and presents front end code with a convenient means of incorporating them into its event loop. Armed with this, I was able to produce a serial back end in relatively short order.
Also, another feature which has been blocked for years on the appearance of that piece of infrastructure is local proxy support (equivalent to ProxyCommand in OpenSSH) on Windows. So I did that too, while I was at it; and I also hacked in a handy option in Plink which uses the SSH port forwarding support to do the equivalent of a server-netcat, only without depending on any particular binary being installed server-
In the process I got cross with Windows's file I/O API. It has a thing called ‘overlapped access’, which essentially means you tell it to start doing a read or a write, and check back later to see if it's finished. If you only have one thread, this is undoubtedly really useful, but in fact I decided it was less hassle just to fork off a subthread for each direction of I/O. If I do that, I thought, I won't need the annoying ‘overlapped’ bureaucracy, because each thread can block independently of the other. Right?
Wrong. It turns out that overlapped file access is mandatory if you want to read and write the same file simultaneously (which you do if it's a serial port). If you don't use overlapped access, then a pending write causes a read on the same handle to return an error code, and vice versa. I can see no credible reason at the API level why this should be so. I can only assume that in the distant past before threads were invented, overlapped access was the only way to do simultaneous reading and writing on the same handle, and nobody in Microsoft has ever got round to digging out all the assumptions left in the code from those days. So in fact, even though each of my I/O threads would be happy just to perform a blocking read or write, they actually have to perform an overlapped read or write and then immediately call the ‘wait for overlapped result’ function in blocking mode. *sigh*
Another thing that puzzles me is that the PuTTY serial back end currently seems to lose data if the device at the other end of the serial connection is sending lots while PuTTY does not have the input focus. I can only assume Windows does something hideous to thread priorities depending on where the GUI input focus is, which is a completely horrible idea from the word go and also I have no idea how to work around it yet. Gah.
On the plus side, I also learned this weekend about ‘strings -f’, which is one of the most useful command-for and sed to do the same job every time I want to know which of a number of binary files a string is in. No more! -f will come to my rescue the next time I need rescuing.
I believe this to be the case, yes.
As to why that causes serial line droppage, who can say. Any modern box really ought to have enough spare oomph to deal with a serial line. You could try doing IO in a pure-IO thread and setting it's thread priority to THREAD_PRIORITY_TIME_CRITICAL (==15).
(*) Foregroundness is a property of a top-level window. If the input focus is in a particular window, then its top-level parent ought to have been automatically foregrounded at some point. Each window is attached to a particular thread by virtue of having called CreateWindow in that thread - each thread for which GUI services have been enabled by calling pretty much any USER32/GDI32 function owns a set of three layered GetMessage message queues for system/synthetic (WM_PAINT etc), input (WM_KEYDOWN) and application (WM_COMMAND) messages, which process messages for any window attached to that thread. The foreground thread is the one to which the foreground window happens to be attached, and any (well, most) keyboard/mouse messages are delivered to its input queue alone.