/*---------------------------------------------------------------------------* 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: 33014 $ *---------------------------------------------------------------------------*/ #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) { // キューにデータ挿入中に他のスレッドから操作されないようにロックします。 ScopedLock locker(m_cs); if (m_size > m_usedCount) { s32 lastIndex = (m_firstIndex + m_usedCount) % m_size; m_ppBuffer[lastIndex] = data; m_usedCount++; // データ挿入待ちスレッドを起床します。 NotifyEnqueue(); return true; } else { return false; } } template bool InterCoreBlockingQueueBase::ForceEnqueue(uptr data, uptr* pOut) { // キューにデータを挿入中に他のスレッドから操作されないようにロックします。 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; // データ挿入待ちスレッドを起床します。 NotifyEnqueue(); return bReturn; } template void InterCoreBlockingQueueBase::Enqueue(uptr data) { // キューに挿入処理中のスレッド数を更新します。 ++m_WaitingDequeueCount; DataSynchronizationBarrier(); for(;;) { if (TryEnqueue(data)) { break; } // キューに空きができるまで待機します。 m_DequeueSemaphore.Acquire(); } --m_WaitingDequeueCount; DataSynchronizationBarrier(); } template bool InterCoreBlockingQueueBase::TryJam(uptr data) { // キューにデータを挿入中に他のスレッドから操作されないようにロックします。 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++; // データ挿入待ちスレッドを起床します。 NotifyEnqueue(); return true; } else { return false; } } template void InterCoreBlockingQueueBase::Jam(uptr data) { // キューに挿入処理中のスレッド数を更新します。 ++m_WaitingDequeueCount; DataSynchronizationBarrier(); for(;;) { if (TryJam(data)) { break; } // キューに空きができるまで待機します。 m_DequeueSemaphore.Acquire(); } --m_WaitingDequeueCount; DataSynchronizationBarrier(); } template bool InterCoreBlockingQueueBase::TryDequeue(uptr* pOut) { // キューからデータを取り出し中に他のスレッドから操作されないようにロックします。 ScopedLock locker(m_cs); if (0 < m_usedCount) { *pOut = m_ppBuffer[m_firstIndex]; m_firstIndex = (m_firstIndex + 1) % m_size; m_usedCount--; // キュー空き待ちスレッドを起床します。 NotifyDequeue(); return true; } else { return false; } } template uptr InterCoreBlockingQueueBase::Dequeue() { // キューから取り出し処理中のスレッド数を更新します。 ++m_WaitingEnqueueCount; DataSynchronizationBarrier(); uptr data; for(;;) { if (TryDequeue(&data)) { break; } // キューの内容が空の場合は待機します。 m_EnqueueSemaphore.Acquire(); } --m_WaitingEnqueueCount; DataSynchronizationBarrier(); return data; } template bool InterCoreBlockingQueueBase::TryGetFront(uptr* pOut) const { // キューからデータを取り出し中に他のスレッドから操作されないようにロックします。 ScopedLock locker(m_cs); if (0 < m_usedCount) { *pOut = m_ppBuffer[m_firstIndex]; return true; } else { return false; } } template uptr InterCoreBlockingQueueBase::GetFront() const { // キューから取り出し処理中のスレッド数を更新します。 ++m_WaitingEnqueueCount; DataSynchronizationBarrier(); uptr data; for(;;) { if (TryGetFront(&data)) { break; } // キューの内容が空の場合は待機します。 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(); } }