/*---------------------------------------------------------------------------* Project: Horizon File: os_InterCoreBlockingQueue.cpp Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 33107 $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- using namespace nn; using namespace nn::fnd; using namespace nn::svc; using namespace nn::os; using namespace nn::util; namespace nn{ namespace os{ namespace detail { template InterCoreBlockingQueueBase::~InterCoreBlockingQueueBase() { NN_TASSERT_(m_WaitingEnqueueCount == 0 && m_WaitingDequeueCount == 0); Finalize(); } template void InterCoreBlockingQueueBase::Initialize(uptr buffer[], size_t size) { m_ppBuffer = buffer; m_size = size; m_firstIndex = 0; m_usedCount = 0; m_WaitingEnqueueCount = 0; m_WaitingDequeueCount = 0; m_EnqueueSemaphore.Initialize(0); m_DequeueSemaphore.Initialize(0); m_cs.Initialize(); } template nn::Result InterCoreBlockingQueueBase::TryInitialize(uptr buffer[], size_t size) { m_ppBuffer = buffer; m_size = size; m_firstIndex = 0; m_usedCount = 0; m_WaitingEnqueueCount = 0; m_WaitingDequeueCount = 0; m_EnqueueSemaphore.Initialize(0); m_DequeueSemaphore.Initialize(0); NN_UTIL_RETURN_IF_FAILED(m_cs.TryInitialize()); return ResultSuccess(); } template void InterCoreBlockingQueueBase::Finalize() { m_cs.Finalize(); m_DequeueSemaphore.Finalize(); m_EnqueueSemaphore.Finalize(); } template inline void InterCoreBlockingQueueBase::NotifyEnqueue() const { if (m_WaitingEnqueueCount > 0) { m_EnqueueSemaphore.Release(); } } template inline void InterCoreBlockingQueueBase::NotifyDequeue() const { if (m_WaitingDequeueCount > 0) { m_DequeueSemaphore.Release(); } } template bool InterCoreBlockingQueueBase::TryEnqueue(uptr data) { // Locks while inserting data in the queue so that other threads cannot perform operations. ScopedLock locker(m_cs); if (m_size > m_usedCount) { s32 lastIndex = (m_firstIndex + m_usedCount) % m_size; m_ppBuffer[lastIndex] = data; m_usedCount++; // Wakes up thread waiting for data insertion. NotifyEnqueue(); return true; } else { return false; } } template bool InterCoreBlockingQueueBase::ForceEnqueue(uptr data, uptr* pOut) { // Locks while inserting data in the queue so that other threads cannot perform operations. ScopedLock locker(m_cs); bool bReturn; s32 lastIndex = (m_firstIndex + m_usedCount) % m_size; if (m_size > m_usedCount) { m_usedCount++; bReturn = true; } else { if (pOut) { *pOut = m_ppBuffer[lastIndex]; } m_firstIndex = (m_firstIndex + 1) % m_size; bReturn = false; } m_ppBuffer[lastIndex] = data; // Wakes up thread waiting for data insertion. NotifyEnqueue(); return bReturn; } template void InterCoreBlockingQueueBase::Enqueue(uptr data) { // Updates the number of threads during the process to insert in queue. ++m_WaitingDequeueCount; DataSynchronizationBarrier(); for(;;) { if (TryEnqueue(data)) { break; } // Waits until there is space in the queue. m_DequeueSemaphore.Acquire(); } --m_WaitingDequeueCount; DataSynchronizationBarrier(); } template bool InterCoreBlockingQueueBase::TryJam(uptr data) { // Locks while inserting data in the queue so that other threads cannot perform operations. ScopedLock locker(m_cs); if (m_size > m_usedCount) { m_firstIndex = (m_firstIndex + m_size - 1) % m_size; m_ppBuffer[m_firstIndex] = data; m_usedCount++; // Wakes up thread waiting for data insertion. NotifyEnqueue(); return true; } else { return false; } } template void InterCoreBlockingQueueBase::Jam(uptr data) { // Updates the number of threads during the process to insert in queue. ++m_WaitingDequeueCount; DataSynchronizationBarrier(); for(;;) { if (TryJam(data)) { break; } // Waits until there is space in the queue. m_DequeueSemaphore.Acquire(); } --m_WaitingDequeueCount; DataSynchronizationBarrier(); } template bool InterCoreBlockingQueueBase::TryDequeue(uptr* pOut) { // Locks while retrieving data from the queue so that other threads cannot perform operations. ScopedLock locker(m_cs); if (0 < m_usedCount) { *pOut = m_ppBuffer[m_firstIndex]; m_firstIndex = (m_firstIndex + 1) % m_size; m_usedCount--; // Wakes up the thread waiting for a space in the queue. NotifyDequeue(); return true; } else { return false; } } template uptr InterCoreBlockingQueueBase::Dequeue() { // Updates the number of threads during the process to retrieve data from the queue. ++m_WaitingEnqueueCount; DataSynchronizationBarrier(); uptr data; for(;;) { if (TryDequeue(&data)) { break; } // Waits when the queue content is empty. m_EnqueueSemaphore.Acquire(); } --m_WaitingEnqueueCount; DataSynchronizationBarrier(); return data; } template bool InterCoreBlockingQueueBase::TryGetFront(uptr* pOut) const { // Locks while retrieving data from the queue so that other threads cannot perform operations. ScopedLock locker(m_cs); if (0 < m_usedCount) { *pOut = m_ppBuffer[m_firstIndex]; return true; } else { return false; } } template uptr InterCoreBlockingQueueBase::GetFront() const { // Updates the number of threads during the process to retrieve data from the queue. ++m_WaitingEnqueueCount; DataSynchronizationBarrier(); uptr data; for(;;) { if (TryGetFront(&data)) { break; } // Waits when the queue content is empty. m_EnqueueSemaphore.Acquire(); } --m_WaitingEnqueueCount; DataSynchronizationBarrier(); return data; } template class InterCoreBlockingQueueBase; } // namespace detail }} // namespace nn::os #include using namespace nn::os; extern "C" { void nnosInterCoreBlockingQueueInitialize(nnosInterCoreBlockingQueue* this_, uptr buffer[], size_t size) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); new (pInterCoreBlockingQueue) InterCoreBlockingQueue(); pInterCoreBlockingQueue->Initialize(buffer, size); } bool nnosInterCoreBlockingQueueTryInitialize(nnosInterCoreBlockingQueue* this_, uptr buffer[], size_t size) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); new (pInterCoreBlockingQueue) InterCoreBlockingQueue(); Result result = pInterCoreBlockingQueue->TryInitialize(buffer, size); return result.IsSuccess(); } void nnosInterCoreBlockingQueueFinalize(nnosInterCoreBlockingQueue* this_) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); pInterCoreBlockingQueue->Finalize(); pInterCoreBlockingQueue->~InterCoreBlockingQueue(); } bool nnosInterCoreBlockingQueueTryEnqueue(nnosInterCoreBlockingQueue* this_, uptr data) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); return pInterCoreBlockingQueue->TryEnqueue(data); } void nnosInterCoreBlockingQueueEnqueue(nnosInterCoreBlockingQueue* this_, uptr data) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); pInterCoreBlockingQueue->Enqueue(data); } bool nnosInterCoreBlockingQueueTryJam(nnosInterCoreBlockingQueue* this_, uptr data) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); return pInterCoreBlockingQueue->TryJam(data); } void nnosInterCoreBlockingQueueJam(nnosInterCoreBlockingQueue* this_, uptr data) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); pInterCoreBlockingQueue->Jam(data); } bool nnosInterCoreBlockingQueueTryDequeue(nnosInterCoreBlockingQueue* this_, uptr* pOut) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); return pInterCoreBlockingQueue->TryDequeue(pOut); } uptr nnosInterCoreBlockingQueueDequeue(nnosInterCoreBlockingQueue* this_) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); return pInterCoreBlockingQueue->Dequeue(); } bool nnosInterCoreBlockingQueueTryGetFront(nnosInterCoreBlockingQueue* this_, uptr* pOut) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); return pInterCoreBlockingQueue->TryGetFront(pOut); } uptr nnosInterCoreBlockingQueueGetFront(nnosInterCoreBlockingQueue* this_) { InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast(this_); return pInterCoreBlockingQueue->GetFront(); } }