【iceoryx】 Freelist


在iceoryx中,publisher 获取一块chunk,填充数据,发布;供subscriber 读取。这一系列操作要用到内存管理中的freeList。

FreeList的init()

void LoFFLi::init(cxx::not_null<Index_t*> freeIndicesMemory, const uint32_t capacity) noexcept
{
    cxx::Expects(capacity > 0 && "A capacity of 0 is not supported!");
    constexpr uint32_t INTERNALLY_RESERVED_INDICES{1U};
    cxx::Expects(capacity < (std::numeric_limits<Index_t>::max() - INTERNALLY_RESERVED_INDICES)
                 && "Requested capacityexceeds limits!");
    cxx::Expects(m_head.is_lock_free() && "std::atomic<LoFFLi::Node> must be lock-free!");

    m_nextFreeIndex = freeIndicesMemory;
    m_size = capacity;
    m_invalidIndex = m_size + 1;

    if (m_nextFreeIndex != nullptr)
    {
        for (uint32_t i = 0; i < m_size + 1; i++)
        {
            // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) upper limit of index is set by m_size
            m_nextFreeIndex[i] = i + 1;
        }
    }
}

init看起来相对简单,拿 [[segment.mempool]] , size = 128 , count = 10000 举例,所做的操作如下:
1、m_invalidIndex = 10001;
2、m_size = 10000;
3、m_nextFreeIndex[10000] 依此填充 1,2,3,4…10001

publisher getChunk()

追踪代码可知,最终调用MemoryManager::getChunk(const ChunkSettings& chunkSettings)
核心代码:

   for (auto& memPool : m_memPoolVector)
    {
        uint32_t chunkSizeOfMemPool = memPool.getChunkSize();
        if (chunkSizeOfMemPool >= requiredChunkSize)
        {
            chunk = memPool.getChunk();
            memPoolPointer = &memPool;
            aquiredChunkSize = chunkSizeOfMemPool;
            break;
        }
    }

void* MemPool::getChunk() :

void* MemPool::getChunk() noexcept
{
    uint32_t l_index{0U};
    if (!m_freeIndices.pop(l_index))
    {
        std::cerr << "Mempool [m_chunkSize = " << m_chunkSize << ", numberOfChunks = " << m_numberOfChunks
                  << ", used_chunks = " << m_usedChunks << " ] has no more space left" << std::endl;
        return nullptr;
    }

    /// @todo: verify that m_usedChunk is not changed during adjustMInFree
    ///         without changing m_minFree
    m_usedChunks.fetch_add(1U, std::memory_order_relaxed);
    adjustMinFree();

    return m_rawMemory + l_index * m_chunkSize;
}

关键在 m_freeIndices.pop(l_index) 这里传入的 l_index 。

看一下freelist的pop

bool LoFFLi::pop(Index_t& index) noexcept
{
    Node oldHead = m_head.load(std::memory_order_acquire);
    Node newHead = oldHead;

    do
    {
        // we are empty if next points to an element with index of Size
        if (oldHead.indexToNextFreeIndex >= m_size)
        {
            return false;
        }

        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) upper limit of index set by m_size
        newHead.indexToNextFreeIndex = m_nextFreeIndex[oldHead.indexToNextFreeIndex]; //<1>
        newHead.abaCounter += 1;
    } while (!m_head.compare_exchange_weak(oldHead, newHead, std::memory_order_acq_rel, std::memory_order_acquire)); //<2>

    /// comes from outside, is not shared and therefore no synchronization is needed
    index = oldHead.indexToNextFreeIndex;  //<3>
    /// @todo what if interrupted here an another thread guesses the index and
    ///         calls push
    /// @brief murphy case: m_nextFreeIndex does not require any synchronization since it
    ///         either is used by the same thread in push or it is given to another
    ///         thread which performs the cleanup and during this process a synchronization
    ///         is required
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    m_nextFreeIndex[index] = m_invalidIndex; //<4>

    /// we need to synchronize m_nextFreeIndex with push so that we can perform a validation
    /// check right before push to avoid double free's
    std::atomic_thread_fence(std::memory_order_release);

    return true;
}

先看一下 LoFFLi 类的成员:

class LoFFLi
{
  private:
    struct alignas(8) Node
    {
        Index_t indexToNextFreeIndex;
        uint32_t abaCounter;
    };
	std::atomic<Node> m_head{{0U, 1U}};
}

它有一个m_head{0,1};
因此 <1>处 newHead.indexToNextFreeIndex = m_nextFreeIndex[0];
由init()知,m_nextFreeIndex[0] = 1;
<2> 如果 m_head 当前的值 == oldHead ,则将 m_head = newHead,并返回 true;
反之 ,oldHead = m_head ,返回false。
这里主要是担心 执行 do 下面的操作时被打断,考虑以下情况:
此时 index = 0;若此时有一线程抢占CPU并将 index = 0 的chunk取走,则此时 m_head = {1,2};
oldHead = {1,2},newHead.indexToNextFreeIndex = m_nextFreeIndex[1],去get index = 1 的 chunk。

后面操作 index = 0 , m_nextFreeIndex[0] = 10001; 然后将index 返回。

MemPool::freeChunk(const void* chunk)

当需要free该chunk时,调用 m_freeIndices.push(index):

bool LoFFLi::push(const Index_t index) noexcept
{
    /// we synchronize with m_nextFreeIndex in pop to perform the validity check
    std::atomic_thread_fence(std::memory_order_release);

    /// we want to avoid double free's therefore we check if the index was acquired
    /// in pop and the push argument "index" is valid
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) index is limited by capacity
    if (index >= m_size || m_nextFreeIndex[index] != m_invalidIndex) //<1>
    {
        return false;
    }

    Node oldHead = m_head.load(std::memory_order_acquire);
    Node newHead = oldHead;

    do
    {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) index is limited by capacity
        m_nextFreeIndex[index] = oldHead.indexToNextFreeIndex; //<2>
        newHead.indexToNextFreeIndex = index; //<3>
        newHead.abaCounter += 1;
    } while (!m_head.compare_exchange_weak(oldHead, newHead, std::memory_order_acq_rel, std::memory_order_acquire));

    return true;
}

假定index = 0
<1>处判断 传入的 m_nextFreeIndex[0] 是否 = m_invalidIndex,因为pop时已置为 m_invalidIndex;
<2> oldHead.indexToNextFreeIndex = (上面 push 时) 1 ,因此 m_nextFreeIndex[0] = 1;
<3> newHead.indexToNextFreeIndex =0 ,即 m_head ={0,3} ;
这样就把 index = 0 的chunk free了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值