blob: 2c041636aaec3c31ed9933e22843b5b39fbddb73 [file] [log] [blame]
/*
*
* 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