#include #include "platform/internal/CHIPDeviceLayerInternal.h" #include #include #include #include #include #include #include #include "MbedEventTimeout.h" #if CHIP_SYSTEM_CONFIG_USE_LWIP #include #endif #define DEFAULT_MIN_SLEEP_PERIOD (60 * 60 * 24 * 30) // Month [sec] using namespace ::chip; using namespace ::chip::Inet; using namespace ::chip::System; namespace chip { namespace DeviceLayer { namespace { System::LayerSocketsLoop & SystemLayerSocketsLoop() { return static_cast(DeviceLayer::SystemLayer()); } } // anonymous namespace // TODO: Event and timer processing is not efficient from a memory perspective. // Both occupy at least 24 bytes when only 4 bytes is required. // An optimized designed could use a separate circular buffer to store events // and register a single mbed event in the event queue to process all of them. // A similar design can be used for timers. CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) { // Members are initialized by the stack if (!mInitialized) { // reinitialize a new thread if it was terminated earlier mLoopTask.~Thread(); new (&mLoopTask) rtos::Thread(osPriorityNormal, CHIP_DEVICE_CONFIG_CHIP_TASK_STACK_SIZE, /* memory provided */ nullptr, CHIP_DEVICE_CONFIG_CHIP_TASK_NAME); mChipTaskId = 0; // Reinitialize the EventQueue mQueue.~EventQueue(); new (&mQueue) events::EventQueue(event_size * CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE); mQueue.background([&](int t) { if (t < 0) { MbedEventTimeout::DetachTimeout(); } else { MbedEventTimeout::AttachTimeout([&] { SystemLayerSocketsLoop().Signal(); }, std::chrono::milliseconds{ t }); } }); // Reinitialize the Mutexes mThisStateMutex.~Mutex(); new (&mThisStateMutex) rtos::Mutex(); mChipStackMutex.~Mutex(); new (&mChipStackMutex) rtos::Mutex(); // Reinitialize the condition variable mEventLoopCond.~ConditionVariable(); new (&mEventLoopCond) rtos::ConditionVariable(mThisStateMutex); mShouldRunEventLoop.store(false); mEventLoopHasStopped = false; mEventLoopHasRun = false; } else { ChipLogError(DeviceLayer, "Trying to reinitialize the stack"); return CHIP_ERROR_INCORRECT_STATE; } #if CHIP_SYSTEM_CONFIG_USE_LWIP // Initialize LwIP. tcpip_init(NULL, NULL); #endif auto err = System::Clock::InitClock_RealTime(); SuccessOrExit(err); // Call up to the base class _InitChipStack() to perform the bulk of the initialization. err = GenericPlatformManagerImpl::_InitChipStack(); SuccessOrExit(err); mInitialized = true; exit: return err; } void PlatformManagerImpl::_LockChipStack() { mChipStackMutex.lock(); } bool PlatformManagerImpl::_TryLockChipStack() { return mChipStackMutex.trylock(); } void PlatformManagerImpl::_UnlockChipStack() { mChipStackMutex.unlock(); } CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * eventPtr) { auto handle = mQueue.call([event = *eventPtr, this] { LockChipStack(); DispatchEvent(&event); UnlockChipStack(); }); if (!handle) { ChipLogError(DeviceLayer, "Error posting event: Not enough memory"); return CHIP_ERROR_NO_MEMORY; } return CHIP_NO_ERROR; } void PlatformManagerImpl::ProcessDeviceEvents() { mQueue.dispatch(0); } void PlatformManagerImpl::_RunEventLoop() { // Update the internal state first. // We may run on the current thread instead of the external task { mbed::ScopedLock lock(mThisStateMutex); // That's a programmign error to run the event loop if it is already running. // return early if (mShouldRunEventLoop.load()) { ChipLogError(DeviceLayer, "Error trying to run the event loop while it is already running"); return; } mShouldRunEventLoop.store(true); // Look if a task ID has already been assigned or not. // If not, it means we run in the thread that called RunEventLoop if (!mChipTaskId) { ChipLogDetail(DeviceLayer, "Run CHIP event loop on external thread"); mChipTaskId = rtos::ThisThread::get_id(); } mEventLoopHasStopped = false; mEventLoopHasRun = true; mEventLoopCond.notify_all(); } LockChipStack(); ChipLogProgress(DeviceLayer, "CHIP Run event loop"); SystemLayerSocketsLoop().EventLoopBegins(); while (mShouldRunEventLoop.load()) { SystemLayerSocketsLoop().PrepareEvents(); UnlockChipStack(); SystemLayerSocketsLoop().WaitForEvents(); LockChipStack(); SystemLayerSocketsLoop().HandleEvents(); ProcessDeviceEvents(); } SystemLayerSocketsLoop().EventLoopEnds(); UnlockChipStack(); // Notify threads waiting on the event loop to stop { mbed::ScopedLock lock(mThisStateMutex); mEventLoopHasStopped = true; mEventLoopCond.notify_all(); } } CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() { mbed::ScopedLock lock(mThisStateMutex); // This function start the Thread that run the chip event loop. // If no threads are needed, the application can directly call RunEventLoop. auto error = mLoopTask.start([this] { ChipLogDetail(DeviceLayer, "CHIP task running"); RunEventLoop(); }); if (!error) { mChipTaskId = mLoopTask.get_id(); } else { ChipLogError(DeviceLayer, "Fail to start internal loop task thread"); } // Wait for event loop run mEventLoopCond.wait([this] { return mEventLoopHasRun == true; }); return TranslateOsStatus(error); } CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() { mbed::ScopedLock lock(mThisStateMutex); // early return if the event loop is not running if (!mShouldRunEventLoop.load()) { return CHIP_NO_ERROR; } // Indicate that the event loop store mShouldRunEventLoop.store(false); // Wake from select so it unblocks processing LockChipStack(); SystemLayerSocketsLoop().Signal(); UnlockChipStack(); osStatus err = osOK; // If the thread running the event loop is different from the caller // then wait it to finish if (mChipTaskId != rtos::ThisThread::get_id()) { // First it waits for the condition variable to finish mEventLoopCond.wait([this] { return mEventLoopHasStopped == true; }); // Then if it was running on the internal task, wait for it to finish if (mChipTaskId == mLoopTask.get_id()) { err = mLoopTask.join(); mInitialized = false; // the internal thread requires initialization again. } } mChipTaskId = 0; return TranslateOsStatus(err); } CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout duration) { // Let LayerSocketsLoop::PrepareSelect() handle timers. return CHIP_NO_ERROR; } void PlatformManagerImpl::_Shutdown() { // // Call up to the base class _Shutdown() to perform the actual stack de-initialization // and clean-up // GenericPlatformManagerImpl::_Shutdown(); mInitialized = false; mQueue.background(nullptr); } CHIP_ERROR PlatformManagerImpl::TranslateOsStatus(osStatus error) { switch (error) { case osErrorNoMemory: return CHIP_ERROR_NO_MEMORY; case osOK: return CHIP_NO_ERROR; default: return CHIP_ERROR_INTERNAL; } } bool PlatformManagerImpl::IsLoopActive() { switch (mLoopTask.get_state()) { case rtos::Thread::Inactive: case rtos::Thread::Ready: case rtos::Thread::Deleted: return false; default: return true; } } // ===== Members for internal use by the following friends. PlatformManagerImpl PlatformManagerImpl::sInstance; } // namespace DeviceLayer } // namespace chip