123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "chromecast/net/io_buffer_pool.h"
- #include <new>
- #include "base/bits.h"
- #include "base/memory/aligned_memory.h"
- #include "base/synchronization/lock.h"
- namespace chromecast {
- // The IOBufferPool allocates IOBuffers and the associated data as a single
- // contiguous buffer. The buffer is laid out like this:
- // |------------Wrapper----------|---data buffer---|
- // |--IOBuffer--|--Internal ptr--|---data buffer---|
- //
- // The contiguous buffer is allocated as a character array, and then a Wrapper
- // instance is placement-newed into it. We return a pointer to the IOBuffer
- // within the Wrapper.
- //
- // When the IOBuffer is deleted (in operator delete), we get a pointer to the
- // beginning of storage for the IOBuffer, which is the same memory location
- // as the Wrapper instance (since the Wrapper has no vtable or base class, this
- // should be true for any compiler). We can therefore cast the "deleted"
- // pointer to a Wrapper* and then reclaim the buffer.
- //
- // All the actual data and logic for the buffer pool is held in the Internal
- // class, which is refcounted with 1 ref for the IOBufferPool owner, and 1 ref
- // for each buffer currently in use (ie, not in the free list). The Internal
- // instance is only deleted when its internal refcount drops to 0; this allows
- // buffers allocated from the pool to be safely used and deleted even after the
- // pool has been destroyed.
- //
- // Note that locking in the Internal methods is optional since it is only needed
- // when threadsafe operation is requested.
- class IOBufferPool::Internal {
- public:
- Internal(size_t buffer_size, size_t max_buffers, bool threadsafe);
- Internal(const Internal&) = delete;
- Internal& operator=(const Internal&) = delete;
- size_t num_allocated() const {
- base::AutoLockMaybe lock(lock_ptr_);
- return num_allocated_;
- }
- size_t num_free() const {
- base::AutoLockMaybe lock(lock_ptr_);
- return num_free_;
- }
- void Preallocate(size_t num_buffers);
- void OwnerDestroyed();
- scoped_refptr<net::IOBuffer> GetBuffer();
- private:
- class Buffer;
- class Wrapper;
- union Storage;
- static constexpr size_t kAlignment = 16;
- static void* AllocateAlignedSpace(size_t buffer_size);
- ~Internal();
- void Reclaim(Wrapper* wrapper);
- const size_t buffer_size_;
- const size_t max_buffers_;
- mutable base::Lock lock_;
- base::Lock* const lock_ptr_;
- Storage* free_buffers_;
- size_t num_allocated_;
- size_t num_free_;
- int refs_;
- };
- class IOBufferPool::Internal::Buffer : public net::IOBuffer {
- public:
- explicit Buffer(char* data) : net::IOBuffer(data) {}
- Buffer(const Buffer&) = delete;
- Buffer& operator=(const Buffer&) = delete;
- private:
- friend class Wrapper;
- ~Buffer() override { data_ = nullptr; }
- static void operator delete(void* ptr);
- };
- class IOBufferPool::Internal::Wrapper {
- public:
- Wrapper(char* data, IOBufferPool::Internal* pool)
- : buffer_(data), pool_(pool) {}
- Wrapper(const Wrapper&) = delete;
- Wrapper& operator=(const Wrapper&) = delete;
- ~Wrapper() = delete;
- static void operator delete(void*) = delete;
- Buffer* buffer() { return &buffer_; }
- void Reclaim() { pool_->Reclaim(this); }
- private:
- Buffer buffer_;
- IOBufferPool::Internal* const pool_;
- };
- union IOBufferPool::Internal::Storage {
- Storage* next; // Pointer to next free buffer.
- Wrapper wrapper;
- };
- void IOBufferPool::Internal::Buffer::operator delete(void* ptr) {
- Wrapper* wrapper = reinterpret_cast<Wrapper*>(ptr);
- wrapper->Reclaim();
- }
- IOBufferPool::Internal::Internal(size_t buffer_size,
- size_t max_buffers,
- bool threadsafe)
- : buffer_size_(buffer_size),
- max_buffers_(max_buffers),
- lock_ptr_(threadsafe ? &lock_ : nullptr),
- free_buffers_(nullptr),
- num_allocated_(0),
- num_free_(0),
- refs_(1) { // 1 ref for the owner.
- }
- IOBufferPool::Internal::~Internal() {
- while (free_buffers_) {
- char* data = reinterpret_cast<char*>(free_buffers_);
- free_buffers_ = free_buffers_->next;
- base::AlignedFree(data);
- }
- }
- // static
- void* IOBufferPool::Internal::AllocateAlignedSpace(size_t buffer_size) {
- size_t kAlignedStorageSize = base::bits::AlignUp(sizeof(Storage), kAlignment);
- return base::AlignedAlloc(kAlignedStorageSize + buffer_size, kAlignment);
- }
- void IOBufferPool::Internal::Preallocate(size_t num_buffers) {
- // We assume that this is uncontended in normal usage, so just lock for the
- // entire method.
- base::AutoLockMaybe lock(lock_ptr_);
- if (num_buffers > max_buffers_) {
- num_buffers = max_buffers_;
- }
- if (num_allocated_ >= num_buffers) {
- return;
- }
- size_t num_extra_buffers = num_buffers - num_allocated_;
- num_free_ += num_extra_buffers;
- num_allocated_ += num_extra_buffers;
- while (num_extra_buffers > 0) {
- void* ptr = AllocateAlignedSpace(buffer_size_);
- Storage* storage = reinterpret_cast<Storage*>(ptr);
- storage->next = free_buffers_;
- free_buffers_ = storage;
- --num_extra_buffers;
- }
- // No need to add refs here, since the newly allocated buffers are not in use.
- }
- void IOBufferPool::Internal::OwnerDestroyed() {
- bool deletable;
- {
- base::AutoLockMaybe lock(lock_ptr_);
- --refs_; // Remove the owner's ref.
- deletable = (refs_ == 0);
- }
- if (deletable) {
- delete this;
- }
- }
- scoped_refptr<net::IOBuffer> IOBufferPool::Internal::GetBuffer() {
- char* ptr = nullptr;
- {
- base::AutoLockMaybe lock(lock_ptr_);
- if (free_buffers_) {
- ptr = reinterpret_cast<char*>(free_buffers_);
- free_buffers_ = free_buffers_->next;
- --num_free_;
- } else {
- if (num_allocated_ == max_buffers_)
- return nullptr;
- ++num_allocated_;
- }
- ++refs_; // Add a ref for the now in-use buffer.
- }
- if (!ptr) {
- ptr = static_cast<char*>(AllocateAlignedSpace(buffer_size_));
- }
- size_t kAlignedStorageSize = base::bits::AlignUp(sizeof(Storage), kAlignment);
- char* data = ptr + kAlignedStorageSize;
- Wrapper* wrapper = new (ptr) Wrapper(data, this);
- return scoped_refptr<net::IOBuffer>(wrapper->buffer());
- }
- void IOBufferPool::Internal::Reclaim(Wrapper* wrapper) {
- Storage* storage = reinterpret_cast<Storage*>(wrapper);
- bool deletable;
- {
- base::AutoLockMaybe lock(lock_ptr_);
- storage->next = free_buffers_;
- free_buffers_ = storage;
- ++num_free_;
- --refs_; // Remove a ref since this buffer is no longer in use.
- deletable = (refs_ == 0);
- }
- if (deletable) {
- delete this;
- }
- }
- IOBufferPool::IOBufferPool(size_t buffer_size,
- size_t max_buffers,
- bool threadsafe)
- : buffer_size_(buffer_size),
- max_buffers_(max_buffers),
- threadsafe_(threadsafe),
- internal_(new Internal(buffer_size, max_buffers, threadsafe)) {}
- IOBufferPool::IOBufferPool(size_t buffer_size)
- : IOBufferPool(buffer_size, static_cast<size_t>(-1)) {}
- IOBufferPool::~IOBufferPool() {
- internal_->OwnerDestroyed();
- }
- size_t IOBufferPool::NumAllocatedForTesting() const {
- return internal_->num_allocated();
- }
- size_t IOBufferPool::NumFreeForTesting() const {
- return internal_->num_free();
- }
- void IOBufferPool::Preallocate(size_t num_buffers) {
- internal_->Preallocate(num_buffers);
- }
- scoped_refptr<net::IOBuffer> IOBufferPool::GetBuffer() {
- return internal_->GetBuffer();
- }
- } // namespace chromecast
|