nix-super/src/libutil/windows/muxable-pipe.cc

71 lines
2.7 KiB
C++
Raw Normal View History

#include <ioapiset.h>
#include "windows-error.hh"
#include "logging.hh"
#include "util.hh"
#include "muxable-pipe.hh"
namespace nix {
void MuxablePipePollState::poll(HANDLE ioport, std::optional<unsigned int> timeout)
{
/* We are on at least Windows Vista / Server 2008 and can get many
(countof(oentries)) statuses in one API call. */
if (!GetQueuedCompletionStatusEx(
ioport, oentries, sizeof(oentries) / sizeof(*oentries), &removed, timeout ? *timeout : INFINITE, false)) {
windows::WinError winError("GetQueuedCompletionStatusEx");
if (winError.lastError != WAIT_TIMEOUT)
throw winError;
assert(removed == 0);
} else {
assert(0 < removed && removed <= sizeof(oentries) / sizeof(*oentries));
}
}
void MuxablePipePollState::iterate(
std::set<MuxablePipePollState::CommChannel> & channels,
std::function<void(Descriptor fd, std::string_view data)> handleRead,
std::function<void(Descriptor fd)> handleEOF)
{
auto p = channels.begin();
while (p != channels.end()) {
decltype(p) nextp = p;
++nextp;
for (ULONG i = 0; i < removed; i++) {
if (oentries[i].lpCompletionKey == ((ULONG_PTR) ((*p)->readSide.get()) ^ 0x5555)) {
printMsg(lvlVomit, "read %s bytes", oentries[i].dwNumberOfBytesTransferred);
if (oentries[i].dwNumberOfBytesTransferred > 0) {
std::string data{
(char *) (*p)->buffer.data(),
oentries[i].dwNumberOfBytesTransferred,
};
handleRead((*p)->readSide.get(), data);
}
if (gotEOF) {
handleEOF((*p)->readSide.get());
nextp = channels.erase(p); // no need to maintain `channels`?
} else {
BOOL rc = ReadFile(
(*p)->readSide.get(), (*p)->buffer.data(), (*p)->buffer.size(), &(*p)->got, &(*p)->overlapped);
if (rc) {
// here is possible (but not obligatory) to call
// `handleRead` and repeat ReadFile immediately
} else {
windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get());
if (winError.lastError == ERROR_BROKEN_PIPE) {
handleEOF((*p)->readSide.get());
nextp = channels.erase(p); // no need to maintain `channels` ?
} else if (winError.lastError != ERROR_IO_PENDING)
throw winError;
}
}
break;
}
}
p = nextp;
}
}
}