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