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