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: 24961 $
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 // アラームシステムを初期化します。
49 // この関数は将来的に削除される可能性があります。
InitializeAlarmSystem(s32 priority)50 void InitializeAlarmSystem(s32 priority)
51 {
52 if (alarmThreadPool)
53 {
54 return;
55 }
56 // アラームスレッド用スタックを確保します。
57 uptr stacks[NUM_ALARM_THREAD];
58 for (s32 i = 0; i < NUM_ALARM_THREAD; ++i)
59 {
60 stacks[i] = s_Stacks[i].GetStackBottom();
61 }
62 threadPoolMemoryBlock.Initialize(sizeof(nn::os::ThreadPool));
63 NN_STATIC_ASSERT(sizeof(ThreadPool) % 8 == 0);
64 NN_TASSERT_(sizeof(ThreadPool) + ThreadPool::GetWorkBufferSize(100, NUM_ALARM_THREAD) <= threadPoolMemoryBlock.GetSize());
65 void* threadPoolStorage = reinterpret_cast<void*>(threadPoolMemoryBlock.GetAddress());
66 void* buffer = reinterpret_cast<void*>(threadPoolMemoryBlock.GetAddress() + sizeof(ThreadPool));
67 alarmThreadPool = new (threadPoolStorage) ThreadPool(buffer, 100, NUM_ALARM_THREAD, stacks, priority);
68 }
69 #endif // if NN_PLATFORM_HAS_MMU
70
71 #if NN_PLATFORM_HAS_MMU
GetRequiredMemorySizeForAlarmSystem(void)72 size_t GetRequiredMemorySizeForAlarmSystem(void)
73 {
74 return STACK_SIZE * NUM_ALARM_THREAD + (sizeof(nn::os::ThreadPool) / NN_OS_MEMORY_PAGE_SIZE + 1) * NN_OS_MEMORY_PAGE_SIZE;
75 }
76 #endif // if NN_PLATFORM_HAS_MMU
77
Initialize()78 void Alarm::Initialize()
79 {
80 NN_TASSERTMSG_(alarmThreadPool, "Not called InitializeAlarmSystem.");
81 m_Timer.Initialize(false);
82 m_Invoker = alarmThreadPool;
83 }
84
Finalize()85 void Alarm::Finalize()
86 {
87 NN_TASSERT_(CanSet());
88 m_Timer.Finalize();
89 }
90
~Alarm()91 Alarm::~Alarm() { this->Finalize(); }
92
93 // ワンショットアラームを設定します。
SetOneShot(TimeSpan time,AlarmHandler handler,void * p)94 void Alarm::SetOneShot(TimeSpan time, AlarmHandler handler, void* p)
95 {
96 NN_TASSERT_(CanSet());
97 m_Handler = handler;
98 m_Parameter = p;
99 m_Flags = Flags::Create(false, true, true);
100 m_Timer.StartOneShot(time);
101 m_Invoker->AddWaitTask(this);
102 }
103
104 // 周期アラームを設定します。
SetPeriodic(TimeSpan initial,TimeSpan interval,AlarmHandler handler,void * p)105 void Alarm::SetPeriodic(TimeSpan initial, TimeSpan interval, AlarmHandler handler, void* p)
106 {
107 NN_TASSERT_(CanSet());
108 m_Handler = handler;
109 m_Parameter = p;
110 m_Flags = Flags::Create(false, false, true);
111 m_Timer.StartPeriodic(initial, interval);
112 m_Invoker->AddWaitTask(this);
113 }
114
115 // アラーム状態をキャンセルに設定するための構造体
116 struct Alarm::CancelFunc
117 {
118 // アラームをキャンセルします。
119 // キャンセルに成功すれば true を返します。
operator ()nn::os::Alarm::CancelFunc120 bool operator()(Flags& flags)
121 {
122 if (flags.isSet)
123 {
124 // キャンセルフラグを設定します。
125 flags = Flags::Create(true, flags.isOneShot, true);
126 return true;
127 }
128 else
129 {
130 // アラームが設定されていません。
131 return false;
132 }
133 }
134 };
135
136 // 設定済みのアラームを取り消します。
Cancel()137 void Alarm::Cancel()
138 {
139 CancelFunc f;
140 if (m_Flags.AtomicUpdateConditional(f))
141 {
142 // キャンセルされれば、タイマーを待たずにハンドラを呼び出します。
143 m_Timer.Signal();
144 }
145 }
146
147 // アラーム状態をハンドラ実行後の状態に設定するための構造体
148 struct Alarm::InvokeFunc
149 {
150 // アラームがキャンセルされたかを canceled に設定します。
151 // ハンドラ呼出し後にアラームを継続する場合には false、しない場合には true を返します。
operator ()nn::os::Alarm::InvokeFunc152 bool operator()(Flags& flags)
153 {
154 this->cancelled = flags.cancelled;
155 if (flags.cancelled || flags.isOneShot)
156 {
157 // アラームが終了したことをフラグに反映します。
158 flags = Flags::Create(flags.cancelled, flags.isOneShot, false);
159
160 // アラームを終了するので true を返します。
161 return true;
162 }
163 else
164 {
165 // アラームを継続するので false を返します。
166 return false;
167 }
168 }
169
170 bool cancelled; // アラームがキャンセルされたかどうか
171 };
172
173 // アラームで実行される処理です。
Invoke()174 void Alarm::Invoke()
175 {
176 // TODO: この周辺の同期周りの検証
177 NN_TASSERT_(m_Flags.Read().isSet);
178 NN_TASSERT_(m_Handler);
179 AlarmHandler handler = m_Handler;
180 void* parameter = m_Parameter;
181 InvokeFunc f;
182 if (m_Flags.AtomicUpdateConditional(f))
183 {
184 handler(parameter, f.cancelled);
185 }
186 else
187 {
188 handler(parameter, f.cancelled);
189
190 // アラームが再度呼ばれるようにタスクを登録します。
191 m_Invoker->AddWaitTask(this);
192 }
193 }
194
195 // シグナルを発行する同期オブジェクトを取得します。
GetWaitObject()196 nn::os::WaitObject* Alarm::GetWaitObject()
197 {
198 return &m_Timer;
199 }
200
201 }} // namespace nn::os
202
203 #include <new>
204
205 using namespace nn::os;
206
207 extern "C" {
208
nnosInitializeAlarmSystem()209 void nnosInitializeAlarmSystem()
210 {
211 nn::os::InitializeAlarmSystem();
212 }
213
nnosAlarmInitialize(nnosAlarm * p)214 void nnosAlarmInitialize(nnosAlarm* p)
215 {
216 Alarm* this_ = new (p) Alarm;
217 this_->Initialize();
218 }
219
nnosAlarmFinalize(nnosAlarm * p)220 void nnosAlarmFinalize(nnosAlarm* p)
221 {
222 Alarm* pAlarm = reinterpret_cast<Alarm*>(p);
223 pAlarm->~Alarm();
224 }
225
nnosAlarmSetOneShot(nnosAlarm * p,s64 time,nnosAlarmHandler handler,void * param)226 void nnosAlarmSetOneShot(nnosAlarm* p, s64 time, nnosAlarmHandler handler, void* param)
227 {
228 Alarm* pAlarm = reinterpret_cast<Alarm*>(p);
229 pAlarm->SetOneShot(nn::fnd::TimeSpan::FromNanoSeconds(time), handler, param);
230 }
231
nnosAlarmSetPeriodic(nnosAlarm * p,s64 initial,s64 interval,nnosAlarmHandler handler,void * param)232 void nnosAlarmSetPeriodic(nnosAlarm* p, s64 initial, s64 interval, nnosAlarmHandler handler, void* param)
233 {
234 Alarm* pAlarm = reinterpret_cast<Alarm*>(p);
235 pAlarm->SetPeriodic(nn::fnd::TimeSpan::FromNanoSeconds(initial), nn::fnd::TimeSpan::FromNanoSeconds(interval), handler, param);
236 }
237
nnosAlarmCancel(nnosAlarm * p)238 void nnosAlarmCancel(nnosAlarm* p)
239 {
240 Alarm* pAlarm = reinterpret_cast<Alarm*>(p);
241 pAlarm->Cancel();
242 }
243
nnosAlarmCanSet(const nnosAlarm * p)244 bool nnosAlarmCanSet(const nnosAlarm* p)
245 {
246 const Alarm* pAlarm = reinterpret_cast<const Alarm*>(p);
247 return pAlarm->CanSet();
248 }
249
250 }
251
252