Using boost asio stream_handle with sequential like file based devices

493 Views Asked by At

I am considering using asio to perform "overlapped" (completion ports) style IO on a windows "device handle" created with CreateFile(...overlapped...)

I have quite a particular design though, because of specifics of my application I must maintain a different thread pool for performing the actual processing of data(work pool) and a pool (a very small one, could be just one thread maybe) of actual IO completions triggered by the processing pool.

Basically, at the beginning I want to trigger a handful of IO requests to my device that are initiated from IO pool. When these complete I notify a scheduling component that POSTS the completion packets content to different threads in the work pool. These completion notifications return quickly because actual processing will happen in work pool -> and from a particular work pool thread after processing takes place a new "read" is initiated that should trigger completion on the io pool.

Is this dissociation possible using windows::stream_handle ? In general seems the asio API associates read completions with the same io_service associated with the stream object.

EDIT Been a long time now, as I've already implemented the approach. I've updated my response to reflect my choices.

1

There are 1 best solutions below

0
On BEST ANSWER
  1. I create the "device" handle using windows specific function:

    HANDLE file_handle = CreateFile(... FILE_FLAG_OVERLAPPED ...);

  2. I can associate/register the handle with my ioService so that all overlapped requests to the device are handled by one of the ioService thread(s).

    error_code ec; auto &io_service_impl = use_service(ioservice); io_service_impl.register_handle(file_handle, ec);

  3. Then I can use the asio overlapped to initiate async IO from a different thread than ioService.run(), making it trigger completion in the io service thread(s):

        void IoPool::InitiateNewRead()
        {
    
        service.post([this]() {auto handler = InterceptorReadHandler::Create(bufferPool, data, service);
                    handler->SetContext(context);
    
                    return device.Read(std::move(handler));
                });
            }
        }
    
  4. For reference a device read implementation:

    bool Device::Read(std::shared_ptr<InterceptorReadHandler> handler) {
    auto handlerWrapper = [handler](const boost::system::error_code &ec, std::size_t len) {
        handler->Completion(ec, len);
    };
    win::overlapped_ptr overlapped(handler->GetService(), handlerWrapper);
    
    
    unsigned long bytesRead = 0;
    auto& packet = handler->GetBuffer();
    auto ok = ::ReadFile(handle,
                         packet.data,
                         static_cast<DWORD> (packet.length),
                         &bytesRead,
                         overlapped.get()) ? true : false;
    
    auto lastError = ::GetLastError();
    if (!ok && lastError != ERROR_IO_PENDING) {
        boost::system::error_code errCode(lastError, boost::system::get_system_category());
        overlapped.complete(errCode, 0);
        return false;
    } else {
        overlapped.release();
    }
    
    return true;
    

    }