1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: os_LightAlarm.cpp
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:$
14 *---------------------------------------------------------------------------*/
15
16 #include <nn/os.h>
17 #include <nn/assert.h>
18 #include <nn/config.h>
19 #include <nn/os/os_LightAlarm.h>
20 #include <nn/svc/svc_Stub.h>
21 #include <nn/os/os_ThreadPool.h>
22 #include <nn/os/os_MemoryBlock.h>
23 #include <nn/util/util_StaticAssert.h>
24 #include <new>
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::os::detail;
33 using namespace nn::util;
34
35 #define TICK_BORDER 0x3FFFFFFFFFFFFFFF
36 #define CANCEL_WAIT_TIME 1
37
38 //Global Variable Declarations
39 namespace {//
40 CriticalSection s_CriticalSection = WithInitialize();
41 //Structure to generate stack region objects from the stack region bottom address.
42 struct StackBufferAdapter
43 {
44 uptr stackBottom;
StackBufferAdapter__anona08cb2590111::StackBufferAdapter45 StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {}
GetStackBottom__anona08cb2590111::StackBufferAdapter46 uptr GetStackBottom() const { return stackBottom; }
47 };
48 //Define class for alarm system.
49 nn::os::detail::LightAlarmSystem s_LightAlarmSystemCore;
50 }
51 //Definition of static member
52 nn::os::detail::LightAlarmSystem* LightAlarm::s_System;
53
54 namespace nn{ namespace os{
55
SetTimer(Tick nowTick)56 void LightAlarmSystem::SetTimer( Tick nowTick )
57 {
58 { //Sets first alarm timer of the queue.
59 //This process performed with mutual exclusion.
60 CriticalSection::ScopedLock locker(s_CriticalSection);
61 LightAlarmNode* pSetNode = list.GetFront();
62 if( pSetNode == NULL)
63 {
64 //No alarm, so stop timer.
65 LightAlarm::s_System->timer.Stop();
66 return;
67 }
68 //If the expression timeout has already elapsed when the timer is set
69 Tick diff = pSetNode->fire - nowTick;
70 if( diff < 0 )
71 { //The timer signal is sent immediately.
72 LightAlarm::s_System->timer.Signal();
73 }
74 //If the GetSystemCurrent function tick value wraps around
75 //The determination boundary value is set to TICK_BORDER.
76 else if( diff >= TICK_BORDER )
77 {
78 //The timer signal is sent immediately.
79 LightAlarm::s_System->timer.Signal();
80 }
81 else
82 {
83 LightAlarm::s_System->timer.StartOneShot( pSetNode->fire - nowTick );
84 }
85 }
86 }
87
InsertAlarm(LightAlarmNode * pInsertNode,Tick nowTick)88 void LightAlarmSystem::InsertAlarm( LightAlarmNode *pInsertNode, Tick nowTick )
89 {
90 { //Alarms are inserted into the queue in the expressed fire order. This process performed with mutual exclusion.
91 CriticalSection::ScopedLock locker(s_CriticalSection);
92 for(LightAlarmNode* pLightAlarm = list.GetFront(); pLightAlarm != NULL; pLightAlarm = list.GetNext(pLightAlarm))
93 {
94
95 //Searches for the insertion location as though the alarms are registered in the queue in expressed time order.
96 //Confirms that the GetSystemCurrent function tick value does not wrap around.
97 //The determination boundary value is set to TICK_BORDER.
98 if( 0 <= pLightAlarm->fire - pInsertNode->fire
99 && pLightAlarm->fire - pInsertNode->fire <= TICK_BORDER )
100 {
101 list.Insert(pLightAlarm, pInsertNode);
102 //Sets the timer.
103 LightAlarm::s_System->SetTimer( nowTick );
104 return;
105 }
106 }
107 list.PushBack(pInsertNode); //When the expression is later than any of the alarms in the queue, the alarm is inserted at the end.
108 //Sets the timer.
109 LightAlarm::s_System->SetTimer( nowTick );
110 }
111 }
112
LightAlarmThread()113 static void LightAlarmThread()
114 {
115 LightAlarmHandler handler; //Variable for saving alarm handler
116 void* parameter; //Variable for saving alarm handler argument
117 bool canceled; //Alarm cancel flag
118 while(true)
119 {
120 LightAlarm::s_System->timer.Wait();
121
122 { //Performs mutual exclusion until the first node process in the queue finishes.
123 CriticalSection::ScopedLock locker(s_CriticalSection);
124 //If FinalizeLightAlarmSystem has been called
125 if( LightAlarm::s_System->initialized == false )
126 {
127 break;
128 }
129 //Get the first node in the queue
130 LightAlarmNode* pHeadNode = LightAlarm::s_System->list.PopFront();
131 NN_TASSERTMSG_(pHeadNode, "There is no node.\n");
132 //NULL is never entered for pHeadNode, so if this is NULL, process nothing.
133 if( pHeadNode == NULL )
134 {
135 continue;
136 }
137 //Immediately before executing the handler, save the address to a local variable because the address referenced by pHeadNode may be deallocated.
138 //
139 handler = pHeadNode->handler;
140 parameter = pHeadNode->parameter;
141 canceled = pHeadNode->canceled;
142
143 if(pHeadNode->period == 0 || pHeadNode->canceled == true)
144 {
145 //When there is an alarm waiting to be set, a signal is sent to the expression notification event.
146 if( pHeadNode->waiting == true)
147 {
148 //
149 pHeadNode->event.Signal();
150 }
151 //Initialize the handler to express.
152 pHeadNode->handler = NULL;
153 //Initialize the cancel flag.
154 pHeadNode->canceled = false;
155 //When the next node exists, set the timer.
156 LightAlarm::s_System->SetTimer(Tick::GetSystemCurrent());
157 }
158 //For periodic alarms
159 else
160 {
161 //Set the next expression time, and insert the alarm in the queue again.
162 pHeadNode->fire += pHeadNode->period;
163 LightAlarm::s_System->InsertAlarm(pHeadNode, Tick::GetSystemCurrent());
164
165 //When the next node exists, set the timer.
166 LightAlarm::s_System->SetTimer(Tick::GetSystemCurrent());
167 }
168 }
169 /*
170 With the alarm handler, limit exclusive processing to a minimum.
171 */
172 (handler)(parameter,canceled);
173 }
174 }
175
176 // Initializes the alarm system.
InitializeLightAlarmSystemImplCore(uptr stackBottom,s32 priority,nn::os::detail::LightAlarmSystem * lightAlarmSystem)177 void nn::os::detail::InitializeLightAlarmSystemImplCore( uptr stackBottom, s32 priority, nn::os::detail::LightAlarmSystem* lightAlarmSystem )
178 {
179 if(lightAlarmSystem->initialized == true)
180 {
181 return;
182 }
183 LightAlarm::s_System = lightAlarmSystem;
184 LightAlarm::s_System->timer.Initialize(false);
185 StackBufferAdapter stack(stackBottom);
186 LightAlarm::s_System->thread.Start(LightAlarmThread, stack, priority);
187 LightAlarm::s_System->initialized = true;
188 }
189
190 // Initializes the alarm system.
InitializeLightAlarmSystemImpl(uptr stackBottom,s32 priority)191 void InitializeLightAlarmSystemImpl( uptr stackBottom, s32 priority )
192 {
193 InitializeLightAlarmSystemImplCore( stackBottom, priority, &s_LightAlarmSystemCore );
194 }
195
196 // Finalize the alarm system.
FinalizeLightAlarmSystemImpl(nn::os::detail::LightAlarmSystem * lightAlarmSystem)197 void nn::os::detail::FinalizeLightAlarmSystemImpl( nn::os::detail::LightAlarmSystem* lightAlarmSystem )
198 {
199 if(LightAlarm::s_System->initialized == false)
200 {
201 return;
202 }
203 lightAlarmSystem->initialized = false;
204 lightAlarmSystem->timer.Signal();
205 lightAlarmSystem->thread.Join();
206 lightAlarmSystem->thread.Finalize();
207 lightAlarmSystem->timer.Finalize();
208 lightAlarmSystem->list.Clear(); //Delete all elements in the queue.
209 }
210
211 // Finalize the alarm system.
FinalizeLightAlarmSystem()212 void FinalizeLightAlarmSystem()
213 {
214 nn::os::detail::FinalizeLightAlarmSystemImpl( &s_LightAlarmSystemCore );
215 }
216
Initialize(void)217 void LightAlarm::Initialize(void)
218 {
219 NN_TASSERTMSG_(LightAlarm::s_System->initialized, "Not called InitializeLightAlarmSystem.\n");
220 m_LightAlarm.handler = NULL; //Initialization of the alarm handler
221 m_LightAlarm.canceled = false; //Initialization of the cancel flag
222 }
223
Finalize(void)224 void LightAlarm::Finalize(void)
225 {
226 //If an alarm is set
227 if(CanSet() == false)
228 {
229 //Call the Cancel function.
230 Cancel();
231 }
232 }
233
~LightAlarm(void)234 LightAlarm::~LightAlarm(void) { this->Finalize(); }
235
236 // Set a one-shot alarm.
SetOneShot(TimeSpan time,LightAlarmHandler handler,void * p)237 void LightAlarm::SetOneShot(TimeSpan time, LightAlarmHandler handler, void* p)
238 {
239 //NN_TASSERTMSG_(CanSet(), "Alarm is already set.\n");
240 if( CanSet() == false )
241 {
242 m_LightAlarm.waiting = true;
243 m_LightAlarm.event.Wait();
244 m_LightAlarm.waiting = false;
245 }
246 m_LightAlarm.handler = handler;
247 m_LightAlarm.parameter = p;
248 m_LightAlarm.fire = time + Tick::GetSystemCurrent();
249 m_LightAlarm.period = static_cast<Tick>(0);
250 LightAlarm::s_System->InsertAlarm(&m_LightAlarm, Tick::GetSystemCurrent()); //Register alarms in the queue
251 }
252
253 // Set a periodic alarm.
SetPeriodic(TimeSpan initial,TimeSpan interval,LightAlarmHandler handler,void * p)254 void LightAlarm::SetPeriodic(TimeSpan initial, TimeSpan interval, LightAlarmHandler handler, void* p)
255 {
256 NN_TASSERTMSG_(CanSet(), "Alarm is already set.\n");
257 NN_TASSERTMSG_(interval>0, "bad period specified.\n");
258
259 m_LightAlarm.handler = handler;
260 m_LightAlarm.parameter = p;
261 m_LightAlarm.fire = initial + Tick::GetSystemCurrent();
262 m_LightAlarm.period = interval;
263 LightAlarm::s_System->InsertAlarm(&m_LightAlarm, Tick::GetSystemCurrent()); //Register alarms in the queue
264 }
265
266 // Cancel set alarms.
Cancel(void)267 void LightAlarm::Cancel(void)
268 {
269 { //Process under mutual exclusion to process a queue.
270 CriticalSection::ScopedLock locker(s_CriticalSection);
271 //Confirm that an alarm to be canceled exists in the queue.
272 LightAlarmNode* node = LightAlarm::s_System->list.GetFront();
273 //Continue confirming of existence of alarms to be canceled until the end of the queue is reached.
274 while( node != NULL )
275 {
276 //When an alarm to be canceled exists in the queue
277 if(node == &m_LightAlarm)
278 {
279 break;
280 }
281 node = LightAlarm::s_System->list.GetNext(node);
282 }
283 //When an alarm to be canceled does not exist in the queue
284 if(node == NULL)
285 {
286 return;
287 }
288 //When the node to delete is not at the start of the queue
289 if( &m_LightAlarm != LightAlarm::s_System->list.GetFront() )
290 {
291 //Moves the canceled node to the start of the queue.
292 LightAlarm::s_System->list.Erase( &m_LightAlarm );
293 LightAlarm::s_System->list.PushFront( &m_LightAlarm );
294 }
295 //Enables the Canceled flag
296 m_LightAlarm.canceled = true;
297 //Sends a signal to the alarm thread timer
298 LightAlarm::s_System->timer.Signal();
299 }
300 //Wait until alarm runs.
301 while( CanSet() == false )
302 {
303 Thread::Sleep(TimeSpan::FromMilliSeconds(CANCEL_WAIT_TIME));
304 }
305 }
306
CanSet(void)307 bool LightAlarm::CanSet(void)
308 {
309 return !(m_LightAlarm.handler);
310 }
311
312 }} // namespace nn::os
313
314 #include <new>
315
316 using namespace nn::os;
317
318 extern "C" {
319
nnosInitializeLightAlarmSystem(uptr stackBottom,s32 priority)320 void nnosInitializeLightAlarmSystem( uptr stackBottom, s32 priority )
321 {
322 nn::os::InitializeLightAlarmSystemImpl(stackBottom, priority);
323 }
324
nnosLightAlarmInitialize(nnosLightAlarm * p)325 void nnosLightAlarmInitialize(nnosLightAlarm* p)
326 {
327 LightAlarm* this_ = new (p) LightAlarm;
328 this_->Initialize();
329 }
330
nnosLightAlarmFinalize(nnosLightAlarm * p)331 void nnosLightAlarmFinalize(nnosLightAlarm* p)
332 {
333 LightAlarm* pLightAlarm = reinterpret_cast<LightAlarm*>(p);
334 pLightAlarm->~LightAlarm();
335 }
336
nnosLightAlarmSetOneShot(nnosLightAlarm * p,s64 time,nnosLightAlarmHandler handler,void * param)337 void nnosLightAlarmSetOneShot(nnosLightAlarm* p, s64 time, nnosLightAlarmHandler handler, void* param)
338 {
339 LightAlarm* pLightAlarm = reinterpret_cast<LightAlarm*>(p);
340 pLightAlarm->SetOneShot(nn::fnd::TimeSpan::FromNanoSeconds(time), handler, param);
341 }
342
nnosLightAlarmSetPeriodic(nnosLightAlarm * p,s64 initial,s64 interval,nnosLightAlarmHandler handler,void * param)343 void nnosLightAlarmSetPeriodic(nnosLightAlarm* p, s64 initial, s64 interval, nnosLightAlarmHandler handler, void* param)
344 {
345 LightAlarm* pLightAlarm = reinterpret_cast<LightAlarm*>(p);
346 pLightAlarm->SetPeriodic(nn::fnd::TimeSpan::FromNanoSeconds(initial), nn::fnd::TimeSpan::FromNanoSeconds(interval), handler, param);
347 }
348
nnosLightAlarmCancel(nnosLightAlarm * p)349 void nnosLightAlarmCancel(nnosLightAlarm* p)
350 {
351 LightAlarm* pLightAlarm = reinterpret_cast<LightAlarm*>(p);
352 pLightAlarm->Cancel();
353 }
354
nnosLightAlarmCanSet(nnosLightAlarm * p)355 bool nnosLightAlarmCanSet(nnosLightAlarm* p)
356 {
357 LightAlarm* pLightAlarm = reinterpret_cast<LightAlarm*>(p);
358 return pLightAlarm->CanSet();
359 }
360
361 }
362