/*---------------------------------------------------------------------------* Project: Horizon File: os_Alarm.cpp Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 24961 $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- using namespace nn; using namespace nn::fnd; using namespace nn::svc; using namespace nn::os; using namespace nn::util; namespace nn{ namespace os{ namespace { #if NN_PLATFORM_HAS_MMU const size_t NUM_ALARM_THREAD = 2; //!< アラームスレッド数 const size_t STACK_SIZE = 4096; //!< アラームスレッド用スタックのサイズ StackBuffer s_Stacks[NUM_ALARM_THREAD]; MemoryBlock threadPoolMemoryBlock; //!< アラームスレッド用メモリブロック #endif // if NN_PLATFORM_HAS_MMU ThreadPool* alarmThreadPool = 0; } #if NN_PLATFORM_HAS_MMU // アラームシステムを初期化します。 // この関数は将来的に削除される可能性があります。 void InitializeAlarmSystem(s32 priority) { if (alarmThreadPool) { return; } // アラームスレッド用スタックを確保します。 uptr stacks[NUM_ALARM_THREAD]; for (s32 i = 0; i < NUM_ALARM_THREAD; ++i) { stacks[i] = s_Stacks[i].GetStackBottom(); } threadPoolMemoryBlock.Initialize(sizeof(nn::os::ThreadPool)); NN_STATIC_ASSERT(sizeof(ThreadPool) % 8 == 0); NN_TASSERT_(sizeof(ThreadPool) + ThreadPool::GetWorkBufferSize(100, NUM_ALARM_THREAD) <= threadPoolMemoryBlock.GetSize()); void* threadPoolStorage = reinterpret_cast(threadPoolMemoryBlock.GetAddress()); void* buffer = reinterpret_cast(threadPoolMemoryBlock.GetAddress() + sizeof(ThreadPool)); alarmThreadPool = new (threadPoolStorage) ThreadPool(buffer, 100, NUM_ALARM_THREAD, stacks, priority); } #endif // if NN_PLATFORM_HAS_MMU #if NN_PLATFORM_HAS_MMU size_t GetRequiredMemorySizeForAlarmSystem(void) { return STACK_SIZE * NUM_ALARM_THREAD + (sizeof(nn::os::ThreadPool) / NN_OS_MEMORY_PAGE_SIZE + 1) * NN_OS_MEMORY_PAGE_SIZE; } #endif // if NN_PLATFORM_HAS_MMU void Alarm::Initialize() { NN_TASSERTMSG_(alarmThreadPool, "Not called InitializeAlarmSystem."); m_Timer.Initialize(false); m_Invoker = alarmThreadPool; } void Alarm::Finalize() { NN_TASSERT_(CanSet()); m_Timer.Finalize(); } Alarm::~Alarm() { this->Finalize(); } // ワンショットアラームを設定します。 void Alarm::SetOneShot(TimeSpan time, AlarmHandler handler, void* p) { NN_TASSERT_(CanSet()); m_Handler = handler; m_Parameter = p; m_Flags = Flags::Create(false, true, true); m_Timer.StartOneShot(time); m_Invoker->AddWaitTask(this); } // 周期アラームを設定します。 void Alarm::SetPeriodic(TimeSpan initial, TimeSpan interval, AlarmHandler handler, void* p) { NN_TASSERT_(CanSet()); m_Handler = handler; m_Parameter = p; m_Flags = Flags::Create(false, false, true); m_Timer.StartPeriodic(initial, interval); m_Invoker->AddWaitTask(this); } // アラーム状態をキャンセルに設定するための構造体 struct Alarm::CancelFunc { // アラームをキャンセルします。 // キャンセルに成功すれば true を返します。 bool operator()(Flags& flags) { if (flags.isSet) { // キャンセルフラグを設定します。 flags = Flags::Create(true, flags.isOneShot, true); return true; } else { // アラームが設定されていません。 return false; } } }; // 設定済みのアラームを取り消します。 void Alarm::Cancel() { CancelFunc f; if (m_Flags.AtomicUpdateConditional(f)) { // キャンセルされれば、タイマーを待たずにハンドラを呼び出します。 m_Timer.Signal(); } } // アラーム状態をハンドラ実行後の状態に設定するための構造体 struct Alarm::InvokeFunc { // アラームがキャンセルされたかを canceled に設定します。 // ハンドラ呼出し後にアラームを継続する場合には false、しない場合には true を返します。 bool operator()(Flags& flags) { this->cancelled = flags.cancelled; if (flags.cancelled || flags.isOneShot) { // アラームが終了したことをフラグに反映します。 flags = Flags::Create(flags.cancelled, flags.isOneShot, false); // アラームを終了するので true を返します。 return true; } else { // アラームを継続するので false を返します。 return false; } } bool cancelled; // アラームがキャンセルされたかどうか }; // アラームで実行される処理です。 void Alarm::Invoke() { // TODO: この周辺の同期周りの検証 NN_TASSERT_(m_Flags.Read().isSet); NN_TASSERT_(m_Handler); AlarmHandler handler = m_Handler; void* parameter = m_Parameter; InvokeFunc f; if (m_Flags.AtomicUpdateConditional(f)) { handler(parameter, f.cancelled); } else { handler(parameter, f.cancelled); // アラームが再度呼ばれるようにタスクを登録します。 m_Invoker->AddWaitTask(this); } } // シグナルを発行する同期オブジェクトを取得します。 nn::os::WaitObject* Alarm::GetWaitObject() { return &m_Timer; } }} // namespace nn::os #include using namespace nn::os; extern "C" { void nnosInitializeAlarmSystem() { nn::os::InitializeAlarmSystem(); } void nnosAlarmInitialize(nnosAlarm* p) { Alarm* this_ = new (p) Alarm; this_->Initialize(); } void nnosAlarmFinalize(nnosAlarm* p) { Alarm* pAlarm = reinterpret_cast(p); pAlarm->~Alarm(); } void nnosAlarmSetOneShot(nnosAlarm* p, s64 time, nnosAlarmHandler handler, void* param) { Alarm* pAlarm = reinterpret_cast(p); pAlarm->SetOneShot(nn::fnd::TimeSpan::FromNanoSeconds(time), handler, param); } void nnosAlarmSetPeriodic(nnosAlarm* p, s64 initial, s64 interval, nnosAlarmHandler handler, void* param) { Alarm* pAlarm = reinterpret_cast(p); pAlarm->SetPeriodic(nn::fnd::TimeSpan::FromNanoSeconds(initial), nn::fnd::TimeSpan::FromNanoSeconds(interval), handler, param); } void nnosAlarmCancel(nnosAlarm* p) { Alarm* pAlarm = reinterpret_cast(p); pAlarm->Cancel(); } bool nnosAlarmCanSet(const nnosAlarm* p) { const Alarm* pAlarm = reinterpret_cast(p); return pAlarm->CanSet(); } }