1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: osl_IpcIpcDispatcher.h 4 5 Copyright (C)2009 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: 13955 $ 14 *---------------------------------------------------------------------------*/ 15 16 #ifndef NN_NET_OSL_OSL_IPCDISPATCHER_H_ 17 #define NN_NET_OSL_OSL_IPCDISPATCHER_H_ 18 19 #include <new> 20 #include <nn/srv.h> 21 #include <nn/os/os_Thread.h> 22 #include <nn/os/os_CriticalSection.h> 23 #include <nn/os/ipc/os_Port.h> 24 #include <nn/os/ipc/os_Session.h> 25 #include <nn/fnd/fnd_LinkedList.h> 26 #include <nn/fnd/fnd_Allocator.h> 27 28 namespace nn { 29 namespace net { 30 namespace osl { 31 32 class IpcDispatcherBase : public os::ipc::Port 33 { 34 public: 35 virtual Result Launch() = 0; 36 }; 37 38 class IpcDispatcherExecutor 39 { 40 public: 41 static const size_t DISPATCHERS_MAX = 8; 42 static const size_t WAITOBJECTS_MAX = 8; IpcDispatcherExecutor(IpcDispatcherBase * ppDispatchers[],s32 countDispatchers)43 IpcDispatcherExecutor(IpcDispatcherBase* ppDispatchers[], s32 countDispatchers) 44 : m_countDispatchers(countDispatchers) 45 { 46 NN_MIN_ASSERT(countDispatchers, 0); 47 NN_MAX_ASSERT(countDispatchers, DISPATCHERS_MAX - 1); 48 49 for (s32 i = 0; i < m_countDispatchers; ++i) 50 { 51 NN_POINTER_ASSERT(ppDispatchers[i]); 52 m_ppDispatchers[i] = ppDispatchers[i]; 53 } 54 } 55 IpcDispatcherExecutor(IpcDispatcherBase * pDispatcher)56 IpcDispatcherExecutor(IpcDispatcherBase* pDispatcher) 57 : m_countDispatchers(1) 58 { 59 NN_POINTER_ASSERT(pDispatcher); 60 m_ppDispatchers[0] = pDispatcher; 61 } 62 WaitAny(nn::os::WaitObject * ppObjects[],s32 countWaitObjects)63 s32 WaitAny(nn::os::WaitObject* ppObjects[], s32 countWaitObjects) 64 { 65 NN_MIN_ASSERT(countWaitObjects, 0); 66 NN_MAX_ASSERT(countWaitObjects, WAITOBJECTS_MAX - 1); 67 68 nn::os::WaitObject* ppObjectsToWait[DISPATCHERS_MAX + WAITOBJECTS_MAX]; 69 70 for (s32 i = 0; i < m_countDispatchers; ++i) 71 { 72 NN_POINTER_ASSERT(m_ppDispatchers[i]); 73 ppObjectsToWait[i] = m_ppDispatchers[i]; 74 } 75 for (s32 i = 0; i < countWaitObjects; ++i) 76 { 77 NN_POINTER_ASSERT(ppObjects[i]); 78 ppObjectsToWait[m_countDispatchers + i] = ppObjects[i]; 79 } 80 81 s32 index; 82 while(true) 83 { 84 index = nn::os::WaitObject::WaitAny(ppObjectsToWait, m_countDispatchers + countWaitObjects); 85 if (index < m_countDispatchers) 86 { 87 Result result = m_ppDispatchers[index]->Launch(); 88 if (result.IsFailure()) 89 { 90 NN_LOG_WARN("IPC dispatch failed.\n"); 91 nn::dbg::PrintResult(result); 92 } 93 } 94 else 95 { 96 return index - m_countDispatchers; 97 } 98 } 99 } 100 101 private: 102 IpcDispatcherBase* m_ppDispatchers[DISPATCHERS_MAX]; 103 s32 m_countDispatchers; 104 s32 m_countWaitObjects; 105 s32 m_indexNotification; 106 }; 107 108 template <size_t StackSize, typename ImplT, Result (*pSessionLoopFunction)(s32*, ImplT*, Handle*, s32, s32)> 109 class IpcDispatcher : public IpcDispatcherBase 110 { 111 typedef IpcDispatcher<StackSize, ImplT, pSessionLoopFunction> Base; 112 class Worker : public fnd::IntrusiveLinkedList<Worker>::Item 113 { 114 public: Worker(IpcDispatcher * pDispatcher)115 Worker(IpcDispatcher* pDispatcher) 116 : m_pDispatcher(pDispatcher) 117 , m_bInitialized(false) 118 { 119 }; 120 ~Worker()121 ~Worker() 122 { 123 if (m_bInitialized) 124 { 125 Finalize(); 126 } 127 } 128 IsInitialized(void)129 inline bool IsInitialized(void) const 130 { 131 return m_bInitialized; 132 } 133 134 Result TryInitialize(os::ipc::Port& port, s32 priority = os::DEFAULT_THREAD_PRIORITY) 135 { 136 NN_ASSERT(!m_bInitialized); 137 138 Result result; 139 140 result = port.TryAccept(&m_session, false); 141 NN_UTIL_RETURN_IF_FAILED(result); 142 143 result = m_thread.TryStart<Worker*, Worker*, StackT>(SessionThread, this, m_stack, priority); 144 if (result.IsFailure()) 145 { 146 m_session.Close(); 147 return result; 148 } 149 150 m_bInitialized = true; 151 return ResultSuccess(); 152 } 153 Finalize(void)154 void Finalize(void) 155 { 156 NN_ASSERT(m_bInitialized); 157 Wait(); 158 m_thread.Finalize(); 159 m_bInitialized = false; 160 } 161 Wait(void)162 void Wait(void) 163 { 164 m_thread.Join(); 165 } 166 new(size_t size,nn::fnd::IAllocator & allocator)167 static void* operator new(size_t size, nn::fnd::IAllocator& allocator) throw() 168 { 169 return allocator.Allocate(size, sizeof(u64)); 170 } 171 delete(void * p)172 static void operator delete(void* p) throw() 173 { 174 NN_ASSERT(p && reinterpret_cast<Worker*>(p)->m_pDispatcher); 175 reinterpret_cast<Worker*>(p)->m_pDispatcher->m_allocator.Free(p); 176 } 177 178 private: SessionThread(Worker * pWorker)179 static void SessionThread(Worker* pWorker) 180 { 181 pWorker->SessionThreadImpl(); 182 } 183 SessionThreadImpl(void)184 void SessionThreadImpl(void) 185 { 186 ImplT* pImpl = reinterpret_cast<ImplT*>(&m_implStorage); 187 new (pImpl) ImplT(); 188 { 189 s32 resultIndex; 190 Handle handles[] = { m_session.GetHandle() }; 191 Result result = pSessionLoopFunction(&resultIndex, pImpl, handles, 1, 0); 192 if (result.IsFailure() && result.GetDescription() != nn::os::DESCRIPTION_SESSION_CLOSED) 193 { 194 NN_UTIL_PANIC_IF_FAILED(result); 195 } 196 } 197 pImpl->~ImplT(); 198 199 m_session.Close(); 200 m_pDispatcher->TakeBackFreeWorker(this); 201 } 202 203 typedef nn::os::StackBuffer<StackSize> StackT; 204 205 StackT m_stack; 206 u64 m_implStorage[sizeof(ImplT)/sizeof(u64) + 1]; 207 nn::os::Thread m_thread; 208 nn::os::ipc::Session m_session; 209 IpcDispatcher* m_pDispatcher; 210 bool m_bInitialized; 211 u8 padding[7]; 212 }; 213 214 typedef fnd::IntrusiveLinkedList<Worker> WorkerList; 215 friend class Worker; 216 217 public: 218 IpcDispatcher(const char* pServiceName, 219 nn::fnd::IAllocator& allocator, 220 s32 maxThreads = 1, s32 priority = os::DEFAULT_THREAD_PRIORITY) m_cs(nn::WithInitialize ())221 : m_cs(nn::WithInitialize()) 222 , m_allocator(allocator) 223 , m_maxThreads(maxThreads) 224 , m_priority(priority) 225 , m_pServiceName(pServiceName) 226 { 227 Result result; 228 Handle hPort; 229 230 ConstructWorkerList(maxThreads); 231 232 NN_LOG_INFO("Registering service: %s\n", pServiceName); 233 result = nn::srv::RegisterService(&hPort, pServiceName); 234 NN_UTIL_PANIC_IF_FAILED(result); 235 236 WaitObject::SetHandle(hPort); 237 } 238 ~IpcDispatcher()239 ~IpcDispatcher() 240 { 241 if (IsValid()) 242 { 243 Finalize(); 244 } 245 } 246 Finalize(void)247 void Finalize(void) 248 { 249 Result result; 250 251 DestructWorkerList(); 252 253 result = nn::srv::UnregisterService(m_pServiceName); 254 NN_UTIL_PANIC_IF_FAILED(result); 255 256 WaitObject::Close(); 257 } 258 Launch(void)259 virtual Result Launch(void) 260 { 261 NN_LOG_INFO("Accepting %s\n", m_pServiceName); 262 263 Worker* pWorker = GetFreeWorker(); 264 if (!pWorker) 265 { 266 NN_LOG_WARN("No free worker.\n"); 267 nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(1000)); 268 return nn::MakeStatusResult(nn::Result::SUMMARY_OUT_OF_RESOURCE, nn::Result::MODULE_NN_OS, nn::Result::DESCRIPTION_OUT_OF_MEMORY); 269 } 270 271 Result result = pWorker->TryInitialize(*this, m_priority); 272 if (result.IsFailure()) 273 { 274 TakeBackFreeWorker(pWorker); 275 return result; 276 } 277 return ResultSuccess(); 278 } 279 280 private: GetFreeWorker(void)281 Worker* GetFreeWorker(void) 282 { 283 os::CriticalSection::ScopedLock locker(m_cs); 284 if (m_freeList.IsEmpty()) 285 { 286 return NULL; 287 } 288 Worker* pWorker = m_freeList.PopFront(); 289 m_activeList.PushBack(pWorker); 290 291 // 使用済みなら一回 Finalized しておく 292 if (pWorker->IsInitialized()) 293 { 294 pWorker->Finalize(); 295 } 296 return pWorker; 297 } 298 TakeBackFreeWorker(Worker * pWorker)299 void TakeBackFreeWorker(Worker* pWorker) 300 { 301 os::CriticalSection::ScopedLock locker(m_cs); 302 303 // このコンテキストで Join するとデッドロックするので改めて使うときに Finalize する 304 m_activeList.Erase(pWorker); 305 m_freeList.PushBack(pWorker); 306 } 307 ConstructWorkerList(s32 maxThreads)308 void ConstructWorkerList(s32 maxThreads) 309 { 310 NN_ASSERT(maxThreads > 0); 311 312 for(s32 i = 0; i < maxThreads; ++i) 313 { 314 Worker* pWorker = new (m_allocator) Worker(this); 315 if (pWorker == NULL) 316 { 317 NN_LOG_WARN("Failed to allocate memory for Woker.(%d/%d)", i, maxThreads); 318 NN_ASSERT(pWorker); 319 break; 320 } 321 m_freeList.PushBack(pWorker); 322 } 323 } 324 DestructWorkerList(void)325 void DestructWorkerList(void) 326 { 327 os::CriticalSection::ScopedLock locker(m_cs); 328 329 while(!m_freeList.IsEmpty() || !m_activeList.IsEmpty()) 330 { 331 while(!m_freeList.IsEmpty()) 332 { 333 Worker* pWorker = m_freeList.PopFront(); 334 delete pWorker; 335 } 336 337 if(!m_activeList.IsEmpty()) 338 { 339 Worker* pWorker = m_activeList.GetFront(); 340 { 341 m_cs.Leave(); 342 pWorker->Wait(); 343 m_cs.Enter(); 344 } 345 } 346 } 347 348 } 349 nn::os::CriticalSection m_cs; 350 nn::fnd::IAllocator& m_allocator; 351 WorkerList m_freeList; 352 WorkerList m_activeList; 353 s32 m_maxThreads; 354 s32 m_priority; 355 const char* m_pServiceName; 356 }; 357 358 } // end of namespace osl 359 } // end of namespace net 360 } // end of namespace nn 361 362 #endif // NN_NET_OSL_OSL_IPCDISPATCHER_H_ 363