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: 33107 $
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 // Locks while inserting data in the queue so that other threads cannot perform operations.
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 // Wakes up thread waiting for data insertion.
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 // Locks while inserting data in the queue so that other threads cannot perform operations.
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 // Wakes up thread waiting for data insertion.
149 NotifyEnqueue();
150 return bReturn;
151 }
152
153 template <class Locker>
Enqueue(uptr data)154 void InterCoreBlockingQueueBase<Locker>::Enqueue(uptr data)
155 {
156 // Updates the number of threads during the process to insert in queue.
157 ++m_WaitingDequeueCount;
158 DataSynchronizationBarrier();
159 for(;;)
160 {
161 if (TryEnqueue(data))
162 {
163 break;
164 }
165
166 // Waits until there is space in the queue.
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 // Locks while inserting data in the queue so that other threads cannot perform operations.
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 // Wakes up thread waiting for data insertion.
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 // Updates the number of threads during the process to insert in queue.
199 ++m_WaitingDequeueCount;
200 DataSynchronizationBarrier();
201 for(;;)
202 {
203 if (TryJam(data))
204 {
205 break;
206 }
207
208 // Waits until there is space in the queue.
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 // Locks while retrieving data from the queue so that other threads cannot perform operations.
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 // Wakes up the thread waiting for a space in the queue.
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 // Updates the number of threads during the process to retrieve data from the queue.
241 ++m_WaitingEnqueueCount;
242 DataSynchronizationBarrier();
243 uptr data;
244 for(;;)
245 {
246 if (TryDequeue(&data))
247 {
248 break;
249 }
250
251 // Waits when the queue content is empty.
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 // Locks while retrieving data from the queue so that other threads cannot perform operations.
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 // Updates the number of threads during the process to retrieve data from the queue.
281 ++m_WaitingEnqueueCount;
282 DataSynchronizationBarrier();
283 uptr data;
284 for(;;)
285 {
286 if (TryGetFront(&data))
287 {
288 break;
289 }
290
291 // Waits when the queue content is empty.
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