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