| /* |
| * |
| * Copyright (c) 2020-2021 Project CHIP Authors |
| * Copyright (c) 2016-2017 Nest Labs, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file contains definitions of the chip::System::Layer |
| * class methods and related data and functions. |
| */ |
| |
| // Include module header |
| #include <system/SystemLayer.h> |
| |
| // Include common private header |
| #include "SystemLayerPrivate.h" |
| |
| // Include local headers |
| #include <system/SystemClock.h> |
| #include <system/SystemTimer.h> |
| |
| // Include additional CHIP headers |
| #include <platform/LockTracker.h> |
| #include <support/CHIPMem.h> |
| #include <support/CodeUtils.h> |
| #include <support/DLLUtil.h> |
| #include <support/logging/CHIPLogging.h> |
| |
| // Include system and language headers |
| #include <stddef.h> |
| #include <string.h> |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| |
| #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| #include <pthread.h> |
| |
| // Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one. |
| #ifndef PTHREAD_NULL |
| #define PTHREAD_NULL 0 |
| #endif // PTHREAD_NULL |
| #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| |
| namespace chip { |
| namespace System { |
| |
| Layer::Layer() : mLayerState(kLayerState_NotInitialized), mPlatformData(nullptr) |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| if (!sSystemEventHandlerDelegate.IsInitialized()) |
| sSystemEventHandlerDelegate.Init(HandleSystemLayerEvent); |
| |
| this->mEventDelegateList = NULL; |
| this->mTimerList = NULL; |
| this->mTimerComplete = false; |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK |
| #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| this->mHandleSelectThread = PTHREAD_NULL; |
| #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK |
| } |
| |
| CHIP_ERROR Layer::Init() |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| RegisterPOSIXErrorFormatter(); |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| RegisterLwIPErrorFormatter(); |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| if (this->mLayerState != kLayerState_NotInitialized) |
| return CHIP_ERROR_INCORRECT_STATE; |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| mWatchableEvents.Init(*this); |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| this->AddEventHandlerDelegate(sSystemEventHandlerDelegate); |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| this->mLayerState = kLayerState_Initialized; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Layer::Shutdown() |
| { |
| if (this->mLayerState == kLayerState_NotInitialized) |
| return CHIP_ERROR_INCORRECT_STATE; |
| |
| for (size_t i = 0; i < Timer::sPool.Size(); ++i) |
| { |
| Timer * lTimer = Timer::sPool.Get(*this, i); |
| |
| if (lTimer != nullptr) |
| { |
| lTimer->Cancel(); |
| } |
| } |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| mWatchableEvents.Shutdown(); |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| |
| this->mLayerState = kLayerState_NotInitialized; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Layer::NewTimer(Timer *& aTimerPtr) |
| { |
| Timer * lTimer = nullptr; |
| |
| if (this->State() != kLayerState_Initialized) |
| return CHIP_ERROR_INCORRECT_STATE; |
| |
| lTimer = Timer::sPool.TryCreate(*this); |
| aTimerPtr = lTimer; |
| |
| if (lTimer == nullptr) |
| { |
| ChipLogError(chipSystemLayer, "Timer pool EMPTY"); |
| return CHIP_ERROR_NO_MEMORY; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * @brief |
| * This method starts a one-shot timer. |
| * |
| * @note |
| * Only a single timer is allowed to be started with the same @a aComplete and @a aAppState |
| * arguments. If called with @a aComplete and @a aAppState identical to an existing timer, |
| * the currently-running timer will first be cancelled. |
| * |
| * @param[in] aMilliseconds Expiration time in milliseconds. |
| * @param[in] aComplete A pointer to the function called when timer expires. |
| * @param[in] aAppState A pointer to the application state object used when timer expires. |
| * |
| * @return CHIP_NO_ERROR On success. |
| * @return CHIP_ERROR_NO_MEMORY If a timer cannot be allocated. |
| * @return Other Value indicating timer failed to start. |
| * |
| */ |
| CHIP_ERROR Layer::StartTimer(uint32_t aMilliseconds, TimerCompleteFunct aComplete, void * aAppState) |
| { |
| CHIP_ERROR lReturn; |
| Timer * lTimer; |
| |
| this->CancelTimer(aComplete, aAppState); |
| lReturn = this->NewTimer(lTimer); |
| SuccessOrExit(lReturn); |
| |
| lReturn = lTimer->Start(aMilliseconds, aComplete, aAppState); |
| if (lReturn != CHIP_NO_ERROR) |
| { |
| lTimer->Release(); |
| } |
| |
| exit: |
| return lReturn; |
| } |
| |
| /** |
| * @brief |
| * This method cancels a one-shot timer, started earlier through @p StartTimer(). |
| * |
| * @note |
| * The cancellation could fail silently in two different ways. If the timer specified by the combination of the callback |
| * function and application state object couldn't be found, cancellation could fail. If the timer has fired, but not yet |
| * removed from memory, cancellation could also fail. |
| * |
| * @param[in] aOnComplete A pointer to the callback function used in calling @p StartTimer(). |
| * @param[in] aAppState A pointer to the application state object used in calling @p StartTimer(). |
| * |
| */ |
| void Layer::CancelTimer(Layer::TimerCompleteFunct aOnComplete, void * aAppState) |
| { |
| if (this->State() != kLayerState_Initialized) |
| return; |
| |
| for (size_t i = 0; i < Timer::sPool.Size(); ++i) |
| { |
| Timer * lTimer = Timer::sPool.Get(*this, i); |
| |
| if (lTimer != nullptr && lTimer->OnComplete == aOnComplete && lTimer->AppState == aAppState) |
| { |
| lTimer->Cancel(); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * @brief |
| * Schedules a function with a signature identical to |
| * `TimerCompleteFunct` to be run as soon as possible on the CHIP |
| * thread. |
| * |
| * @note |
| * This function could, in principle, be implemented as |
| * `StartTimer`. The specification for |
| * `SystemTimer` however permits certain optimizations that might |
| * make that implementation impossible. Specifically, `SystemTimer` |
| * API may only be called from the thread owning the particular |
| * `SystemLayer`, whereas the `ScheduleWork` may be called from |
| * any thread. Additionally, whereas the `SystemTimer` API permits |
| * the invocation of the already expired handler in line, |
| * `ScheduleWork` guarantees that the handler function will be |
| * called only after the current CHIP event completes. |
| * |
| * @param[in] aComplete A pointer to a callback function to be called |
| * when this timer fires. |
| * |
| * @param[in] aAppState A pointer to an application state object to be |
| * passed to the callback function as argument. |
| * |
| * @retval CHIP_ERROR_INCORRECT_STATE If the SystemLayer has |
| * not been initialized. |
| * |
| * @retval CHIP_ERROR_NO_MEMORY If the SystemLayer cannot |
| * allocate a new timer. |
| * |
| * @retval CHIP_NO_ERROR On success. |
| */ |
| CHIP_ERROR Layer::ScheduleWork(TimerCompleteFunct aComplete, void * aAppState) |
| { |
| assertChipStackLockedByCurrentThread(); |
| |
| CHIP_ERROR lReturn; |
| Timer * lTimer; |
| |
| lReturn = this->NewTimer(lTimer); |
| SuccessOrExit(lReturn); |
| |
| lReturn = lTimer->ScheduleWork(aComplete, aAppState); |
| if (lReturn != CHIP_NO_ERROR) |
| { |
| lTimer->Release(); |
| } |
| |
| exit: |
| return lReturn; |
| } |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK |
| |
| bool Layer::GetTimeout(struct timeval & aSleepTime) |
| { |
| if (this->State() != kLayerState_Initialized) |
| return false; |
| |
| const Clock::MonotonicMilliseconds kCurrentTime = Clock::GetMonotonicMilliseconds(); |
| Clock::MonotonicMilliseconds lAwakenTime = kCurrentTime + static_cast<Clock::MonotonicMilliseconds>(aSleepTime.tv_sec) * 1000 + |
| static_cast<uint32_t>(aSleepTime.tv_usec) / 1000; |
| |
| bool anyTimer = false; |
| for (size_t i = 0; i < Timer::sPool.Size(); i++) |
| { |
| Timer * lTimer = Timer::sPool.Get(*this, i); |
| |
| if (lTimer != nullptr) |
| { |
| anyTimer = true; |
| |
| if (!Timer::IsEarlier(kCurrentTime, lTimer->mAwakenTime)) |
| { |
| lAwakenTime = kCurrentTime; |
| break; |
| } |
| |
| if (Timer::IsEarlier(lTimer->mAwakenTime, lAwakenTime)) |
| lAwakenTime = lTimer->mAwakenTime; |
| } |
| } |
| |
| const Clock::MonotonicMilliseconds kSleepTime = lAwakenTime - kCurrentTime; |
| aSleepTime.tv_sec = static_cast<time_t>(kSleepTime / 1000); |
| aSleepTime.tv_usec = static_cast<suseconds_t>((kSleepTime % 1000) * 1000); |
| |
| return anyTimer; |
| } |
| |
| void Layer::HandleTimeout() |
| { |
| assertChipStackLockedByCurrentThread(); |
| |
| #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| this->mHandleSelectThread = pthread_self(); |
| #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| |
| const Clock::MonotonicMilliseconds kCurrentTime = Clock::GetMonotonicMilliseconds(); |
| |
| for (size_t i = 0; i < Timer::sPool.Size(); i++) |
| { |
| Timer * lTimer = Timer::sPool.Get(*this, i); |
| |
| if (lTimer != nullptr && !Timer::IsEarlier(kCurrentTime, lTimer->mAwakenTime)) |
| { |
| lTimer->HandleComplete(); |
| } |
| } |
| |
| #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| this->mHandleSelectThread = PTHREAD_NULL; |
| #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| } |
| |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| LwIPEventHandlerDelegate Layer::sSystemEventHandlerDelegate; |
| |
| bool LwIPEventHandlerDelegate::IsInitialized() const |
| { |
| return this->mFunction != NULL; |
| } |
| |
| void LwIPEventHandlerDelegate::Init(LwIPEventHandlerFunction aFunction) |
| { |
| this->mFunction = aFunction; |
| this->mNextDelegate = NULL; |
| } |
| |
| void LwIPEventHandlerDelegate::Prepend(const LwIPEventHandlerDelegate *& aDelegateList) |
| { |
| this->mNextDelegate = aDelegateList; |
| aDelegateList = this; |
| } |
| |
| /** |
| * This is the dispatch handler for system layer events. |
| * |
| * @param[in,out] aTarget A pointer to the CHIP System Layer object making the post request. |
| * @param[in] aEventType The type of event to post. |
| * @param[in,out] aArgument The argument associated with the event to post. |
| */ |
| CHIP_ERROR Layer::HandleSystemLayerEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) |
| { |
| CHIP_ERROR lReturn = CHIP_NO_ERROR; |
| ; |
| |
| // Dispatch logic specific to the event type |
| switch (aEventType) |
| { |
| case kEvent_ReleaseObj: |
| aTarget.Release(); |
| break; |
| |
| case kEvent_ScheduleWork: |
| static_cast<Timer &>(aTarget).HandleComplete(); |
| break; |
| |
| default: |
| lReturn = CHIP_ERROR_UNEXPECTED_EVENT; |
| break; |
| } |
| |
| return lReturn; |
| } |
| |
| /** |
| * This adds an event handler delegate to the system layer to extend its ability to handle LwIP events. |
| * |
| * @param[in] aDelegate An uninitialied LwIP event handler delegate structure |
| * |
| * @retval CHIP_NO_ERROR On success. |
| * @retval CHIP_ERROR_INVALID_ARGUMENT If the function pointer contained in aDelegate is NULL |
| */ |
| CHIP_ERROR Layer::AddEventHandlerDelegate(LwIPEventHandlerDelegate & aDelegate) |
| { |
| CHIP_ERROR lReturn; |
| |
| VerifyOrExit(aDelegate.mFunction != NULL, lReturn = CHIP_ERROR_INVALID_ARGUMENT); |
| aDelegate.Prepend(this->mEventDelegateList); |
| lReturn = CHIP_NO_ERROR; |
| |
| exit: |
| return lReturn; |
| } |
| |
| /** |
| * This posts an event / message of the specified type with the provided argument to this instance's platform-specific event |
| * queue. |
| * |
| * @param[in,out] aTarget A pointer to the CHIP System Layer object making the post request. |
| * @param[in] aEventType The type of event to post. |
| * @param[in,out] aArgument The argument associated with the event to post. |
| * |
| * @retval CHIP_NO_ERROR On success. |
| * @retval CHIP_ERROR_INCORRECT_STATE If the state of the Layer object is incorrect. |
| * @retval CHIP_ERROR_NO_MEMORY If the event queue is already full. |
| * @retval other Platform-specific errors generated indicating the reason for failure. |
| */ |
| CHIP_ERROR Layer::PostEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) |
| { |
| CHIP_ERROR lReturn = CHIP_NO_ERROR; |
| VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); |
| |
| // Sanity check that this instance and the target layer haven't been "crossed". |
| VerifyOrDieWithMsg(aTarget.IsRetained(*this), chipSystemLayer, "wrong poster! [target %p != this %p]", &(aTarget.SystemLayer()), |
| this); |
| |
| lReturn = Platform::Eventing::PostEvent(*this, aTarget, aEventType, aArgument); |
| if (lReturn != CHIP_NO_ERROR) |
| { |
| ChipLogError(chipSystemLayer, "Failed to queue CHIP System Layer event (type %d): %s", aEventType, ErrorStr(lReturn)); |
| } |
| SuccessOrExit(lReturn); |
| |
| exit: |
| return lReturn; |
| } |
| |
| /** |
| * This is a syntactic wrapper around a platform-specific hook that effects an event loop, waiting on a queue that services this |
| * instance, pulling events off of that queue, and then dispatching them for handling. |
| * |
| * @return #CHIP_NO_ERROR on success; otherwise, a specific error indicating the reason for initialization failure. |
| */ |
| CHIP_ERROR Layer::DispatchEvents() |
| { |
| CHIP_ERROR lReturn = CHIP_NO_ERROR; |
| VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); |
| |
| lReturn = Platform::Eventing::DispatchEvents(*this); |
| SuccessOrExit(lReturn); |
| |
| exit: |
| return lReturn; |
| } |
| |
| /** |
| * This dispatches the specified event for handling by this instance. |
| * |
| * The unmarshalling of the type and arguments from the event is handled by a platform-specific hook which should then call |
| * back to Layer::HandleEvent for the actual dispatch. |
| * |
| * @param[in] aEvent The platform-specific event object to dispatch for handling. |
| * |
| * @return CHIP_NO_ERROR on success; otherwise, a specific error indicating the reason for initialization failure. |
| */ |
| CHIP_ERROR Layer::DispatchEvent(Event aEvent) |
| { |
| CHIP_ERROR lReturn = CHIP_NO_ERROR; |
| VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); |
| |
| lReturn = Platform::Eventing::DispatchEvent(*this, aEvent); |
| SuccessOrExit(lReturn); |
| |
| exit: |
| return lReturn; |
| } |
| |
| /** |
| * This implements the actual dispatch and handling of a CHIP System Layer event. |
| * |
| * @param[in,out] aTarget A reference to the layer object to which the event is targeted. |
| * @param[in] aEventType The event / message type to handle. |
| * @param[in] aArgument The argument associated with the event / message. |
| * |
| * @retval CHIP_NO_ERROR On success. |
| * @retval CHIP_ERROR_INCORRECT_STATE If the state of the InetLayer object is incorrect. |
| * @retval CHIP_ERROR_UNEXPECTED_EVENT If the event type is unrecognized. |
| */ |
| CHIP_ERROR Layer::HandleEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) |
| { |
| const LwIPEventHandlerDelegate * lEventDelegate; |
| CHIP_ERROR lReturn; |
| VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); |
| |
| // Sanity check that this instance and the target layer haven't been "crossed". |
| VerifyOrDieWithMsg(aTarget.IsRetained(*this), chipSystemLayer, "wrong handler! [target %p != this %p]", |
| &(aTarget.SystemLayer()), this); |
| |
| lReturn = CHIP_ERROR_UNEXPECTED_EVENT; |
| lEventDelegate = this->mEventDelegateList; |
| |
| // Prevent the target object from being freed while dispatching the event. |
| aTarget.Retain(); |
| |
| while (lReturn == CHIP_ERROR_UNEXPECTED_EVENT && lEventDelegate != NULL) |
| { |
| lReturn = lEventDelegate->mFunction(aTarget, aEventType, aArgument); |
| lEventDelegate = lEventDelegate->mNextDelegate; |
| } |
| |
| if (lReturn == CHIP_ERROR_UNEXPECTED_EVENT) |
| { |
| ChipLogError(chipSystemLayer, "Unexpected event type %d", aEventType); |
| } |
| |
| /* |
| Release the reference to the target object. When the object's lifetime finally comes to an end, in most cases this will be |
| the release call that decrements the ref count to zero. |
| */ |
| aTarget.Release(); |
| |
| exit: |
| return lReturn; |
| } |
| |
| /** |
| * Start the platform timer with specified millsecond duration. |
| * |
| * @brief |
| * Calls the Platform specific API to start a platform timer. This API is called by the chip::System::Timer class when |
| * one or more timers are active and require deferred execution. |
| * |
| * @param[in] aDelayMilliseconds The timer duration in milliseconds. |
| * |
| * @return CHIP_NO_ERROR on success, error code otherwise. |
| * |
| */ |
| CHIP_ERROR Layer::StartPlatformTimer(uint32_t aDelayMilliseconds) |
| { |
| CHIP_ERROR lReturn = CHIP_NO_ERROR; |
| VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); |
| |
| lReturn = Platform::Eventing::StartTimer(*this, aDelayMilliseconds); |
| SuccessOrExit(lReturn); |
| |
| exit: |
| return lReturn; |
| } |
| |
| /** |
| * Handle the platform timer expiration event. |
| * |
| * @brief |
| * Calls chip::System::Timer::HandleExpiredTimers to handle any expired timers. It is assumed that this API is called |
| * only while on the thread which owns the CHIP System Layer object. |
| * |
| * @return CHIP_NO_ERROR on success, error code otherwise. |
| * |
| */ |
| CHIP_ERROR Layer::HandlePlatformTimer() |
| { |
| CHIP_ERROR lReturn = CHIP_NO_ERROR; |
| VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); |
| |
| lReturn = Timer::HandleExpiredTimers(*this); |
| |
| SuccessOrExit(lReturn); |
| |
| exit: |
| return lReturn; |
| } |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| } // namespace System |
| } // namespace chip |