1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_Alarm.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: 34337 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nn/assert.h>
17 #include <nn/config.h>
18 #include <nn/os/os_Alarm.h>
19 #include <nn/svc/svc_Stub.h>
20 #include <nn/os/os_ThreadPool.h>
21 #include <nn/os/os_MemoryBlock.h>
22 #include <nn/util/util_StaticAssert.h>
23 #include <new>
24 
25 //---------------------------------------------------------------------------
26 
27 using namespace nn;
28 using namespace nn::fnd;
29 using namespace nn::svc;
30 using namespace nn::os;
31 using namespace nn::util;
32 
33 namespace nn{ namespace os{
34 
35 namespace {
36 
37 #if NN_PLATFORM_HAS_MMU
38     const size_t NUM_ALARM_THREAD = 2; //
39     const size_t STACK_SIZE = 4096;    //
40     StackBuffer<STACK_SIZE> s_Stacks[NUM_ALARM_THREAD];
41     MemoryBlock threadPoolMemoryBlock; //
42 #endif  // if NN_PLATFORM_HAS_MMU
43     ThreadPool* alarmThreadPool = 0;
44 
45 }
46 
47 #if NN_PLATFORM_HAS_MMU
48 // Initializes the alarm system.
49 // This function may be deleted in the future.
InitializeAlarmSystem(s32 priority)50 void InitializeAlarmSystem(s32 priority)
51 {
52     InitializeAlarmSystem(priority, 0);
53 }
InitializeAlarmSystem(s32 workerPriority,s32 waitPriority)54 void InitializeAlarmSystem(s32 workerPriority, s32 waitPriority)
55 {
56     if (alarmThreadPool)
57     {
58         return;
59     }
60     // Allocates the alarm thread stack.
61     uptr stacks[NUM_ALARM_THREAD];
62     for (s32 i = 0; i < NUM_ALARM_THREAD; ++i)
63     {
64         stacks[i] = s_Stacks[i].GetStackBottom();
65     }
66     threadPoolMemoryBlock.Initialize(sizeof(nn::os::ThreadPool));
67     NN_STATIC_ASSERT(sizeof(ThreadPool) % 8 == 0);
68     NN_TASSERT_(sizeof(ThreadPool) + ThreadPool::GetWorkBufferSize(100, NUM_ALARM_THREAD) <= threadPoolMemoryBlock.GetSize());
69     void* threadPoolStorage = reinterpret_cast<void*>(threadPoolMemoryBlock.GetAddress());
70     void* buffer = reinterpret_cast<void*>(threadPoolMemoryBlock.GetAddress() + sizeof(ThreadPool));
71     alarmThreadPool = new (threadPoolStorage) ThreadPool(buffer, 100, NUM_ALARM_THREAD, stacks, workerPriority, waitPriority);
72 }
73 #endif  // if NN_PLATFORM_HAS_MMU
74 
75 #if NN_PLATFORM_HAS_MMU
GetRequiredMemorySizeForAlarmSystem(void)76 size_t GetRequiredMemorySizeForAlarmSystem(void)
77 {
78     return STACK_SIZE * NUM_ALARM_THREAD + (sizeof(nn::os::ThreadPool) / NN_OS_MEMORY_PAGE_SIZE + 1) * NN_OS_MEMORY_PAGE_SIZE;
79 }
80 #endif  // if NN_PLATFORM_HAS_MMU
81 
Initialize()82 void Alarm::Initialize()
83 {
84     NN_TASSERTMSG_(alarmThreadPool, "Not called InitializeAlarmSystem.");
85     m_Timer.Initialize(false);
86     m_Invoker = alarmThreadPool;
87 }
88 
Finalize()89 void Alarm::Finalize()
90 {
91     NN_TASSERT_(CanSet());
92     m_Timer.Finalize();
93 }
94 
~Alarm()95 Alarm::~Alarm() { this->Finalize(); }
96 
97 // Sets a one-shot alarm.
SetOneShot(TimeSpan time,AlarmHandler handler,void * p)98 void Alarm::SetOneShot(TimeSpan time, AlarmHandler handler, void* p)
99 {
100     NN_TASSERT_(CanSet());
101     m_Handler = handler;
102     m_Parameter = p;
103     m_Flags = Flags::Create(false, true, true);
104     m_Timer.StartOneShot(time);
105     m_Invoker->AddWaitTask(this);
106 }
107 
108 // Sets a cyclic alarm.
SetPeriodic(TimeSpan initial,TimeSpan interval,AlarmHandler handler,void * p)109 void Alarm::SetPeriodic(TimeSpan initial, TimeSpan interval, AlarmHandler handler, void* p)
110 {
111     NN_TASSERT_(CanSet());
112     m_Handler = handler;
113     m_Parameter = p;
114     m_Flags = Flags::Create(false, false, true);
115     m_Timer.StartPeriodic(initial, interval);
116     m_Invoker->AddWaitTask(this);
117 }
118 
119 // Structure to set the alarm state to cancel
120 struct Alarm::CancelFunc
121 {
122     // Cancels the alarm.
123     // Returns true if cancel succeeds.
operator ()nn::os::Alarm::CancelFunc124     bool operator()(Flags& flags)
125     {
126         if (flags.isSet)
127         {
128             // Sets the cancel flag.
129             flags = Flags::Create(true, flags.isOneShot, true);
130             return true;
131         }
132         else
133         {
134             // Alarm is not set.
135             return false;
136         }
137     }
138 };
139 
140 // Cancels set alarms.
Cancel()141 void Alarm::Cancel()
142 {
143     CancelFunc f;
144     if (m_Flags.AtomicUpdateConditional(f))
145     {
146         // If cancelled, calls handler without waiting for timer.
147         m_Timer.Signal();
148     }
149 }
150 
151 // Structure to set the alarm state to the state after the handler executes
152 struct Alarm::InvokeFunc
153 {
154     // Sets whether the alarm was canceled
155     // Returns false if the alarm continues after the handler is called; true if it does not.
operator ()nn::os::Alarm::InvokeFunc156     bool operator()(Flags& flags)
157     {
158         this->cancelled = flags.cancelled;
159         if (flags.cancelled || flags.isOneShot)
160         {
161             // Flag reflects that the alarm has finished.
162             flags = Flags::Create(flags.cancelled, flags.isOneShot, false);
163 
164 			// Returns true because the alarm finished.
165             return true;
166         }
167         else
168         {
169 			// Returns false because the alarm continues.
170             return false;
171         }
172     }
173 
174     bool cancelled;  // Whether the alarm was cancelled
175 };
176 
177 // Process executed by the alarm.
Invoke()178 void Alarm::Invoke()
179 {
180     // TODO: Verification of synchronization related area here
181     NN_TASSERT_(m_Flags.Read().isSet);
182     NN_TASSERT_(m_Handler);
183     AlarmHandler handler = m_Handler;
184     void* parameter = m_Parameter;
185     InvokeFunc f;
186     if (m_Flags.AtomicUpdateConditional(f))
187     {
188         handler(parameter, f.cancelled);
189     }
190     else
191     {
192         handler(parameter, f.cancelled);
193 
194         // Register task to call alarm again.
195         m_Invoker->AddWaitTask(this);
196     }
197 }
198 
199 // Gets synchronization object to issue signal.
GetWaitObject()200 nn::os::WaitObject* Alarm::GetWaitObject()
201 {
202     return &m_Timer;
203 }
204 
205 }} // namespace nn::os
206 
207 #include <new>
208 
209 using namespace nn::os;
210 
211 extern "C" {
212 
nnosInitializeAlarmSystem()213 void nnosInitializeAlarmSystem()
214 {
215     nn::os::InitializeAlarmSystem();
216 }
217 
nnosAlarmInitialize(nnosAlarm * p)218 void nnosAlarmInitialize(nnosAlarm* p)
219 {
220     Alarm* this_ = new (p) Alarm;
221     this_->Initialize();
222 }
223 
nnosAlarmFinalize(nnosAlarm * p)224 void nnosAlarmFinalize(nnosAlarm* p)
225 {
226     Alarm* pAlarm = reinterpret_cast<Alarm*>(p);
227     pAlarm->~Alarm();
228 }
229 
nnosAlarmSetOneShot(nnosAlarm * p,s64 time,nnosAlarmHandler handler,void * param)230 void nnosAlarmSetOneShot(nnosAlarm* p, s64 time, nnosAlarmHandler handler, void* param)
231 {
232     Alarm* pAlarm = reinterpret_cast<Alarm*>(p);
233     pAlarm->SetOneShot(nn::fnd::TimeSpan::FromNanoSeconds(time), handler, param);
234 }
235 
nnosAlarmSetPeriodic(nnosAlarm * p,s64 initial,s64 interval,nnosAlarmHandler handler,void * param)236 void nnosAlarmSetPeriodic(nnosAlarm* p, s64 initial, s64 interval, nnosAlarmHandler handler, void* param)
237 {
238     Alarm* pAlarm = reinterpret_cast<Alarm*>(p);
239     pAlarm->SetPeriodic(nn::fnd::TimeSpan::FromNanoSeconds(initial), nn::fnd::TimeSpan::FromNanoSeconds(interval), handler, param);
240 }
241 
nnosAlarmCancel(nnosAlarm * p)242 void nnosAlarmCancel(nnosAlarm* p)
243 {
244     Alarm* pAlarm = reinterpret_cast<Alarm*>(p);
245     pAlarm->Cancel();
246 }
247 
nnosAlarmCanSet(const nnosAlarm * p)248 bool nnosAlarmCanSet(const nnosAlarm* p)
249 {
250     const Alarm* pAlarm = reinterpret_cast<const Alarm*>(p);
251     return pAlarm->CanSet();
252 }
253 
254 }
255 
256