// // COutputQueuee Destructor : // // Free all resources - // // Thread, // Batched samples // COutputQueue::~COutputQueue() { DbgLog((LOG_TRACE, 3, TEXT("COutputQueue::~COutputQueue"))); /* Free our pointer */ if (m_pInputPin != NULL) { m_pInputPin->Release(); } if (m_hThread != NULL) { { CAutoLock lck(this); m_bTerminate = TRUE; m_hr = S_FALSE; NotifyThread(); } DbgWaitForSingleObject(m_hThread); EXECUTE_ASSERT(CloseHandle(m_hThread)); // The thread frees the samples when asked to terminate ASSERT(m_List->GetCount() == 0); delete m_List; } else { FreeSamples(); } if (m_hSem != NULL) { EXECUTE_ASSERT(CloseHandle(m_hSem)); } delete [] m_ppSamples; }
HRESULT TffOutputQueue::ReceiveMultiple( IMediaSample **ppSamples, long nSamples, long *nSamplesProcessed) { CAutoLock lck(this); // Either call directly or queue up the samples /* We're sending to our thread */ if (m_hr != S_OK) { *nSamplesProcessed = 0; DPRINTF(_l("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"), nSamples, m_hr); for (int i = 0; i < nSamples; i++) { ppSamples[i]->Release(); } return m_hr; } m_bFlushed = FALSE; for (long i = 0; i < nSamples; i++) { QueueSample(ppSamples[i]); } *nSamplesProcessed = nSamples; if (!m_bBatchExact || m_nBatched + m_List->GetCount() >= m_lBatchSize) { //DPRINTF(_l("COutputQueue queued a sample")); NotifyThread(); } return S_OK; }
bool RuntimeQueue::Block(const ThreadId& targetThread, u64 taskId, rqe& ec) { if(context->shutdownFlag.load()) { ec = RQE::ShuttingDown; return false; } DProfContext __(RQ_API "Blocking task"); auto thread_id = targetThread.hash(); RuntimeQueue* pQueue = nullptr; { Lock _(context->globalMod); auto q_it = context->queues.find(thread_id); if(q_it == context->queues.end()) { ec = RQE::InvalidQueue; return false; } pQueue = &(*q_it).second; } auto& queue = *pQueue; RuntimeTask const* task = nullptr; szptr idx = 0; if(!(task = GetTask(queue.mTasks, taskId, ec, &idx))) { ec = RQE::InvalidTaskId; return false; } { /* We do this check in case we are executing in the queue */ /* Otherwise we deadlock */ RecLock ___(queue.mTasksLock); if(!queue.mTasks[idx].alive) { ec = RQE::TaskAlreadyBlocked; return false; } auto currentBase = RuntimeTask::clock::now(); auto previousNextTime = queue.timeTillNext(currentBase); queue.mTasks[idx].alive = false; NotifyThread(context, thread_id, previousNextTime, currentBase); } return true; }
// Get ready for new data - cancels sticky m_hr void COutputQueue::Reset() { if (!IsQueued()) { m_hr = S_OK; } else { CAutoLock lck(this); QueueSample(RESET_PACKET); NotifyThread(); m_evFlushComplete.Wait(); } }
// // Flush all the samples in the queue // void COutputQueue::BeginFlush() { if (IsQueued()) { { CAutoLock lck(this); // block receives -- we assume this is done by the // filter in which we are a component // discard all queued data m_bFlushing = TRUE; // Make sure we discard all samples from now on if (m_hr == S_OK) { m_hr = S_FALSE; } // Optimize so we don't keep calling downstream all the time if (m_bFlushed && m_bFlushingOpt) { return; } // Make sure we really wait for the flush to complete m_evFlushComplete.Reset(); NotifyThread(); } // pass this downstream m_pPin->BeginFlush(); } else { // pass downstream first to avoid deadlocks m_pPin->BeginFlush(); CAutoLock lck(this); // discard all queued data m_bFlushing = TRUE; // Make sure we discard all samples from now on if (m_hr == S_OK) { m_hr = S_FALSE; } } }
bool RuntimeQueue::CancelTask(const ThreadId& targetThread, u64 taskId, rqe& ec) { if(context->shutdownFlag.load()) { ec = RQE::ShuttingDown; return false; } auto thread_id = targetThread.hash(); RuntimeQueue* pQueue = nullptr; { Lock _(context->globalMod); auto q_it = context->queues.find(thread_id); if(q_it == context->queues.end()) { ec = RQE::InvalidQueue; return false; } pQueue = &(*q_it).second; } auto& queue = *pQueue; { RecLock _(queue.mTasksLock); RuntimeTask const* task = nullptr; szptr idx = 0; if(!(task = GetTask(queue.mTasks, taskId, ec, &idx))) { ec = RQE::InvalidTaskId; return false; } queue.mTasks[idx].alive = false; queue.mTasks[idx].to_dispose = true; auto currentBase = RuntimeTask::clock::now(); auto previousNextTime = queue.timeTillNext(currentBase); NotifyThread(context, thread_id, previousNextTime, currentBase); } return true; }
// Send batched stuff anyway void COutputQueue::SendAnyway() { if (!IsQueued()) { // m_bSendAnyway is a private parameter checked in ReceiveMultiple m_bSendAnyway = TRUE; LONG nProcessed; ReceiveMultiple(NULL, 0, &nProcessed); m_bSendAnyway = FALSE; } else { CAutoLock lck(this); QueueSample(SEND_PACKET); NotifyThread(); } }
void COutputQueue::NewSegment( REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { if (!IsQueued()) { if (S_OK == m_hr) { if (m_bBatchExact) { SendAnyway(); } m_pPin->NewSegment(tStart, tStop, dRate); } } else { if (m_hr == S_OK) { // // we need to queue the new segment to appear in order in the // data, but we need to pass parameters to it. Rather than // take the hit of wrapping every single sample so we can tell // special ones apart, we queue special pointers to indicate // special packets, and we guarantee (by holding the // critical section) that the packet immediately following a // NEW_SEGMENT value is a NewSegmentPacket containing the // parameters. NewSegmentPacket * ppack = new NewSegmentPacket; if (ppack == NULL) { return; } ppack->tStart = tStart; ppack->tStop = tStop; ppack->dRate = dRate; CAutoLock lck(this); QueueSample(NEW_SEGMENT); QueueSample( (IMediaSample*) ppack); NotifyThread(); } } }
// // End of Stream is queued to output device // void COutputQueue::EOS() { CAutoLock lck(this); if (!IsQueued()) { if (m_bBatchExact) { SendAnyway(); } if (m_hr == S_OK) { DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()"))); m_bFlushed = FALSE; HRESULT hr = m_pPin->EndOfStream(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()"))); } } } else { if (m_hr == S_OK) { m_bFlushed = FALSE; QueueSample(EOS_PACKET); NotifyThread(); } } }
u64 RuntimeQueue::Queue( RuntimeQueue* queue, RuntimeTask&& task, RuntimeQueue::rqe& ec) { if(context->shutdownFlag.load()) { ec = RQE::ShuttingDown; return 0; } DProfContext _(RQ_API "Adding task to Queue"); auto& ref = *queue; RecLock __(queue->mTasksLock); auto currentBase = RuntimeTask::clock::now(); auto previousNextTime = ref.timeTillNext(currentBase); auto id = ref.enqueue(std::move(task)); NotifyThread( context, queue->mThreadId.hash(), previousNextTime, currentBase); return id; }
bool LqThreadBase::StartThreadAsync() { bool Res = true; StartThreadLocker.LockWrite(); if(IsThreadRunning()) { StartThreadLocker.UnlockWrite(); lq_errno_set(EALREADY); return false; } IsShouldEnd = false; IsStarted = false; #ifdef LQPLATFORM_WINDOWS unsigned threadID; uintptr_t Handler = _beginthreadex(NULL, 0, BeginThreadHelper, this, 0, &threadID); CurThreadId = threadID; ThreadHandler = Handler; if(Handler == -1L) { switch(errno) { case EAGAIN: lq_errno_set(EAGAIN); break; case EINVAL: lq_errno_set(EINVAL); break; case EACCES: lq_errno_set(EACCES); break; } #else pthread_t threadID = 0; int Err = pthread_create(&threadID, NULL, BeginThreadHelper, this); CurThreadId = threadID; if(Err != 0) { #endif Res = false; CurThreadId = -((intptr_t)1); ThreadHandler = 0; } StartThreadLocker.UnlockWrite(); return Res; } bool LqThreadBase::StartThreadSync() { bool Res = true; StartThreadLocker.LockWrite(); if(IsThreadRunning()) { StartThreadLocker.UnlockWrite(); lq_errno_set(EALREADY); return false; } IsShouldEnd = false; IsStarted = false; #ifdef LQPLATFORM_WINDOWS unsigned threadID; uintptr_t Handler = _beginthreadex(NULL, 0, BeginThreadHelper, this, 0, &threadID); CurThreadId = threadID; ThreadHandler = Handler; if(Handler == -1L) { switch(errno) { case EAGAIN: lq_errno_set(EAGAIN); break; case EINVAL: lq_errno_set(EINVAL); break; case EACCES: lq_errno_set(EACCES); break; } #else pthread_t threadID = 0; int Err = pthread_create(&threadID, NULL, BeginThreadHelper, this); CurThreadId = threadID; if(Err != 0){ #endif Res = false; CurThreadId = -((intptr_t)1); ThreadHandler = 0; } else { while(!IsStarted) LqThreadYield(); } StartThreadLocker.UnlockWrite(); return Res; } bool LqThreadBase::IsThisThread() const { #ifdef LQPLATFORM_WINDOWS return GetCurrentThreadId() == CurThreadId; #else return pthread_equal(pthread_self(), CurThreadId); #endif } bool LqThreadBase::ExitThreadAsync() { bool r = true; StartThreadLocker.LockWrite(); if(IsThreadRunning()) { IsShouldEnd = true; if(!IsThisThread()) NotifyThread(); } else { lq_errno_set(ENOENT); r = false; } StartThreadLocker.UnlockWrite(); return r; }
HRESULT COutputQueue::ReceiveMultiple ( IMediaSample **ppSamples, long nSamples, long *nSamplesProcessed) { CAutoLock lck(this); // Either call directly or queue up the samples if (!IsQueued()) { // If we already had a bad return code then just return if (S_OK != m_hr) { // If we've never received anything since the last Flush() // and the sticky return code is not S_OK we must be // flushing // ((!A || B) is equivalent to A implies B) ASSERT(!m_bFlushed || m_bFlushing); // We're supposed to Release() them anyway! *nSamplesProcessed = 0; for (int i = 0; i < nSamples; i++) { DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (direct) : Discarding %d samples code 0x%8.8X"), nSamples, m_hr)); ppSamples[i]->Release(); } return m_hr; } // // If we're flushing the sticky return code should be S_FALSE // ASSERT(!m_bFlushing); m_bFlushed = FALSE; ASSERT(m_nBatched < m_lBatchSize); ASSERT(m_nBatched == 0 || m_bBatchExact); // Loop processing the samples in batches LONG iLost = 0; long iDone; for (iDone = 0; iDone < nSamples || (m_nBatched != 0 && m_bSendAnyway); ) { //pragma message (REMIND("Implement threshold scheme")) ASSERT(m_nBatched < m_lBatchSize); if (iDone < nSamples) { m_ppSamples[m_nBatched++] = ppSamples[iDone++]; } if (m_nBatched == m_lBatchSize || nSamples == 0 && (m_bSendAnyway || !m_bBatchExact)) { LONG nDone; DbgLog((LOG_TRACE, 4, TEXT("Batching %d samples"), m_nBatched)); if (m_hr == S_OK) { m_hr = m_pInputPin->ReceiveMultiple(m_ppSamples, m_nBatched, &nDone); } else { nDone = 0; } iLost += m_nBatched - nDone; for (LONG i = 0; i < m_nBatched; i++) { m_ppSamples[i]->Release(); } m_nBatched = 0; } } *nSamplesProcessed = iDone - iLost; if (*nSamplesProcessed < 0) { *nSamplesProcessed = 0; } return m_hr; } else { /* We're sending to our thread */ if (m_hr != S_OK) { *nSamplesProcessed = 0; DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"), nSamples, m_hr)); for (int i = 0; i < nSamples; i++) { ppSamples[i]->Release(); } return m_hr; } m_bFlushed = FALSE; for (long i = 0; i < nSamples; i++) { QueueSample(ppSamples[i]); } *nSamplesProcessed = nSamples; if (!m_bBatchExact || m_nBatched + m_List->GetCount() >= m_lBatchSize) { NotifyThread(); } return S_OK; } }