blob: 25b5e5e5ccb723ddedba9a3ed01c33fdff88a159 [file] [log] [blame]
#include <new>
#include "platform/internal/CHIPDeviceLayerInternal.h"
#include <platform/CHIPDeviceLayer.h>
#include <platform/PlatformManager.h>
#include <platform/ScopedLock.h>
#include <platform/internal/GenericPlatformManagerImpl.ipp>
#include <platform/mbed/DiagnosticDataProviderImpl.h>
#include <platform/mbed/SystemTimeSupport.h>
#include <rtos/ThisThread.h>
#include "MbedEventTimeout.h"
#if CHIP_SYSTEM_CONFIG_USE_LWIP
#include <lwip/tcpip.h>
#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<System::LayerSocketsLoop &>(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<ImplClass>::_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<rtos::Mutex> 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<rtos::Mutex> lock(mThisStateMutex);
mEventLoopHasStopped = true;
mEventLoopCond.notify_all();
}
}
CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask()
{
mbed::ScopedLock<rtos::Mutex> 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<rtos::Mutex> 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<ImplClass>::_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