1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: socket_SessionPool.h 4 5 Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. 6 7 These coded instructions, statements, and computer programs contain 8 proprietary information of Nintendo of America Inc. and/or Nintendo 9 Company Ltd., and are protected by Federal copyright law. They may 10 not be disclosed to third parties or copied or duplicated in any form, 11 in whole or in part, without the prior written consent of Nintendo. 12 13 $Rev: 46347 $ 14 *---------------------------------------------------------------------------*/ 15 16 #ifndef NN_SOCKET_SOCKET_SOCKET_SESSIONPOOL_H_ 17 #define NN_SOCKET_SOCKET_SOCKET_SESSIONPOOL_H_ 18 19 #include <nn/os/os_CriticalSection.h> 20 #include <nn/os/os_LightEvent.h> 21 #include <nn/svc.h> 22 #include <nn/srv.h> 23 #include <nn/os/os_Thread.h> 24 #include <nn/os/ipc/os_Session.h> 25 #include <nn/fnd/fnd_LinkedList.h> 26 #include <nn/fnd/fnd_Allocator.h> 27 #include <nn/socket/socket_Result.h> 28 #include <cstring> 29 #include <new> 30 31 namespace nn { 32 namespace socket{ 33 namespace detail{ 34 35 class SessionItem: 36 public fnd::IntrusiveLinkedList<SessionItem>::Item, 37 public os::ipc::Session 38 { 39 }; 40 41 class SessionPool 42 { 43 public: 44 typedef fnd::IntrusiveLinkedList<SessionItem> SessionList; 45 class ScopedAllocator 46 { 47 public: ScopedAllocator(SessionPool & sessionPool)48 ScopedAllocator(SessionPool& sessionPool) 49 : m_sessionPool(sessionPool), m_pSessionItem(NULL) 50 { 51 } ~ScopedAllocator()52 ~ScopedAllocator() 53 { 54 if (m_pSessionItem) 55 { 56 m_sessionPool.Free(m_pSessionItem); 57 m_pSessionItem = NULL; 58 } 59 } 60 Allocate(SessionItem * & pSessionItem)61 Result Allocate(SessionItem*& pSessionItem) 62 { 63 NN_TASSERT_(m_pSessionItem == NULL); 64 Result result = m_sessionPool.Allocate(pSessionItem); 65 NN_UTIL_RETURN_IF_FAILED(result); 66 m_pSessionItem = pSessionItem; 67 return result; 68 } 69 TryAllocate(SessionItem * & pSessionItem)70 Result TryAllocate(SessionItem*& pSessionItem) 71 { 72 NN_TASSERT_(m_pSessionItem == NULL); 73 Result result = m_sessionPool.TryAllocate(pSessionItem); 74 NN_UTIL_RETURN_IF_FAILED(result); 75 m_pSessionItem = pSessionItem; 76 return result; 77 } 78 79 private: 80 SessionPool& m_sessionPool; 81 SessionItem* m_pSessionItem; 82 }; 83 SessionPool()84 SessionPool() 85 : m_lock(nn::WithInitialize()) 86 , m_event(false) 87 , m_numActiveSession(0) 88 , m_numFreeSession(0) 89 , m_numInactiveSession(0) 90 , m_numMaxSession(0) 91 , m_bInitialized(false) 92 , m_bFinalizing(false) 93 94 { 95 } 96 ~SessionPool()97 ~SessionPool() 98 { 99 Finalize(); 100 } 101 102 Result TryInitialize(const char8* pName, size_t nameLen, SessionItem pSessions[], s32 count, s32 initial = 0) 103 { 104 os::CriticalSection::ScopedLock lock(m_lock); 105 Result result; 106 107 NN_TASSERT_(initial <= count); 108 NN_TASSERT_(m_numActiveSession == 0); 109 NN_TASSERT_(m_listActive.IsEmpty() 110 && m_listInactive.IsEmpty() 111 && m_listFree.IsEmpty()); 112 113 if (m_bInitialized) 114 { 115 return ResultAlreadyInitialized(); 116 } 117 118 // Save service name 119 std::strlcpy(m_name, pName, sizeof(m_name)); 120 m_nameLength = nameLen; 121 122 // Store session on free list 123 for (s32 i = 0; i < count; ++i) 124 { 125 m_listFree.PushBack(&pSessions[i]); 126 ++m_numFreeSession; 127 } 128 129 // Get sessions from the free list. The number of sessions obtained is equal the number of initial sessions. 130 for (s32 i = 0; i < initial; ++i) 131 { 132 // Get sessions 133 result = AddNewSession(); 134 if (result.IsFailure()) 135 { 136 m_lock.Leave(); 137 // An error occurs if the number of initial sessions cannot be allocated 138 Finalize(); 139 m_lock.Enter(); 140 return result; 141 } 142 } 143 m_bInitialized = true; 144 m_bFinalizing = false; 145 return ResultSuccess(); 146 } 147 SemiFinalize(void)148 void SemiFinalize(void) 149 { 150 os::CriticalSection::ScopedLock lock(m_lock); 151 m_bFinalizing = true; 152 m_event.Pulse(); 153 while(!m_listInactive.IsEmpty()) // Can be used 154 { 155 m_listInactive.PopFront()->Close(); 156 --m_numInactiveSession; 157 //NN_LOG("Finalize f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); 158 } 159 while(!m_listFree.IsEmpty()) // Not connected 160 { 161 --m_numFreeSession; 162 m_listFree.PopFront()->Close(); 163 //NN_LOG("Finalize f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); 164 } 165 } 166 Finalize(void)167 void Finalize(void) 168 { 169 SemiFinalize(); 170 { 171 os::CriticalSection::ScopedLock lock(m_lock); 172 173 for(SessionItem* pSessionItem = m_listActive.GetFront(); 174 pSessionItem != NULL; 175 pSessionItem = m_listActive.GetNext(pSessionItem)) 176 { 177 178 pSessionItem->Close(); 179 } 180 //NN_LOG_DEBUG("Finalize f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); 181 while(true) 182 { 183 NN_TASSERT_(m_numActiveSession >= 0); 184 if (m_numActiveSession == 0) 185 { 186 m_bInitialized = false; 187 break; 188 } 189 m_lock.Leave(); 190 nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(100)); 191 m_lock.Enter(); 192 } 193 } 194 SemiFinalize(); 195 NN_TASSERT_(m_listActive.IsEmpty() 196 && m_listInactive.IsEmpty() 197 && m_listFree.IsEmpty()); 198 NN_TASSERT_(m_numActiveSession == 0); 199 } 200 Allocate(SessionItem * & pSessionItem)201 Result Allocate(SessionItem*& pSessionItem) 202 { 203 Result result; 204 while(true) 205 { 206 result = TryAllocate(pSessionItem); 207 if (result.IsSuccess() || result.GetDescription() != DESCRIPTION_REQUEST_SESSION_FULL) 208 { 209 break; 210 } 211 m_event.Wait(); 212 } 213 return result; 214 } 215 TryAllocate(SessionItem * & pSessionItem)216 Result TryAllocate(SessionItem*& pSessionItem) 217 { 218 os::CriticalSection::ScopedLock lock(m_lock); 219 //NN_LOG("Allocate enter f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); 220 221 if (!m_bInitialized || m_bFinalizing) 222 { 223 return ResultNotInitialized(); 224 } 225 // If there is no usable session, add a new usable session 226 // 227 if (m_listInactive.IsEmpty()) 228 { 229 Result result = AddNewSession(); 230 if (result.IsFailure()) 231 { 232 //NN_LOG("Allocate no more session\n"); 233 return result; 234 } 235 } 236 NN_TASSERT_(!m_listInactive.IsEmpty()); 237 238 // Move from usable session list to session list in use 239 pSessionItem = m_listInactive.PopFront(); 240 --m_numInactiveSession; 241 m_listActive.PushBack(pSessionItem); 242 ++m_numActiveSession; 243 //NN_LOG("Allocate leave f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); 244 return ResultSuccess(); 245 } 246 Free(SessionItem * pSessionItem)247 void Free(SessionItem* pSessionItem) 248 { 249 os::CriticalSection::ScopedLock lock(m_lock); 250 NN_TASSERT_(pSessionItem); 251 NN_TASSERT_(m_bInitialized); 252 NN_TASSERT_(!m_listActive.IsEmpty()); 253 254 //NN_LOG("Free enter f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); 255 if (!m_bInitialized) 256 { 257 return; 258 } 259 260 // Move from the session list in use to the usable session list and then make usable again (reuse the handle without closing it) 261 // 262 m_listActive.Erase(pSessionItem); 263 --m_numActiveSession; 264 265 if (pSessionItem->IsValid()) 266 { 267 m_listInactive.PushBack(pSessionItem); 268 ++m_numInactiveSession; 269 } 270 else 271 { 272 pSessionItem->Close(); 273 m_listFree.PushBack(pSessionItem); 274 ++m_numFreeSession; 275 } 276 m_event.Signal(); 277 //NN_LOG("Free leave f=%d i=%d a=%d\n", m_numFreeSession, m_numInactiveSession, m_numActiveSession); 278 } 279 280 protected: InitializingSession(nn::os::ipc::Session &)281 virtual Result InitializingSession(nn::os::ipc::Session&) 282 { 283 return ResultSuccess(); 284 } 285 286 private: AddNewSession(void)287 Result AddNewSession(void) 288 { 289 // If the free list is empty, then the maximum number has been reached. Accordingly, a new session cannot be established 290 // 291 if (m_listFree.IsEmpty()) 292 { 293 return ResultRequestSessionFull(); 294 } 295 296 SessionItem* pSessionItem = m_listFree.GetFront(); 297 NN_TASSERT_(!pSessionItem->IsValid()); 298 Result result = srv::GetServiceHandle(pSessionItem, m_name, m_nameLength); 299 if (result.IsFailure()) 300 { 301 return result; 302 } 303 304 result = InitializingSession(*pSessionItem); 305 if (result.IsFailure()) 306 { 307 pSessionItem->Close(); 308 return result; 309 } 310 311 NN_TASSERT_(!m_listFree.IsEmpty()); 312 313 m_listFree.PopFront(); 314 --m_numFreeSession; 315 m_listInactive.PushBack(pSessionItem); 316 ++m_numInactiveSession; 317 return ResultSuccess(); 318 } 319 320 SessionList m_listActive; 321 SessionList m_listInactive; 322 SessionList m_listFree; 323 os::CriticalSection m_lock; 324 os::LightEvent m_event; 325 326 s32 m_numActiveSession; 327 s32 m_numFreeSession; 328 s32 m_numInactiveSession; 329 330 331 s32 m_numMaxSession; 332 size_t m_nameLength; 333 char8 m_name[srv::MAX_SERVICE_NAME_LEN + 1]; 334 bool m_bInitialized; 335 bool m_bFinalizing; 336 u8 padding[1]; 337 }; 338 339 template<size_t maxSessions> 340 class SessionPoolFixed : public SessionPool 341 { 342 public: 343 Result TryInitialize(const char8* pName, size_t nameLen, s32 initial = 0) 344 { 345 return SessionPool::TryInitialize(pName, nameLen, m_Sessions, maxSessions, initial); 346 } 347 348 private: 349 SessionItem m_Sessions[maxSessions]; 350 }; 351 352 class SessionPoolAuto : public SessionPool 353 { 354 public: SessionPoolAuto()355 SessionPoolAuto() 356 : m_pSessionItemArray(NULL), m_pAllocator(NULL) 357 { 358 } 359 360 Result TryInitialize(const char8* pName, size_t nameLen, nn::fnd::IAllocator& allocator, s32 count, s32 initial = 0) 361 { 362 const size_t arraySize = GetRequiredMemorySize(count); 363 m_pSessionItemArray = reinterpret_cast<SessionItem*>(allocator.Allocate(arraySize, 4)); 364 if (!m_pSessionItemArray) 365 { 366 return ResultOutOfMemory(); 367 } 368 369 new (m_pSessionItemArray) SessionItem[count]; 370 m_pAllocator = &allocator; 371 372 Result result; 373 result = SessionPool::TryInitialize(pName, nameLen, m_pSessionItemArray, count, initial); 374 if (result.IsFailure()) 375 { 376 allocator.Free(m_pSessionItemArray); 377 m_pSessionItemArray = NULL; 378 } 379 return result; 380 } 381 Finalize(void)382 void Finalize(void) 383 { 384 NN_TASSERT_(m_pSessionItemArray && m_pAllocator); 385 SessionPool::Finalize(); 386 m_pAllocator->Free(m_pSessionItemArray); 387 m_pSessionItemArray = NULL; 388 m_pAllocator = NULL; 389 } 390 GetRequiredMemorySize(s32 count)391 static size_t GetRequiredMemorySize(s32 count) 392 { 393 return sizeof(SessionItem) * count; 394 } 395 396 protected: 397 Result TryInitialize(const char8* pName, size_t nameLen, SessionItem pSessions[], s32 count, s32 initial = 0); 398 399 private: 400 SessionItem* m_pSessionItemArray; 401 nn::fnd::IAllocator* m_pAllocator; 402 }; 403 404 #if 0 405 class SocketSessionPool : public SessionPool 406 { 407 protected: 408 virtual Result InitializingSession(nn::os::ipc::Session& session); 409 }; 410 #endif 411 412 } // end of namespace detail 413 } // end of namespace socket 414 } // end of namespace nn 415 416 #endif // NN_SOCKET_SOCKET_SOCKET_SESSIONPOOL_H_ 417