Esempio n. 1
0
void AsyncByteStream::InitAsyncHandler()
{
    if ( _eventSource != nullptr )
        throw std::logic_error("This stream is already set up for async operation.");
    
    Weak<RingBuffer> weakReadBuf = _readbuf;
    Weak<RingBuffer> weakWriteBuf = _writebuf;
    
    _eventSource = new RunLoop::EventSource([=](RunLoop::EventSource&) {
        // atomically pull out the event flags here
        ThreadEvent t = _event.exchange(Wait);
        
        bool hasRead = false, hasWritten = false;
        
        uint8_t buf[4096];
        
        Shared<RingBuffer> readBuf = weakReadBuf.lock();
        Shared<RingBuffer> writeBuf = weakWriteBuf.lock();
        
        if ( (t & ReadSpaceAvailable) == ReadSpaceAvailable && readBuf )
        {
            std::lock_guard<RingBuffer> _(*readBuf);
            size_type read = this->read_for_async(buf, readBuf->SpaceAvailable());
            if ( read != 0 )
            {
                readBuf->WriteBytes(buf, read);
                hasRead = true;
            }
        }
        if ( (t & DataToWrite) == DataToWrite && writeBuf )
        {
            std::lock_guard<RingBuffer> _(*writeBuf);
            size_type written = writeBuf->ReadBytes(buf, writeBuf->BytesAvailable());
            written = this->write_for_async(buf, written);
            if ( written != 0 )
            {
                // only remove as much as actually went out
                writeBuf->RemoveBytes(written);
                hasWritten = true;
            }
        }
        
        auto invocation = [this, hasRead, hasWritten] () {
            if ( hasRead )
                _eventHandler(AsyncEvent::HasBytesAvailable, this);
            if ( hasWritten )
                _eventHandler(AsyncEvent::HasSpaceAvailable, this);
        };
        
        if ( _targetRunLoop != nullptr )
        {
            _targetRunLoop->PerformFunction(invocation);
        }
        else
        {
            invocation();
        }
    });
    
    if ( _asyncRunLoop == nullptr )
    {
        std::mutex __mut;
        std::condition_variable __inited;
        
        std::unique_lock<std::mutex> __lock(__mut);
        _asyncIOThread = std::thread([&](){
            AsyncByteStream::_asyncRunLoop = RunLoop::CurrentRunLoop();
            {
                std::unique_lock<std::mutex> __(__mut);
                __inited.notify_all();
            }
            
            // now run the run loop
            
            // only spin an empty run loop a certain amount of time before giving up
            // and exiting the thread entirely
            // FIXME: There's a gap here where a race could lose an EventSource addition
            static constexpr unsigned kMaxEmptyTicks(1000);
            static constexpr std::chrono::milliseconds kTickLen(10);
            unsigned __emptyTickCounter = 0;
            
            do
            {
                RunLoop::ExitReason __r = RunLoop::CurrentRunLoop()->Run(true, std::chrono::seconds(20));
                if ( __r == RunLoop::ExitReason::RunFinished )
                {
                    if ( ++__emptyTickCounter == kMaxEmptyTicks )
                        break;      // exit the thread
                    
                    // wait a bit and try again
                    std::this_thread::sleep_for(kTickLen);
                }
                
                // by definition not an empty runloop
                __emptyTickCounter = 0;
            } while (1);
            
            // nullify the global before we quit
            // deletion isn't necessary, it's done by TLS in run_loop.cpp
            _asyncRunLoop = nullptr;
        });
        
        // wait for the runloop to be set
        __inited.wait(__lock, [&](){return _asyncRunLoop != nullptr;});
    }
    
    // install the event source into the run loop, then we're all done
    _asyncRunLoop->AddEventSource(_eventSource);
}