in my io completion ports implementation I was delivering notifications through a switch statement on an opcode in a struct inheriting from OVERLAPPED structure expecting that the OVERLAPPED base is at offset 0 from the derived one
later I used a virtual method in the derived struct instead of an opcode with a switch , this made the design easier and more clear . I was still thinking that the OVERLAPPED structure is at offset 0 so I simply used something like this to issue an io operation :
WSASend(ns, &wsbuf, 1, &sent, 0, (LPWSAOVERLAPPED)(std::addressof(CTX)), nullptr);
where CTX
is a struct of type BaseIoCtx
which is :
BaseIoCtx : public OVERLAPPED
{
void *fd;
virtual void HandleIocpPacket(io::iocp::CompletionResult& IocpPacket) = 0;
virtual ~BaseIoCtx() = default;
};
and in casting back in the io loop I used this code :
// BaseIoCtx inherits from OVERLAPPED
// result is an OVERLAPPED_ENTRY
BaseIoCtx *ctx = reinterpret_cast<BaseIoCtx*>(result.lpOverlapped);
ctx->HandleIocpPacket(result); // the virtual function that delivers the notification
I thought this should work well , and it worked well for more than month !
yesterday after doing some tests I got a strange crash at this line :
ctx->HandleIocpPacket(result); // the virtual function that delivers the notification
the debugger was saying : read access violation ... ctx-> was (nullptr, 0XFFFFFFFF, any strange address)
however I seed ctx itself was at a valid address in the visual studio debugger and contained the correct transferred length in the OVERLAPPED base but the __vfptr
was nullptr
or a very strange address like 0xFFFFFFF or 0xCCCCCCCCC so I set a breakpoint on the line before submitting the io request and saw that the __vfptr was pointing to a valid vtable
and contained the two virtual functions of BaseIoCtx
, so I figured that during the io request some windows internal function wrote at the __vfptr
and corrupted it .
I was wondering because I though the base structure was at the start of the derived not the __vfptr
but after some experiments with offsetof I found that __vfptr
was at offset 0 instead !
yet I then changed the BaseIoCtx
into this :
struct BaseIoCtx : public IoContext
{
OVERLAPPED ov = { 0 };
static BaseIoCtx * from_ov_ptr(OVERLAPPED *ov_ptr) noexcept
{
constexpr std::size_t ov_offset = offsetof(BaseIoCtx, ov);
return reinterpret_cast<BaseIoCtx*>(reinterpret_cast<char*>(ov_ptr) - ov_offset);
}
void *fd;
virtual void HandleIocpPacket(io::iocp::CompletionResult& IocpPacket) = 0;
virtual ~BaseIoCtx() = default;
};
and in the the submitting I used :
WSASend(ns, &wsbuf, 1, &sent, 0, (LPWSAOVERLAPPED)(std::addressof(CTX.ov)), nullptr);
then in the io loop I got back the structure as :
BaseIoCtx * ctx = BaseIoCtx::from_ov_ptr(result.lpOverlapped);
and it works well . now the questions are : how did the first implementation worked for long time on the same compiler ! and is the new one considered a portable way in c++ rather than a hack and if it is a hack is there a better one ?
User contributions licensed under CC BY-SA 3.0