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