1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: socket_SessionPool.h 4 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. 5 These coded instructions, statements, and computer programs contain 6 proprietary information of Nintendo of America Inc. and/or Nintendo 7 Company Ltd., and are protected by Federal copyright law. They may 8 not be disclosed to third parties or copied or duplicated in any form, 9 in whole or in part, without the prior written consent of Nintendo. 10 $Rev: 21822 $ 11 *--------------------------------------------------------------------------- 12 13 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_ASSERT(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_ASSERT(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_ASSERT(initial <= count); 108 NN_ASSERT(m_numActiveSession == 0); 109 NN_ASSERT(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_ASSERT(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_ASSERT(m_listActive.IsEmpty() 196 && m_listInactive.IsEmpty() 197 && m_listFree.IsEmpty()); 198 NN_ASSERT(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_ASSERT(!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_ASSERT(pSessionItem); 251 NN_ASSERT(m_bInitialized); 252 NN_ASSERT(!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_ASSERT(!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_ASSERT(!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_ASSERT(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