Is it possible to change HANDLE that has been opened for synchronous I/O to be opened for asynchronous I/O during its lifetime?
I see I was a bad reader of MSDN :/ I totally missed function ReOpenFile()
which has been introduced probably as early as in June 2003 in Windows Server 2003 (according to this article). To defend myself at least a little: I would expect the CreateFile()
description to cross reference to ReOpenFile()
description. There is a reference on ReOpenFile()
page to CreateFile()
page, but not the other way round.
This function seems to enable precisely what I need: adding or removing of FILE_FLAG_OVELRAPPED
to/from already existing handles by creating new handle with desired properties! :-D I have not tested it yet, though. It is, unfortunately, available only on Windows 2003 Server, Windows Vista and onwards. Question about the previous OS versions has been answered here. The function does not exist in public API in OS prior to Windows 2003 Server. It is used by the underlying implementation, but it is not available to developers on those systems (Not supported).
That practically means that there is no hope for me at least for next few years, until we drop support for older Windows platforms. It also means that the situation regarding the I/O has been REALLY bad on OS older then Windows Vista. Other painful part that has been missing altogether was the possibility to cancel synchronous and asynchronous I/O on those older systems.
Furthermore, I still miss one part of the answer: can the presence of flags be tested by any means? I have not found function for doing that. That means that if we want to guarantee presence of some flag in file object, then the file always has to be re-opened.
3 years passed and Windows 8 has been released. Thanks to the regression introduced in implementation of console in Windows 8 I got to do something about the issue which triggered this question. So I have finally tried to use ReOpenFile() function call.
In one sentence: for my purposes it is useless.
The ReOpenFile() API is used for “taking an existing file handle and getting another handle that has a different set of access rights”. At least that is stated in the original article.
I tried to use The ReOpenFile() on Console input handle:
stdin_in = GetStdHandle(STD_INPUT_HANDLE);
stdin_in_operlapped = ReOpenFile(stdin_in, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, FILE_FLAG_OVERLAPPED);
if (stdin_in_operlapped == INVALID_HANDLE_VALUE)
{
my_debug("failed to ReOpen stdin handle with OVERLAPPED flag: %d", GetLastError());
exit(1);
}
And what I get is: error 1168: “Element Not Found”. “Thank you Microsoft”. I will not even try to use it for anonymous pipes, since the documentation states:
“Asynchronous (overlapped) read and write operations are not supported by anonymous pipes. This means that you cannot use the ReadFileEx and WriteFileEx functions with anonymous pipes. In addition, the lpOverlapped parameter of ReadFile and WriteFile is ignored when these functions are used with anonymous pipes.”
Thank you, people, all for your suggestions. When reading from handle asynchronously, one has to be ready also for the fact that the operation may complete synchronously. That I know. The main reason I was asking this question is:
when a synchronous read has been issued on some objects (at least anonymous pipes, and console input in Windows 8) then calling CloseHandle() from another thread on the same handle will either fail, or hang, until the ReadFile() completes; that means it will hang indefinitely in many cases. That is the reason why I wanted to replace the synchronous handle with asynchronous.
Now it is clear to me that it is simply not possible in Windows operating systems to cancel some read operations in straightforward way. When reading from synchronous handles, one just has to exit from application even if ReadFile() is still reading from a handle in some thread, because it is simply impossible to reliably wake up such a read operation. In know... In newer OS it is possible to cancel that operation. However, there is no way to know weather the thread is in the ReadFile() call already, or not yet. If ReadFile() is not called yet then there is no operation to cancel and subsequent read will hang. The only way would be to close the handle, but that operation hangs, or fails on some objects and on some operating systems. The only proper solution to this is asynchronous I/O. But, as I mentioned in the beginning, our APP is started by third party apps and we can't force them all to always create named pipes with overlapped flag set for the stdio.
I am giving up and going to implement nasty ugly hacks... we will have to keep reading without OVERLAPPED structure from HANDLEs that have been created with OVERLAPPED flag, and leaking handles and threads....
If I understand what you're after, I would like to suggest that you don't care if was opened with the overlapped flag or not. I believe that you can safely pass in an OVERLAPPED
structure in both the synchronous and asynchronous cases. Your code needs to be able to handle ReadFile()
returning false
and GetLastError()
returning ERROR_IO_PENDING
. You'll also need to add appropriate calls to GetOverlappedResult()
, WaitForSingleObject()
, etc.
The MSDN article on ReadFile()
has some good info on this under "Considerations for working with synchronous file handles", and "Considerations for working with asynchronous file handles" in the "Synchronization and File Position" section.