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