hmssql
Safe HaskellNone
LanguageHaskell2010

Hmssql.Connection.Backend.Windows

Description

Overlapped I/O on a Windows handle: what a connection's bytes travel on over Shared Memory and Named Pipes, and on Windows over TCP as well (a socket is a handle that ReadFile and WriteFile accept). Hmssql.Connection.Backend wraps it as a Backend; on other platforms this module is empty.

GHC's own handle and socket I/O is not used. With the threaded RTS and the default I/O manager it blocks in a foreign call, which no asynchronous exception can interrupt and no other thread can end. Without -threaded an exception interrupts a read, but the read itself stays pending on a worker thread and takes the next bytes to arrive. With the native I/O manager (WinIO) it is interruptible, but that manager crashed under a few dozen busy sockets and hung on an interrupted file read (both upstream bugs, fixed on a GHC branch since), so nothing here goes through it either: the same code runs under both managers, and under the native one the low bit of each operation's event keeps its completion off the manager's completion port. Where the native manager is used at all (nativeIO), nothing comes here (see the end of this header).

Here every operation is started overlapped, so it returns at once, and a pending one is waited for on an MVar that a helper thread fills when the operation's event is signaled (awaitEvent). So:

  • An asynchronous exception (a timeout, a killThread) interrupts a blocked operation. The operation is then canceled with CancelIoEx and waited for, since its buffer and its OVERLAPPED must not be freed while the kernel may still write to them.
  • hioStopReceiving and hioShutdown are sticky. Each sets a flag and then cancels what is pending; an operation checks the flag before it starts and again once it is pending, so a cancel that arrives between two operations is not lost.
  • A read and a write can be pending at once (each has its own OVERLAPPED and event), which a handle opened without FILE_FLAG_OVERLAPPED doesn't allow: there the Attention would wait behind the reader thread's read.
  • Every operation is started on one bound thread that lives as long as the process (onIssuer), because Windows cancels a pending operation when the thread that started it exits, and the runtime's worker threads come and go.

That is the threaded runtime. Without -threaded a safe foreign call stops every Haskell thread, so the waits happen elsewhere: on a worker thread of GHC's default I/O manager, through asyncDoProc, which runs a C function there while the scheduler goes on (cbits/windows.c). An operation is started in place there, since every Haskell thread then runs on the one operating-system thread that runs the scheduler. The foreign calls that block for long (a name lookup, a connect, the open of a pipe) are run on a worker the same way (onWorker). The native I/O manager of a released GHC has no such workers, so without -threaded this module needs the default one (requireRuntime).

Under the native I/O manager, with a network and a GHC that support it (nativeIO), nothing comes here, with or without -threaded: TCP goes through network's own calls, as on Linux and macOS, and the pipes through the manager's completion port (Hmssql.Connection.Backend.WinIO), which takes its foreign calls and its errors from here.