| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * |
| * 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 implements WatchableEventManager using libevent. |
| */ |
| |
| #include <platform/CHIPDeviceBuildConfig.h> |
| #include <support/CodeUtils.h> |
| #include <system/SystemLayer.h> |
| #include <system/WatchableEventManager.h> |
| #include <system/WatchableSocket.h> |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ |
| |
| namespace chip { |
| namespace Mdns { |
| void GetMdnsTimeout(timeval & timeout); |
| void HandleMdnsTimeout(); |
| } // namespace Mdns |
| } // namespace chip |
| |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ |
| |
| #ifndef CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS |
| #define CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS 1 // TODO(#5556): default to off |
| #endif |
| |
| namespace chip { |
| namespace System { |
| |
| namespace { |
| |
| System::SocketEvents SocketEventsFromLibeventFlags(short eventFlags) |
| { |
| return System::SocketEvents() |
| .Set(SocketEventFlags::kRead, eventFlags & EV_READ) |
| .Set(SocketEventFlags::kWrite, eventFlags & EV_WRITE); |
| } |
| |
| void TimeoutCallbackHandler(evutil_socket_t fd, short eventFlags, void * data) |
| { |
| event * const ev = reinterpret_cast<event *>(data); |
| evtimer_del(ev); |
| } |
| |
| } // anonymous namespace |
| |
| void WatchableEventManager::Init(System::Layer & systemLayer) |
| { |
| #if CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS |
| static bool enabled_event_debug_mode = false; |
| if (!enabled_event_debug_mode) |
| { |
| enabled_event_debug_mode = true; |
| event_enable_debug_mode(); |
| } |
| #endif // CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS |
| |
| mEventBase = event_base_new(); |
| mTimeoutEvent = evtimer_new(mEventBase, TimeoutCallbackHandler, event_self_cbarg()); |
| mActiveSockets = nullptr; |
| mSystemLayer = &systemLayer; |
| } |
| |
| void WatchableEventManager::PrepareEvents() |
| { |
| // TODO(#5556): Integrate timer platform details with WatchableEventManager. |
| timeval nextTimeout = { 0, 0 }; |
| PrepareEventsWithTimeout(nextTimeout); |
| } |
| |
| void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout) |
| { |
| // TODO(#5556): Integrate timer platform details with WatchableEventManager. |
| mSystemLayer->GetTimeout(nextTimeout); |
| if (nextTimeout.tv_sec || nextTimeout.tv_usec) |
| { |
| evtimer_add(mTimeoutEvent, &nextTimeout); |
| } |
| } |
| |
| void WatchableEventManager::WaitForEvents() |
| { |
| VerifyOrDie(mEventBase != nullptr); |
| event_base_loop(mEventBase, EVLOOP_ONCE); |
| } |
| |
| void WatchableEventManager::HandleEvents() |
| { |
| mSystemLayer->HandleTimeout(); |
| |
| #if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ |
| chip::Mdns::HandleMdnsTimeout(); |
| #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ |
| |
| while (mActiveSockets != nullptr) |
| { |
| WatchableSocket * const watcher = mActiveSockets; |
| mActiveSockets = watcher->mActiveNext; |
| watcher->InvokeCallback(); |
| } |
| } |
| |
| void WatchableEventManager::Shutdown() |
| { |
| event_base_loopbreak(mEventBase); |
| event_free(mTimeoutEvent); |
| mTimeoutEvent = nullptr; |
| event_base_free(mEventBase); |
| mEventBase = nullptr; |
| } |
| |
| void WatchableEventManager::Signal() |
| { |
| /* |
| * Wake up the I/O thread by writing a single byte to the wake pipe. |
| * |
| * If p WakeIOThread() is being called from within an I/O event callback, then writing to the wake pipe can be skipped, |
| * since the I/O thread is already awake. |
| * |
| * Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which |
| * case the select calling thread is going to wake up anyway. |
| */ |
| #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| if (pthread_equal(mSystemLayer->mHandleSelectThread, pthread_self())) |
| { |
| return; |
| } |
| #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING |
| |
| // Send notification to wake up the select call. |
| CHIP_ERROR status = mWakeEvent.Notify(); |
| if (status != CHIP_NO_ERROR) |
| { |
| ChipLogError(chipSystemLayer, "System wake event notify failed: %" CHIP_ERROR_FORMAT, ChipError::FormatError(status)); |
| } |
| } |
| |
| // static |
| void WatchableEventManager::LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data) |
| { |
| WatchableSocket * const watcher = reinterpret_cast<WatchableSocket *>(data); |
| VerifyOrDie(watcher != nullptr); |
| VerifyOrDie(watcher->mFD == fd); |
| |
| watcher->mPendingIO = SocketEventsFromLibeventFlags(eventFlags); |
| |
| // Add to active list. |
| WatchableSocket ** pp = &watcher->mSharedState->mActiveSockets; |
| while (*pp != nullptr) |
| { |
| if (*pp == watcher) |
| { |
| return; |
| } |
| pp = &(*pp)->mActiveNext; |
| } |
| *pp = watcher; |
| watcher->mActiveNext = nullptr; |
| } |
| |
| void WatchableEventManager::RemoveFromQueueIfPresent(WatchableSocket * watcher) |
| { |
| VerifyOrDie(watcher != nullptr); |
| VerifyOrDie(watcher->mSharedState == this); |
| |
| WatchableSocket ** pp = &mActiveSockets; |
| while (*pp != nullptr) |
| { |
| if (*pp == watcher) |
| { |
| *pp = watcher->mActiveNext; |
| return; |
| } |
| pp = &(*pp)->mActiveNext; |
| } |
| } |
| |
| } // namespace System |
| } // namespace chip |