| /* |
| * |
| * Copyright (c) 2020-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 declares the abstraction of socket (file descriptor) events. |
| */ |
| |
| #pragma once |
| |
| // Include configuration headers |
| #include <system/SystemConfig.h> |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS |
| |
| #include <support/BitFlags.h> |
| #include <system/SystemError.h> |
| |
| #include <unistd.h> |
| |
| namespace chip { |
| |
| namespace System { |
| |
| class Layer; |
| |
| enum class SocketEventFlags : uint8_t |
| { |
| kRead = 0x1, /**< Bit flag indicating if there is a read event on a socket. */ |
| kWrite = 0x2, /**< Bit flag indicating if there is a write event on a socket. */ |
| kExcept = 0x4, /**< Bit flag indicating if there is an exceptional condition on a socket (e.g. out-of-band data). */ |
| kError = 0x8, /**< Bit flag indicating if there is an error event on a socket. */ |
| }; |
| |
| using SocketEvents = BitFlags<SocketEventFlags>; |
| |
| /** |
| * @class WatchableEventManager |
| * |
| * An instance of this type is contained in System::Layer. Its purpose is to hold socket-event system state |
| * or methods available to every associated instance of WatchableSocket. |
| * |
| * It MUST provide at least two methods: |
| * |
| * void Init(System::Layer & systemLayer) -- called from System::Layer::Init() |
| * void Shutdown() -- called from System::Layer::Shutdown() |
| * |
| * Other contents depend on the contract between socket-event implementation and platform layer implementation. |
| * For POSIX-like platforms, WatchableEventManager provides a set of functions called from the event loop: |
| * |
| * void EventLoopBegins() -- Called before the first iterations of the event loop. |
| * void PrepareEvents() -- Called at the start of each iteration of the event loop. |
| * void WaitForEvents() -- Called on each iteration of the event loop, between PrepareEvents() and HandleEvents(). |
| * Uniquely, this method gets called with the CHIP stack NOT locked, so it can block. |
| * For example, the select()-based implementation calls select() here. |
| * void HandleEvents() -- Called at the end of each iteration of the event loop. |
| * void EventLoopEnds() -- Called after the last iteration of the event loop. |
| */ |
| class WatchableEventManager; |
| |
| /** |
| * @class WatchableSocket |
| * |
| * Users of a WatchableSocket should interact with it using the methods defined by WatchableSocketBasis. |
| * |
| * Implementations of WatchableSocket must inherit `public WatchableSocketBasis<WatchableSocket>` |
| * and provide the following methods, which are invoked by the corresponding WatchableSocketBasis functions: |
| * |
| * void OnInit() |
| * void OnAttach() |
| * void OnClose() |
| * void OnRequestCallbackOnPendingRead() |
| * void OnRequestCallbackOnPendingWrite() |
| * void OnClearCallbackOnPendingRead() |
| * void OnClearCallbackOnPendingWrite() |
| * |
| */ |
| class WatchableSocket; |
| |
| /** |
| * @class WatchableSocketBasis |
| * |
| * This class provides the interface used by platform-independent parts of the CHIP stack. |
| * |
| * The general pattern for using a WatchableSocket s is: |
| * |
| * s.Init(WatchableEventManager) |
| * s.Attach(fd) |
| * s.SetCallback(callbackFunction, callbackData) |
| * s.{Request|Clear}CallbackOnPending{Read|Write}() |
| * ... |
| * s.Close() |
| * |
| */ |
| template <class Impl> |
| class WatchableSocketBasis |
| { |
| public: |
| enum : int |
| { |
| kInvalidFd = -1 |
| }; |
| |
| /** |
| * Initialize a WatchableSocket. |
| * |
| * @param[in] manager Reference to shared socket-event state (which must already have been initialized). |
| */ |
| Impl & Init(WatchableEventManager & manager) |
| { |
| mFD = kInvalidFd; |
| mPendingIO.ClearAll(); |
| mCallback = nullptr; |
| mCallbackData = 0; |
| mSharedState = &manager; |
| Impl * const impl = static_cast<Impl *>(this); |
| impl->OnInit(); |
| return *impl; |
| } |
| |
| /** |
| * Associate this WatchableSocket with a file descriptor. |
| * |
| * @param[in] fd An open file descriptor. |
| */ |
| Impl & Attach(int fd) |
| { |
| mFD = fd; |
| Impl * const impl = static_cast<Impl *>(this); |
| impl->OnAttach(); |
| return *impl; |
| } |
| |
| /** |
| * Disassociate this WatchableSocket from its file descriptor. |
| * |
| * @returns the file descriptor. |
| */ |
| int ReleaseFD() |
| { |
| static_cast<Impl *>(this)->OnClose(); |
| const int fd = mFD; |
| mFD = kInvalidFd; |
| return fd; |
| } |
| |
| /** |
| * Close the associated file descriptor. |
| * |
| * @returns the return value of `close(2)`. |
| */ |
| int Close() { return close(ReleaseFD()); } |
| |
| /** |
| * Test whether there is an associated open file descriptor. |
| */ |
| bool HasFD() const { return mFD >= 0; } |
| |
| /** |
| * Get the associated open file descriptor. |
| */ |
| int GetFD() const { return mFD; } |
| |
| /** |
| * Indicate that the socket-event system should invoke the registered callback when the file descriptor is ready to read. |
| */ |
| Impl & RequestCallbackOnPendingRead(bool request = true) |
| { |
| Impl * const impl = static_cast<Impl *>(this); |
| if (request) |
| { |
| impl->OnRequestCallbackOnPendingRead(); |
| } |
| return *impl; |
| } |
| |
| /** |
| * Indicate that the socket-event system should invoke the registered callback when the file descriptor is ready to write. |
| */ |
| Impl & RequestCallbackOnPendingWrite(bool request = true) |
| { |
| Impl * const impl = static_cast<Impl *>(this); |
| if (request) |
| { |
| impl->OnRequestCallbackOnPendingWrite(); |
| } |
| return *impl; |
| } |
| |
| /** |
| * Indicate that the socket-event system need not invoke the registered callback when the file descriptor is ready to read. |
| */ |
| Impl & ClearCallbackOnPendingRead() |
| { |
| Impl * const impl = static_cast<Impl *>(this); |
| impl->OnClearCallbackOnPendingRead(); |
| return *impl; |
| } |
| |
| /** |
| * Indicate that the socket-event system need not invoke the registered callback when the file descriptor is ready to write. |
| */ |
| Impl & ClearCallbackOnPendingWrite() |
| { |
| Impl * const impl = static_cast<Impl *>(this); |
| impl->OnClearCallbackOnPendingWrite(); |
| return *impl; |
| } |
| |
| /** |
| * The callback is passed a reference to the WatchableSocket for which the requested event(s) are ready. |
| */ |
| using Callback = void (*)(WatchableSocket & socket); |
| |
| /** |
| * Register a callback function. |
| * |
| * The callback will be invoked (with the CHIP stack lock held) when requested event(s) are ready. |
| * |
| * @param[in] callback Function invoked when event(s) are ready. |
| * @param[in] data Arbitrary data accessible within a callback function. |
| */ |
| Impl & SetCallback(Callback callback, intptr_t data) |
| { |
| mCallback = callback; |
| mCallbackData = data; |
| return *static_cast<Impl *>(this); |
| } |
| |
| /** |
| * Retrieve callback data. |
| * |
| * @returns the pointer supplied to SetCallback(). |
| */ |
| intptr_t GetCallbackData() const { return mCallbackData; } |
| |
| /** |
| * Inside a callback function, get the pending SocketEvents. |
| */ |
| SocketEvents GetPendingEvents() const { return mPendingIO; } |
| |
| /** |
| * Inside a callback function, test whether the file descriptor is ready to read. |
| */ |
| bool HasPendingRead() const { return mPendingIO.Has(SocketEventFlags::kRead); } |
| |
| /** |
| * Inside a callback function, test whether the file descriptor is ready to write. |
| */ |
| bool HasPendingWrite() const { return mPendingIO.Has(SocketEventFlags::kWrite); } |
| |
| /** |
| * Inside a callback function, test whether there is an exceptional condition (e.g. out-of-band data) |
| * associated with the file descriptor. |
| */ |
| bool HasPendingException() const { return mPendingIO.Has(SocketEventFlags::kExcept); } |
| |
| /** |
| * Inside a callback function, test whether there is an error condition associated with the file descriptor. |
| */ |
| bool HasPendingError() const { return mPendingIO.Has(SocketEventFlags::kError); } |
| |
| /** |
| * Inside a callback function, reset the set of pending events. |
| */ |
| void ClearPendingIO() { mPendingIO.ClearAll(); } |
| |
| protected: |
| void InvokeCallback() |
| { |
| if (mCallback != nullptr) |
| { |
| mCallback(static_cast<Impl &>(*this)); |
| } |
| } |
| |
| int mFD; |
| SocketEvents mPendingIO; |
| Callback mCallback; |
| intptr_t mCallbackData; |
| WatchableEventManager * mSharedState; |
| }; |
| |
| } // namespace System |
| } // namespace chip |
| |
| #define INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE 1 |
| #ifdef CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE |
| #include CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE |
| #else // CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE |
| #include <system/WatchableSocketSelect.h> |
| #endif // CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE |
| #undef INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE |
| |
| namespace chip { |
| namespace System { |
| |
| using ::chip::System::Error; |
| |
| /** |
| * @class WakeEvent |
| * |
| * An instance of this type is contained in System::Layer. Its purpose is to allow other threads |
| * to wake the event loop thread via System::Layer::WakeIOThread(). |
| */ |
| class WakeEvent |
| { |
| public: |
| Error Open(WatchableEventManager & watchState); /**< Initialize the pipeline */ |
| Error Close(); /**< Close both ends of the pipeline. */ |
| |
| int GetNotifFD() const { return mFD.GetFD(); } |
| |
| Error Notify(); /**< Set the event. */ |
| void Confirm(); /**< Clear the event. */ |
| static void Confirm(WatchableSocket & socket) { reinterpret_cast<WakeEvent *>(socket.GetCallbackData())->Confirm(); } |
| |
| private: |
| #if CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE |
| int mWriteFD; |
| #endif |
| WatchableSocket mFD; |
| }; |
| |
| } // namespace System |
| } // namespace chip |
| |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS |