/*---------------------------------------------------------------------------* Project: Horizon File: socket_SessionPool.h 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: 21822 $ *---------------------------------------------------------------------------*/ #ifndef NN_SOCKET_SOCKET_SOCKET_SESSIONPOOL_H_ #define NN_SOCKET_SOCKET_SOCKET_SESSIONPOOL_H_ #include #include #include #include #include #include #include #include #include #include #include namespace nn { namespace socket{ namespace detail{ class SessionItem: public fnd::IntrusiveLinkedList::Item, public os::ipc::Session { }; class SessionPool { public: typedef fnd::IntrusiveLinkedList SessionList; class ScopedAllocator { public: ScopedAllocator(SessionPool& sessionPool) : m_sessionPool(sessionPool), m_pSessionItem(NULL) { } ~ScopedAllocator() { if (m_pSessionItem) { m_sessionPool.Free(m_pSessionItem); m_pSessionItem = NULL; } } Result Allocate(SessionItem*& pSessionItem) { NN_ASSERT(m_pSessionItem == NULL); Result result = m_sessionPool.Allocate(pSessionItem); NN_UTIL_RETURN_IF_FAILED(result); m_pSessionItem = pSessionItem; return result; } Result TryAllocate(SessionItem*& pSessionItem) { NN_ASSERT(m_pSessionItem == NULL); Result result = m_sessionPool.TryAllocate(pSessionItem); NN_UTIL_RETURN_IF_FAILED(result); m_pSessionItem = pSessionItem; return result; } private: SessionPool& m_sessionPool; SessionItem* m_pSessionItem; }; SessionPool() : m_lock(nn::WithInitialize()) , m_event(false) , m_numActiveSession(0) , m_numFreeSession(0) , m_numInactiveSession(0) , m_numMaxSession(0) , m_bInitialized(false) , m_bFinalizing(false) { } ~SessionPool() { Finalize(); } Result TryInitialize(const char8* pName, size_t nameLen, SessionItem pSessions[], s32 count, s32 initial = 0) { os::CriticalSection::ScopedLock lock(m_lock); Result result; NN_ASSERT(initial <= count); NN_ASSERT(m_numActiveSession == 0); NN_ASSERT(m_listActive.IsEmpty() && m_listInactive.IsEmpty() && m_listFree.IsEmpty()); if (m_bInitialized) { return ResultAlreadyInitialized(); } // サービス名を保存 std::strlcpy(m_name, pName, sizeof(m_name)); m_nameLength = nameLen; // フリーリストにセッションを格納 for (s32 i = 0; i < count; ++i) { m_listFree.PushBack(&pSessions[i]); ++m_numFreeSession; } // フリーリストから初期セッション数だけセッションを得る for (s32 i = 0; i < initial; ++i) { // セッションの取得 result = AddNewSession(); if (result.IsFailure()) { m_lock.Leave(); // 初期セッション数確保できない場合はエラー Finalize(); m_lock.Enter(); return result; } } m_bInitialized = true; m_bFinalizing = false; return ResultSuccess(); } void SemiFinalize(void) { os::CriticalSection::ScopedLock lock(m_lock); m_bFinalizing = true; m_event.Pulse(); while(!m_listInactive.IsEmpty()) // 使用可能 { m_listInactive.PopFront()->Close(); --m_numInactiveSession; //NN_LOG("Finalize f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); } while(!m_listFree.IsEmpty()) // 未接続 { --m_numFreeSession; m_listFree.PopFront()->Close(); //NN_LOG("Finalize f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); } } void Finalize(void) { SemiFinalize(); { os::CriticalSection::ScopedLock lock(m_lock); for(SessionItem* pSessionItem = m_listActive.GetFront(); pSessionItem != NULL; pSessionItem = m_listActive.GetNext(pSessionItem)) { pSessionItem->Close(); } //NN_LOG_DEBUG("Finalize f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); while(true) { NN_ASSERT(m_numActiveSession >= 0); if (m_numActiveSession == 0) { m_bInitialized = false; break; } m_lock.Leave(); nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(100)); m_lock.Enter(); } } SemiFinalize(); NN_ASSERT(m_listActive.IsEmpty() && m_listInactive.IsEmpty() && m_listFree.IsEmpty()); NN_ASSERT(m_numActiveSession == 0); } Result Allocate(SessionItem*& pSessionItem) { Result result; while(true) { result = TryAllocate(pSessionItem); if (result.IsSuccess() || result.GetDescription() != DESCRIPTION_REQUEST_SESSION_FULL) { break; } m_event.Wait(); } return result; } Result TryAllocate(SessionItem*& pSessionItem) { os::CriticalSection::ScopedLock lock(m_lock); //NN_LOG("Allocate enter f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); if (!m_bInitialized || m_bFinalizing) { return ResultNotInitialized(); } // 使用可能セッションが無い場合は、 // 新たに使用可能なセッションを追加 if (m_listInactive.IsEmpty()) { Result result = AddNewSession(); if (result.IsFailure()) { //NN_LOG("Allocate no more session\n"); return result; } } NN_ASSERT(!m_listInactive.IsEmpty()); // 使用可能セッションリストから使用中セッションリストに移動 pSessionItem = m_listInactive.PopFront(); --m_numInactiveSession; m_listActive.PushBack(pSessionItem); ++m_numActiveSession; //NN_LOG("Allocate leave f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); return ResultSuccess(); } void Free(SessionItem* pSessionItem) { os::CriticalSection::ScopedLock lock(m_lock); NN_ASSERT(pSessionItem); NN_ASSERT(m_bInitialized); NN_ASSERT(!m_listActive.IsEmpty()); //NN_LOG("Free enter f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); if (!m_bInitialized) { return; } // 使用中セッションリストから使用可能セッションリストに移動して // 再利用可能にする(ハンドルはそのまま閉じずに使い回す) m_listActive.Erase(pSessionItem); --m_numActiveSession; if (pSessionItem->IsValid()) { m_listInactive.PushBack(pSessionItem); ++m_numInactiveSession; } else { pSessionItem->Close(); m_listFree.PushBack(pSessionItem); ++m_numFreeSession; } m_event.Signal(); //NN_LOG("Free leave f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); } protected: virtual Result InitializingSession(nn::os::ipc::Session&) { return ResultSuccess(); } private: Result AddNewSession(void) { // フリーリストが空の場合は最大数に達しているので、 // 新たにセッションを確立する事はできない if (m_listFree.IsEmpty()) { return ResultRequestSessionFull(); } SessionItem* pSessionItem = m_listFree.GetFront(); NN_ASSERT(!pSessionItem->IsValid()); Result result = srv::GetServiceHandle(pSessionItem, m_name, m_nameLength); if (result.IsFailure()) { return result; } result = InitializingSession(*pSessionItem); if (result.IsFailure()) { pSessionItem->Close(); return result; } NN_ASSERT(!m_listFree.IsEmpty()); m_listFree.PopFront(); --m_numFreeSession; m_listInactive.PushBack(pSessionItem); ++m_numInactiveSession; return ResultSuccess(); } SessionList m_listActive; SessionList m_listInactive; SessionList m_listFree; os::CriticalSection m_lock; os::LightEvent m_event; s32 m_numActiveSession; s32 m_numFreeSession; s32 m_numInactiveSession; s32 m_numMaxSession; size_t m_nameLength; char8 m_name[srv::MAX_SERVICE_NAME_LEN + 1]; bool m_bInitialized; bool m_bFinalizing; u8 padding[1]; }; template class SessionPoolFixed : public SessionPool { public: Result TryInitialize(const char8* pName, size_t nameLen, s32 initial = 0) { return SessionPool::TryInitialize(pName, nameLen, m_Sessions, maxSessions, initial); } private: SessionItem m_Sessions[maxSessions]; }; class SessionPoolAuto : public SessionPool { public: SessionPoolAuto() : m_pSessionItemArray(NULL), m_pAllocator(NULL) { } Result TryInitialize(const char8* pName, size_t nameLen, nn::fnd::IAllocator& allocator, s32 count, s32 initial = 0) { const size_t arraySize = GetRequiredMemorySize(count); m_pSessionItemArray = reinterpret_cast(allocator.Allocate(arraySize, 4)); if (!m_pSessionItemArray) { return ResultOutOfMemory(); } new (m_pSessionItemArray) SessionItem[count]; m_pAllocator = &allocator; Result result; result = SessionPool::TryInitialize(pName, nameLen, m_pSessionItemArray, count, initial); if (result.IsFailure()) { allocator.Free(m_pSessionItemArray); m_pSessionItemArray = NULL; } return result; } void Finalize(void) { NN_ASSERT(m_pSessionItemArray && m_pAllocator); SessionPool::Finalize(); m_pAllocator->Free(m_pSessionItemArray); m_pSessionItemArray = NULL; m_pAllocator = NULL; } static size_t GetRequiredMemorySize(s32 count) { return sizeof(SessionItem) * count; } protected: Result TryInitialize(const char8* pName, size_t nameLen, SessionItem pSessions[], s32 count, s32 initial = 0); private: SessionItem* m_pSessionItemArray; nn::fnd::IAllocator* m_pAllocator; }; #if 0 class SocketSessionPool : public SessionPool { protected: virtual Result InitializingSession(nn::os::ipc::Session& session); }; #endif } // end of namespace detail } // end of namespace socket } // end of namespace nn #endif // NN_SOCKET_SOCKET_SOCKET_SESSIONPOOL_H_