1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_InterCoreBlockingQueue.cpp
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: 33014 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nn/assert.h>
17 #include <nn/config.h>
18 #include <nn/util/util_Result.h>
19 #include <nn/os/os_InterCoreBlockingQueue.h>
20 #include <nn/os/os_InterCoreCriticalSection.h>
21 #include <nn/fnd/fnd_Interlocked.h>
22 #include <nn/util/detail/util_InitializationTransaction.h>
23 
24 #include <climits>
25 
26 //---------------------------------------------------------------------------
27 
28 using namespace nn;
29 using namespace nn::fnd;
30 using namespace nn::svc;
31 using namespace nn::os;
32 using namespace nn::util;
33 
34 namespace nn{ namespace os{
35 
36 namespace detail {
37 
38 template <class Locker>
~InterCoreBlockingQueueBase()39 InterCoreBlockingQueueBase<Locker>::~InterCoreBlockingQueueBase()
40 {
41     NN_TASSERT_(m_WaitingEnqueueCount == 0 && m_WaitingDequeueCount == 0);
42     Finalize();
43 }
44 
45 template <class Locker>
Initialize(uptr buffer[],size_t size)46 void InterCoreBlockingQueueBase<Locker>::Initialize(uptr buffer[], size_t size)
47 {
48     m_ppBuffer      = buffer;
49     m_size          = size;
50     m_firstIndex    = 0;
51     m_usedCount     = 0;
52     m_WaitingEnqueueCount = 0;
53     m_WaitingDequeueCount = 0;
54     m_EnqueueSemaphore.Initialize(0);
55     m_DequeueSemaphore.Initialize(0);
56     m_cs.Initialize();
57 }
58 
59 template <class Locker>
TryInitialize(uptr buffer[],size_t size)60 nn::Result InterCoreBlockingQueueBase<Locker>::TryInitialize(uptr buffer[], size_t size)
61 {
62     m_ppBuffer      = buffer;
63     m_size          = size;
64     m_firstIndex    = 0;
65     m_usedCount     = 0;
66     m_WaitingEnqueueCount = 0;
67     m_WaitingDequeueCount = 0;
68 
69     m_EnqueueSemaphore.Initialize(0);
70     m_DequeueSemaphore.Initialize(0);
71 
72     NN_UTIL_RETURN_IF_FAILED(m_cs.TryInitialize());
73     return ResultSuccess();
74 }
75 
76 template <class Locker>
Finalize()77 void InterCoreBlockingQueueBase<Locker>::Finalize()
78 {
79     m_cs.Finalize();
80     m_DequeueSemaphore.Finalize();
81     m_EnqueueSemaphore.Finalize();
82 }
83 
84 
85 template <class Locker>
NotifyEnqueue() const86 inline void InterCoreBlockingQueueBase<Locker>::NotifyEnqueue() const
87 {
88     if (m_WaitingEnqueueCount > 0)
89     {
90         m_EnqueueSemaphore.Release();
91     }
92 }
93 
94 template <class Locker>
NotifyDequeue() const95 inline void InterCoreBlockingQueueBase<Locker>::NotifyDequeue() const
96 {
97     if (m_WaitingDequeueCount > 0)
98     {
99         m_DequeueSemaphore.Release();
100     }
101 }
102 
103 template <class Locker>
TryEnqueue(uptr data)104 bool InterCoreBlockingQueueBase<Locker>::TryEnqueue(uptr data)
105 {
106     // キューにデータ挿入中に他のスレッドから操作されないようにロックします。
107     ScopedLock locker(m_cs);
108 
109     if (m_size > m_usedCount)
110     {
111         s32 lastIndex = (m_firstIndex + m_usedCount) % m_size;
112         m_ppBuffer[lastIndex] = data;
113         m_usedCount++;
114 
115         // データ挿入待ちスレッドを起床します。
116         NotifyEnqueue();
117         return true;
118     }
119     else
120     {
121         return false;
122     }
123 }
124 
125 template <class Locker>
ForceEnqueue(uptr data,uptr * pOut)126 bool InterCoreBlockingQueueBase<Locker>::ForceEnqueue(uptr data, uptr* pOut)
127 {
128     // キューにデータを挿入中に他のスレッドから操作されないようにロックします。
129     ScopedLock locker(m_cs);
130     bool bReturn;
131     s32 lastIndex = (m_firstIndex + m_usedCount) % m_size;
132     if (m_size > m_usedCount)
133     {
134         m_usedCount++;
135         bReturn = true;
136     }
137     else
138     {
139         if (pOut)
140         {
141             *pOut = m_ppBuffer[lastIndex];
142         }
143         m_firstIndex = (m_firstIndex + 1) % m_size;
144         bReturn = false;
145     }
146 
147     m_ppBuffer[lastIndex] = data;
148     // データ挿入待ちスレッドを起床します。
149     NotifyEnqueue();
150     return bReturn;
151 }
152 
153 template <class Locker>
Enqueue(uptr data)154 void InterCoreBlockingQueueBase<Locker>::Enqueue(uptr data)
155 {
156     // キューに挿入処理中のスレッド数を更新します。
157     ++m_WaitingDequeueCount;
158     DataSynchronizationBarrier();
159     for(;;)
160     {
161         if (TryEnqueue(data))
162         {
163             break;
164         }
165 
166         // キューに空きができるまで待機します。
167         m_DequeueSemaphore.Acquire();
168     }
169     --m_WaitingDequeueCount;
170     DataSynchronizationBarrier();
171 }
172 
173 template <class Locker>
TryJam(uptr data)174 bool InterCoreBlockingQueueBase<Locker>::TryJam(uptr data)
175 {
176     // キューにデータを挿入中に他のスレッドから操作されないようにロックします。
177     ScopedLock locker(m_cs);
178 
179     if (m_size > m_usedCount)
180     {
181         m_firstIndex = (m_firstIndex + m_size - 1) % m_size;
182         m_ppBuffer[m_firstIndex] = data;
183         m_usedCount++;
184 
185         // データ挿入待ちスレッドを起床します。
186         NotifyEnqueue();
187         return true;
188     }
189     else
190     {
191         return false;
192     }
193 }
194 
195 template <class Locker>
Jam(uptr data)196 void InterCoreBlockingQueueBase<Locker>::Jam(uptr data)
197 {
198     // キューに挿入処理中のスレッド数を更新します。
199     ++m_WaitingDequeueCount;
200     DataSynchronizationBarrier();
201     for(;;)
202     {
203         if (TryJam(data))
204         {
205             break;
206         }
207 
208         // キューに空きができるまで待機します。
209         m_DequeueSemaphore.Acquire();
210     }
211     --m_WaitingDequeueCount;
212     DataSynchronizationBarrier();
213 }
214 
215 template <class Locker>
TryDequeue(uptr * pOut)216 bool InterCoreBlockingQueueBase<Locker>::TryDequeue(uptr* pOut)
217 {
218     // キューからデータを取り出し中に他のスレッドから操作されないようにロックします。
219     ScopedLock locker(m_cs);
220 
221     if (0 < m_usedCount)
222     {
223         *pOut = m_ppBuffer[m_firstIndex];
224         m_firstIndex = (m_firstIndex + 1) % m_size;
225         m_usedCount--;
226 
227         // キュー空き待ちスレッドを起床します。
228         NotifyDequeue();
229         return true;
230     }
231     else
232     {
233         return false;
234     }
235 }
236 
237 template <class Locker>
Dequeue()238 uptr InterCoreBlockingQueueBase<Locker>::Dequeue()
239 {
240     // キューから取り出し処理中のスレッド数を更新します。
241     ++m_WaitingEnqueueCount;
242     DataSynchronizationBarrier();
243     uptr data;
244     for(;;)
245     {
246         if (TryDequeue(&data))
247         {
248             break;
249         }
250 
251         // キューの内容が空の場合は待機します。
252         m_EnqueueSemaphore.Acquire();
253     }
254     --m_WaitingEnqueueCount;
255     DataSynchronizationBarrier();
256     return data;
257 }
258 
259 template <class Locker>
TryGetFront(uptr * pOut) const260 bool InterCoreBlockingQueueBase<Locker>::TryGetFront(uptr* pOut) const
261 {
262     // キューからデータを取り出し中に他のスレッドから操作されないようにロックします。
263     ScopedLock locker(m_cs);
264 
265     if (0 < m_usedCount)
266     {
267         *pOut = m_ppBuffer[m_firstIndex];
268 
269         return true;
270     }
271     else
272     {
273         return false;
274     }
275 }
276 
277 template <class Locker>
GetFront() const278 uptr InterCoreBlockingQueueBase<Locker>::GetFront() const
279 {
280     // キューから取り出し処理中のスレッド数を更新します。
281     ++m_WaitingEnqueueCount;
282     DataSynchronizationBarrier();
283     uptr data;
284     for(;;)
285     {
286         if (TryGetFront(&data))
287         {
288             break;
289         }
290 
291         // キューの内容が空の場合は待機します。
292         m_EnqueueSemaphore.Acquire();
293     }
294     --m_WaitingEnqueueCount;
295     DataSynchronizationBarrier();
296     return data;
297 }
298 
299 template class InterCoreBlockingQueueBase<nn::os::InterCoreCriticalSection>;
300 
301 } // namespace detail
302 
303 }} // namespace nn::os
304 
305 
306 #include <new>
307 
308 using namespace nn::os;
309 
310 extern "C" {
311 
nnosInterCoreBlockingQueueInitialize(nnosInterCoreBlockingQueue * this_,uptr buffer[],size_t size)312 void nnosInterCoreBlockingQueueInitialize(nnosInterCoreBlockingQueue* this_, uptr buffer[], size_t size)
313 {
314     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
315     new (pInterCoreBlockingQueue) InterCoreBlockingQueue();
316     pInterCoreBlockingQueue->Initialize(buffer, size);
317 }
318 
nnosInterCoreBlockingQueueTryInitialize(nnosInterCoreBlockingQueue * this_,uptr buffer[],size_t size)319 bool nnosInterCoreBlockingQueueTryInitialize(nnosInterCoreBlockingQueue* this_, uptr buffer[], size_t size)
320 {
321     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
322     new (pInterCoreBlockingQueue) InterCoreBlockingQueue();
323     Result result = pInterCoreBlockingQueue->TryInitialize(buffer, size);
324     return result.IsSuccess();
325 }
326 
nnosInterCoreBlockingQueueFinalize(nnosInterCoreBlockingQueue * this_)327 void nnosInterCoreBlockingQueueFinalize(nnosInterCoreBlockingQueue* this_)
328 {
329     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
330     pInterCoreBlockingQueue->Finalize();
331     pInterCoreBlockingQueue->~InterCoreBlockingQueue();
332 }
333 
nnosInterCoreBlockingQueueTryEnqueue(nnosInterCoreBlockingQueue * this_,uptr data)334 bool nnosInterCoreBlockingQueueTryEnqueue(nnosInterCoreBlockingQueue* this_, uptr data)
335 {
336     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
337     return pInterCoreBlockingQueue->TryEnqueue(data);
338 }
339 
nnosInterCoreBlockingQueueEnqueue(nnosInterCoreBlockingQueue * this_,uptr data)340 void nnosInterCoreBlockingQueueEnqueue(nnosInterCoreBlockingQueue* this_, uptr data)
341 {
342     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
343     pInterCoreBlockingQueue->Enqueue(data);
344 }
345 
nnosInterCoreBlockingQueueTryJam(nnosInterCoreBlockingQueue * this_,uptr data)346 bool nnosInterCoreBlockingQueueTryJam(nnosInterCoreBlockingQueue* this_, uptr data)
347 {
348     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
349     return pInterCoreBlockingQueue->TryJam(data);
350 }
351 
nnosInterCoreBlockingQueueJam(nnosInterCoreBlockingQueue * this_,uptr data)352 void nnosInterCoreBlockingQueueJam(nnosInterCoreBlockingQueue* this_, uptr data)
353 {
354     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
355     pInterCoreBlockingQueue->Jam(data);
356 }
357 
nnosInterCoreBlockingQueueTryDequeue(nnosInterCoreBlockingQueue * this_,uptr * pOut)358 bool nnosInterCoreBlockingQueueTryDequeue(nnosInterCoreBlockingQueue* this_, uptr* pOut)
359 {
360     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
361     return pInterCoreBlockingQueue->TryDequeue(pOut);
362 }
363 
nnosInterCoreBlockingQueueDequeue(nnosInterCoreBlockingQueue * this_)364 uptr nnosInterCoreBlockingQueueDequeue(nnosInterCoreBlockingQueue* this_)
365 {
366     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
367     return pInterCoreBlockingQueue->Dequeue();
368 }
369 
nnosInterCoreBlockingQueueTryGetFront(nnosInterCoreBlockingQueue * this_,uptr * pOut)370 bool nnosInterCoreBlockingQueueTryGetFront(nnosInterCoreBlockingQueue* this_, uptr* pOut)
371 {
372     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
373     return pInterCoreBlockingQueue->TryGetFront(pOut);
374 }
375 
nnosInterCoreBlockingQueueGetFront(nnosInterCoreBlockingQueue * this_)376 uptr nnosInterCoreBlockingQueueGetFront(nnosInterCoreBlockingQueue* this_)
377 {
378     InterCoreBlockingQueue* pInterCoreBlockingQueue = reinterpret_cast<InterCoreBlockingQueue*>(this_);
379     return pInterCoreBlockingQueue->GetFront();
380 }
381 
382 }
383