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