blob: f6b26025ae9980eb79ec0d1fc9eb818f7730bfd1 [file] [log] [blame]
/*
*
* Copyright (c) 2020 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 declarations of the following classes and
* templates:
*
* - class chip::System::Object
* - template<typename ALIGN, size_t SIZE> union chip::System::ObjectArena
* - template<class T, unsigned int N> class chip::System::ObjectPool
*/
#pragma once
// Include configuration headers
#include <system/SystemConfig.h>
// Include dependent headers
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <utility>
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
#include <memory>
#include <mutex>
#include <vector>
#endif
#include <lib/support/DLLUtil.h>
#include <system/SystemError.h>
#include <system/SystemStats.h>
#ifndef SYSTEM_OBJECT_HWM_TEST_HOOK
#define SYSTEM_OBJECT_HWM_TEST_HOOK()
#endif
namespace chip {
namespace System {
// Forward class and class template declarations
class Layer;
class LayerSockets;
class LayerLwIP;
template <class T, size_t N>
class ObjectPoolStatic;
template <class T>
class ObjectPoolDynamic;
/**
* @class Object
*
* @brief
* This represents a reference-counted object allocated from space contained in an ObjectPool<T, N> object.
*
* @note
* Instance of this class may only be constructed using the related ObjectPool class template. The copy constructor and the
* assignment operator are deleted. A reference counting system is used to track retentions of instances of this class.
* When an object is initially retained, its reference count is one. Additional retentions may increment the reference count.
* When the object is released, the reference count is decremented. When the reference count is zero, the object is recycled
* back to the pool for reallocation. There is no destructor available. Subclasses must be designed to ensure that all
* encapsulated resources are released when the final retention is released and the object is recycled.
*
* While this class is defined as concrete, it should be regarded as abstract.
*/
class DLL_EXPORT Object
{
template <class T, size_t N>
friend class ObjectPoolStatic;
template <class T>
friend class ObjectPoolDynamic;
public:
Object() : mRefCount(0)
{
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
mNext = nullptr;
mPrev = nullptr;
#endif
}
virtual ~Object() {}
/** Test whether this object is retained. Concurrency safe. */
bool IsRetained() const;
void Retain();
void Release();
Layer & SystemLayer() const;
/**< What to do when DeferredRelease fails to post a kEvent_ReleaseObj. */
enum ReleaseDeferralErrorTactic
{
kReleaseDeferralErrorTactic_Ignore, /**< No action. */
kReleaseDeferralErrorTactic_Release, /**< Release immediately. */
kReleaseDeferralErrorTactic_Die, /**< Die with message. */
};
protected:
#if CHIP_SYSTEM_CONFIG_USE_LWIP
void DeferredRelease(LayerLwIP * aSystemLayer, ReleaseDeferralErrorTactic aTactic);
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
private:
Object(const Object &) = delete;
Object & operator=(const Object &) = delete;
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
Object * mNext;
Object * mPrev;
std::mutex * mMutexRef;
#endif
unsigned int mRefCount; /**< Count of remaining calls to Release before object is dead. */
/**
* @brief
* Attempts to perform an initial retention of this object, in a THREAD-SAFE manner.
*
* @note
* If reference count is non-zero, tryCreate will fail and return false.
* If reference count is zero, then:
* - reference count will be set to 1
* - The size of the created object (assumed to be derived from SystemObject) is aOctects.
* the method will memset to 0 the bytes following sizeof(SystemObject).
*
* Typical usage is like:
* class Foo: public SystemObject {...}
* ....
* Foo foo;
* foo.TryCreate(sizeof(foo));
*
* IMPORTANT inheritance precondition:
* 0 memset assumes that SystemObject is the top of the inheritance. This will NOT work properly:
* class Bar: public Baz, SystemObject {...}
* Bar bar;
* bar.TryCreate(sizeof(bar)); /// NOT safe: this will clear sizeof(Baz) extra bytes in unallocated space.
*/
bool TryCreate(size_t aOctets);
public:
void * AppState; /**< Generic pointer to app-specific data associated with the object. */
};
/**
* @brief
* Tests whether this object is retained.
*
* @note
* No memory barrier is applied. If this returns \c false in one thread context, then it does not imply that another thread
* cannot have previously retained the object for \c aLayer. If it returns \c true, then the logic using \c mRefCount is
* responsible for ensuring concurrency safety for this object.
*/
inline bool Object::IsRetained() const
{
return this->mRefCount > 0;
}
/**
* @brief
* Increments the reference count for the CHIP System Layer object. The object is assumed to be live.
*/
inline void Object::Retain()
{
__sync_fetch_and_add(&this->mRefCount, 1);
}
/**
* @brief
* A union template used for representing a well-aligned block of memory.
*
* @tparam ALIGN a typename with the alignment properties for the block.
* @tparam SIZE a constant size of the block in bytes.
*/
template <typename ALIGN, size_t SIZE>
union ObjectArena
{
uint8_t uMemory[SIZE];
ALIGN uAlign;
};
/**
* @class ObjectPoolStatic
*
* @brief
* This is the data class for static object pools.
*
* @tparam T a subclass of Object to be allocated.
* @tparam N a positive integer number of objects of class T to allocate from the arena.
*/
template <typename T, size_t N>
class ObjectPoolStatic
{
public:
T * TryCreate()
{
T * lReturn = nullptr;
(void) static_cast<Object *>(lReturn); /* In C++-11, this would be a static_assert that T inherits Object. */
size_t lIndex = 0;
for (lIndex = 0; lIndex < N; ++lIndex)
{
T & lObject = reinterpret_cast<T *>(mArena.uMemory)[lIndex];
if (lObject.TryCreate(sizeof(T)))
{
lReturn = &lObject;
break;
}
}
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
size_t lNumInUse = 0;
if (lReturn != nullptr)
{
lIndex++;
lNumInUse = lIndex;
GetNumObjectsInUse(lIndex, lNumInUse);
}
else
{
lNumInUse = N;
}
UpdateHighWatermark(lNumInUse);
#endif
return lReturn;
}
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
/**
* Return the number of objects in use starting at a given index
*
* @param[in] aStartIndex The index to start counting from; pass 0 to count over
* the whole pool.
* @param[in/out] aNumInUse The number of objects in use. If aStartIndex is not 0,
* the function adds to the counter without resetting it first.
*/
void GetNumObjectsInUse(size_t aStartIndex, size_t & aNumInUse)
{
size_t count = 0;
for (size_t lIndex = aStartIndex; lIndex < N; ++lIndex)
{
T & lObject = reinterpret_cast<T *>(mArena.uMemory)[lIndex];
if (lObject.IsRetained())
{
count++;
}
}
if (aStartIndex == 0)
{
aNumInUse = 0;
}
aNumInUse += count;
}
void UpdateHighWatermark(const size_t & aCandidate)
{
size_t lTmp;
while (aCandidate > (lTmp = mHighWatermark))
{
SYSTEM_OBJECT_HWM_TEST_HOOK();
(void) __sync_bool_compare_and_swap(&mHighWatermark, lTmp, aCandidate);
}
}
volatile size_t mHighWatermark;
#endif
void GetStatistics(chip::System::Stats::count_t & aNumInUse, chip::System::Stats::count_t & aHighWatermark)
{
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
size_t lNumInUse;
size_t lHighWatermark;
GetNumObjectsInUse(0, lNumInUse);
lHighWatermark = mHighWatermark;
if (lNumInUse > CHIP_SYS_STATS_COUNT_MAX)
{
lNumInUse = CHIP_SYS_STATS_COUNT_MAX;
}
if (lHighWatermark > CHIP_SYS_STATS_COUNT_MAX)
{
lHighWatermark = CHIP_SYS_STATS_COUNT_MAX;
}
aNumInUse = static_cast<chip::System::Stats::count_t>(lNumInUse);
aHighWatermark = static_cast<chip::System::Stats::count_t>(lHighWatermark);
#endif
}
/**
* @brief
* Run a functor for each active object in the pool
*
* @param function The functor of type `bool (*)(T*)`, return false to break the iteration
* @return bool Returns false if broke during iteration
*/
template <typename Function>
bool ForEachActiveObject(Function && function)
{
for (size_t i = 0; i < N; ++i)
{
T & lObject = reinterpret_cast<T *>(mArena.uMemory)[i];
if (lObject.IsRetained())
{
if (!function(&lObject))
return false;
}
}
return true;
}
void Reset()
{
memset(mArena.uMemory, 0, N * sizeof(T));
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
mHighWatermark = 0;
#endif
}
ObjectArena<void *, N * sizeof(T)> mArena;
};
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
/**
* @class ObjectPoolDynamic
*
* @brief
* This is the data class for dynamic object pools.
*
* @tparam T a subclass of Object to be allocated.
*/
template <typename T>
class ObjectPoolDynamic
{
public:
T * TryCreate()
{
T * newNode = new T();
if (newNode->TryCreate(sizeof(T)))
{
std::lock_guard<std::mutex> lock(mMutex);
Object * p = &mDummyHead;
if (p->mNext)
{
p->mNext->mPrev = newNode;
}
newNode->mNext = p->mNext;
p->mNext = newNode;
newNode->mPrev = p;
newNode->mMutexRef = &mMutex;
return newNode;
}
else
{
delete newNode;
return nullptr;
}
}
/**
* @brief
* Run a functor for each active object in the pool
*
* @param function The functor of type `bool (*)(T*)`, return false to break the iteration
* @return bool Returns false if broke during iteration
*/
template <typename Function>
bool ForEachActiveObject(Function && function)
{
std::lock_guard<std::mutex> lock(mMutex);
Object * p = mDummyHead.mNext;
while (p)
{
if (!function(static_cast<T *>(p)))
{
return false;
}
p = p->mNext;
}
return true;
}
void Reset()
{
std::lock_guard<std::mutex> lock(mMutex);
Object * p = mDummyHead.mNext;
while (p)
{
Object * del = p;
p = p->mNext;
delete del;
}
mDummyHead.mNext = nullptr;
}
void GetStatistics(chip::System::Stats::count_t & aNumInUse, chip::System::Stats::count_t & aHighWatermark) {}
std::mutex mMutex;
Object mDummyHead;
friend class TestObject;
};
#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
template <typename T, unsigned int N>
using ObjectPool = ObjectPoolDynamic<T>;
#else
template <typename T, unsigned int N>
using ObjectPool = ObjectPoolStatic<T, N>;
#endif
enum class ObjectPoolMem
{
kStatic,
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
kDynamic
#endif
};
template <typename T, size_t N, ObjectPoolMem P>
class MemTypeObjectPool;
template <typename T, size_t N>
class MemTypeObjectPool<T, N, ObjectPoolMem::kStatic> : public ObjectPoolStatic<T, N>
{
};
#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
template <typename T, size_t N>
class MemTypeObjectPool<T, N, ObjectPoolMem::kDynamic> : public ObjectPoolDynamic<T>
{
};
#endif
} // namespace System
} // namespace chip