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