// perform a synchronous read request on this thread. // may not be aligned - so we will have to buffer. HRESULT CAsyncIo::SyncRead( LONGLONG llPos, LONG lLength, BYTE * pBuffer) { if(IsAligned(llPos) && IsAligned(lLength) && IsAligned((LONG_PTR) pBuffer)) { LONG cbUnused; return SyncReadAligned(llPos, lLength, pBuffer, &cbUnused, NULL); } // not aligned with requirements - use buffered file handle. //!!! might want to fix this to buffer the data ourselves? CAsyncRequest request; HRESULT hr = request.Request(this, m_pStream, llPos, lLength, FALSE, pBuffer, NULL, 0); if(FAILED(hr)) { return hr; } return request.Complete(); }
HRESULT CAsyncIo::SyncRead( LONGLONG llPos, LONG lLength, BYTE* pBuffer) { if (IsAligned(llPos) && IsAligned(lLength) && IsAligned((LONG) pBuffer)) { LONG cbUnused; return SyncReadAligned(llPos, lLength, pBuffer, &cbUnused, NULL); } CAsyncRequest request; HRESULT hr = request.Request( this, m_pStream, llPos, lLength, FALSE, pBuffer, NULL, 0); if (FAILED(hr)) { return hr; } return request.Complete(); }
// add a request to the queue. HRESULT CAsyncIo::Request( LONGLONG llPos, LONG lLength, BOOL bAligned, BYTE * pBuffer, LPVOID pContext, DWORD_PTR dwUser) { if(bAligned) { if(!IsAligned(llPos) || !IsAligned(lLength) || !IsAligned((LONG_PTR) pBuffer)) { return VFW_E_BADALIGN; } } CAsyncRequest* pRequest = new CAsyncRequest; if (!pRequest) return E_OUTOFMEMORY; HRESULT hr = pRequest->Request(this, m_pStream, llPos, lLength, bAligned, pBuffer, pContext, dwUser); if(SUCCEEDED(hr)) { // might fail if flushing hr = PutWorkItem(pRequest); } if(FAILED(hr)) { delete pRequest; } return hr; }
// called on thread to process any active requests void CAsyncIo::ProcessRequests(void) { // lock to get the item and increment the outstanding count CAsyncRequest * preq = NULL; for(;;) { { CAutoLock lock(&m_csLists); preq = GetWorkItem(); if(preq == NULL) { // done return; } // one more item not on the done or work list m_cItemsOut++; // release critsec } preq->Complete(); // regain critsec to replace on done list { CAutoLock l(&m_csLists); PutDoneItem(preq); if(--m_cItemsOut == 0) { if(m_bWaiting) m_evAllDone.Set(); } } } }
// perform a synchronous read request on this thread. // Need to hold m_csFile while doing this (done in request object) HRESULT CAsyncIo::SyncReadAligned( LONGLONG llPos, LONG lLength, BYTE * pBuffer, LONG * pcbActual, PVOID pvContext) { CheckPointer(pcbActual,E_POINTER); if(!IsAligned(llPos) || !IsAligned(lLength) || !IsAligned((LONG_PTR) pBuffer)) { return VFW_E_BADALIGN; } CAsyncRequest request; m_pStream->Lock(); HRESULT hr = request.Request(this, m_pStream, llPos, lLength, TRUE, pBuffer, pvContext, 0); if(SUCCEEDED(hr)) { hr = request.Complete(); } m_pStream->Unlock(); // return actual data length *pcbActual = request.GetActualLength(); return hr; }
void CAsyncIo::ProcessRequests(void) { CAsyncRequest * preq = NULL; for (;;) { { CAutoLock lock(&m_csLists); preq = GetWorkItem(); if (preq == NULL) { return; } m_cItemsOut++; } preq->Complete(); { CAutoLock l(&m_csLists); PutDoneItem(preq); if (--m_cItemsOut == 0) { if (m_bWaiting) { m_evAllDone.Set(); } } } } }
HRESULT CAsyncIo::SyncReadAligned( LONGLONG llPos, LONG lLength, BYTE* pBuffer, LONG* pcbActual, PVOID pvContext ) { if (!IsAligned(llPos) || !IsAligned(lLength) || !IsAligned((LONG) pBuffer)) { return VFW_E_BADALIGN; } CAsyncRequest request; HRESULT hr = request.Request( this, m_pStream, llPos, lLength, TRUE, pBuffer, pvContext, 0); if (FAILED(hr)) { return hr; } hr = request.Complete(); *pcbActual = request.GetActualLength(); return hr; }
HRESULT CAsyncIo::BeginFlush() { { CAutoLock lock(&m_csLists); m_bFlushing = TRUE; CAsyncRequest * preq; while(preq = GetWorkItem()) { preq->Cancel(); PutDoneItem(preq); } if (m_cItemsOut > 0) { ASSERT(!m_bWaiting); m_bWaiting = TRUE; } else { m_evDone.Set(); return S_OK; } } ASSERT(m_bWaiting); for (;;) { m_evAllDone.Wait(); { CAutoLock lock(&m_csLists); if (m_cItemsOut == 0) { m_bWaiting = FALSE; m_evDone.Set(); return S_OK; } } } }
HRESULT CAsyncIo::WaitForNext( DWORD dwTimeout, LPVOID *ppContext, DWORD * pdwUser, LONG* pcbActual) { *ppContext = NULL; for (;;) { if (!m_evDone.Wait(dwTimeout)) { return VFW_E_TIMEOUT; } CAsyncRequest* pRequest = GetDoneItem(); if (pRequest) { HRESULT hr = pRequest->GetHResult(); if (hr == S_FALSE) { if ((pRequest->GetActualLength() + pRequest->GetStart()) == Size()) { hr = S_OK; } else { hr = E_FAIL; } } *pcbActual = pRequest->GetActualLength(); *ppContext = pRequest->GetContext(); *pdwUser = pRequest->GetUser(); delete pRequest; return hr; } else { CAutoLock lck(&m_csLists); if (m_bFlushing && !m_bWaiting) { return VFW_E_WRONG_STATE; } } } }
// cancel all items on the worklist onto the done list // and refuse further requests or further WaitForNext calls // until the end flush // // WaitForNext must return with NULL only if there are no successful requests. // So Flush does the following: // 1. set m_bFlushing ensures no more requests succeed // 2. move all items from work list to the done list. // 3. If there are any outstanding requests, then we need to release the // critsec to allow them to complete. The m_bWaiting as well as ensuring // that we are signalled when they are all done is also used to indicate // to WaitForNext that it should continue to block. // 4. Once all outstanding requests are complete, we force m_evDone set and // m_bFlushing set and m_bWaiting false. This ensures that WaitForNext will // not block when the done list is empty. HRESULT CAsyncIo::BeginFlush() { // hold the lock while emptying the work list { CAutoLock lock(&m_csLists); // prevent further requests being queued. // Also WaitForNext will refuse to block if this is set // unless m_bWaiting is also set which it will be when we release // the critsec if there are any outstanding). m_bFlushing = TRUE; CAsyncRequest * preq; while((preq = GetWorkItem()) != 0) { preq->Cancel(); PutDoneItem(preq); } // now wait for any outstanding requests to complete if(m_cItemsOut > 0) { // can be only one person waiting ASSERT(!m_bWaiting); // this tells the completion routine that we need to be // signalled via m_evAllDone when all outstanding items are // done. It also tells WaitForNext to continue blocking. m_bWaiting = TRUE; } else { // all done // force m_evDone set so that even if list is empty, // WaitForNext will not block // don't do this until we are sure that all // requests are on the done list. m_evDone.Set(); return S_OK; } } ASSERT(m_bWaiting); // wait without holding critsec for(;;) { m_evAllDone.Wait(); { // hold critsec to check CAutoLock lock(&m_csLists); if(m_cItemsOut == 0) { // now we are sure that all outstanding requests are on // the done list and no more will be accepted m_bWaiting = FALSE; // force m_evDone set so that even if list is empty, // WaitForNext will not block // don't do this until we are sure that all // requests are on the done list. m_evDone.Set(); return S_OK; } } } }
// wait for the next request to complete HRESULT CAsyncIo::WaitForNext( DWORD dwTimeout, LPVOID * ppContext, DWORD_PTR * pdwUser, LONG * pcbActual) { CheckPointer(ppContext,E_POINTER); CheckPointer(pdwUser,E_POINTER); CheckPointer(pcbActual,E_POINTER); // some errors find a sample, others don't. Ensure that // *ppContext is NULL if no sample found *ppContext = NULL; // wait until the event is set, but since we are not // holding the critsec when waiting, we may need to re-wait for(;;) { if(!m_evDone.Wait(dwTimeout)) { // timeout occurred return VFW_E_TIMEOUT; } // get next event from list CAsyncRequest* pRequest = GetDoneItem(); if(pRequest) { // found a completed request // check if ok HRESULT hr = pRequest->GetHResult(); if(hr == S_FALSE) { LONGLONG llBytesToRead = 0, llAvailable = 0; hr = m_pStream->Length(&llBytesToRead, &llAvailable); if (SUCCEEDED(hr) && (pRequest->GetActualLength() + pRequest->GetStart() == llBytesToRead)) { // this means the actual length was less than // requested - may be ok if he aligned the end of file hr = S_OK; } else { // it was an actual read error hr = E_FAIL; } } // return actual bytes read *pcbActual = pRequest->GetActualLength(); // return his context *ppContext = pRequest->GetContext(); *pdwUser = pRequest->GetUser(); delete pRequest; return hr; } else { // Hold the critical section while checking the list state CAutoLock lck(&m_csLists); if(m_bFlushing && !m_bWaiting) { // can't block as we are between BeginFlush and EndFlush // but note that if m_bWaiting is set, then there are some // items not yet complete that we should block for. return VFW_E_WRONG_STATE; } } // done item was grabbed between completion and // us locking m_csLists. } }