使用boost :: intrusive_ptr

时间:2017-12-28 02:55:49

标签: boost c++14 shared-ptr

我有一个用例,其中一个线程将消息读入一个大缓冲区,并将处理分配给一堆线程。之后,缓冲区由多个线程共享。它是只读的,当最后一个线程完成时,必须释放缓冲区。缓冲区是从无锁的slab分配器中分配的。

我最初的设计是使用shared_ptr作为缓冲区。但缓冲区可以有不同的大小。我绕道而行就是做这样的事情。

struct SharedBuffer {
    SharedBuffer (uint16_t len, std::shared_ptr<void> ptr)
                 : _length(len), _buf(std::move(ptr))
    {
    }
    uint8_t data () { return (uint8_t *)_buf.get(); }
    uint16_t length 
    std::shared_ptr<void> _buf; // type-erase the shared_ptr as the SharedBuffer
                                // need to stored in some other structs
};

现在分配器将像这样分配shared_ptr:

SharedBuffer allocate (size_t size)
{
     auto buf = std::allocate_shared<std::array<uint8_t, 16_K>>(myallocator);
     return SharedBuffer{16_K, buf}; // type erase the std::array
}

并且SharedBuffer被排队到每个想要它的线程。

现在我想,我正在做很多不必要的事情,我可以用下面的方案来处理boost :: intrusive_ptr。事情有点C'ish-因为我使用可变大小的数组。为了简单起见,我在这里使用运算符new()更改了slab分配器。我想运行它以查看此实现是否正常。

template <typename T>
inline int atomicIncrement (T* t)
{
    return __atomic_add_fetch(&t->_ref, 1, __ATOMIC_ACQUIRE);
}

template <typename T>
inline int atomicDecrement (T* t)
{
    return __atomic_sub_fetch(&t->_ref, 1, __ATOMIC_RELEASE);
}

class SharedBuffer {
public:

    friend int atomicIncrement<SharedBuffer>(SharedBuffer*);
    friend int atomicDecrement<SharedBuffer>(SharedBuffer*);

    SharedBuffer(uint16_t len) : _length(len) {}

    uint8_t *data ()
    {
        return &_data[0];
    }

    uint16_t length () const
    {
        return _length;
    }

private:

    int              _ref{0};
    const uint16_t   _length;
    uint8_t          _data[];
};

using SharedBufferPtr = boost::intrusive_ptr<SharedBuffer>;

SharedBufferPtr allocate (size_t size)
{
    // dummy implementation
    void *p = ::operator new (size + sizeof(SharedBuffer));

    // I am not explicitly constructing the array of uint8_t
    return new (p) SharedBuffer(size);
}

void deallocate (SharedBuffer* sbuf)
{
    sbuf->~SharedBuffer();

    // dummy implementation
    ::operator delete ((void *)sbuf);
}

void intrusive_ptr_add_ref(SharedBuffer* sbuf)
{
    atomicIncrement(sbuf);
}

void intrusive_ptr_release (SharedBuffer* sbuf)
{
    if (atomicDecrement(sbuf) == 0) {
        deallocate(sbuf);
    }
}

1 个答案:

答案 0 :(得分:0)

我会使用更简单的实现(使用shared_ptr),除非您避免特定问题(即首先配置文件)。

  

附注 :您可以将boost::shared_pointer<>boost::make_shared<T[]>(N)一起使用,[{1}}将其添加到标准库中

请注意allocate_shared已经将控制块嵌入到相同的分配中,就像使用侵入式方法一样。

最后,我使用std::atomic_int所以你有一个明确的合同,不能(意外地)使用错误。与此同时,它将消除剩余的复杂性。