| // Copyright (c) 2018 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. |
| |
| #ifndef NET_BASE_DATAGRAM_BUFFER_H_ |
| #define NET_BASE_DATAGRAM_BUFFER_H_ |
| |
| #include <list> |
| |
| #include "base/memory/weak_ptr.h" |
| #include "net/base/net_export.h" |
| |
| namespace net { |
| |
| // An IO buffer, (at least initially) specifically for use with the |
| // new DatagramClientSocket::WriteAsync method, with the following key |
| // features: |
| // |
| // 1) Meant to be easily batched when that improves efficiency. The |
| // primary goal of WriteAsync is to enable enlisting an |
| // additional cpu core for the kernel part of socket write. |
| // 2) Uses unique_ptr (with std::move) rather than reference |
| // counting as in IOBuffers. The benefit is safer cancellation |
| // semantics, IOBuffer used reference count to enforce unique |
| // ownership in an idiomatic fashion. unique_ptr is ligher weight |
| // as it doesn't use thread safe primitives as |
| // RefCountedThreadSafe does. |
| // 3) Provides a pooling allocator, which for datagram buffers is |
| // much cheaper than using fully general allocator (e.g. malloc |
| // etc.). The implementation takes advantage of |
| // std::list::splice so that costs associated with allocations |
| // and copies of pool metadata quickly amortize to zero, and all |
| // common operations are O(1). |
| |
| class DatagramBuffer; |
| |
| // Batches of DatagramBuffers are treated as a FIFO queue, implemented |
| // by |std::list|. Note that |std::list::splice()| is attractive for |
| // this use case because it keeps most operations to O(1) and |
| // minimizes allocations/frees and copies. |
| typedef std::list<std::unique_ptr<DatagramBuffer>> DatagramBuffers; |
| |
| class NET_EXPORT_PRIVATE DatagramBufferPool { |
| public: |
| // |max_buffer_size| must be >= largest |buf_len| provided to |
| // ||New()|. |
| DatagramBufferPool(size_t max_buffer_size); |
| DatagramBufferPool(const DatagramBufferPool&) = delete; |
| DatagramBufferPool& operator=(const DatagramBufferPool&) = delete; |
| virtual ~DatagramBufferPool(); |
| // Insert a new element (drawn from the pool) containing a copy of |
| // |buffer| to |buffers|. Caller retains owenership of |buffers| and |buffer|. |
| void Enqueue(const char* buffer, size_t buf_len, DatagramBuffers* buffers); |
| // Return all elements of |buffers| to the pool. Caller retains |
| // ownership of |buffers|. |
| void Dequeue(DatagramBuffers* buffers); |
| |
| size_t max_buffer_size() { return max_buffer_size_; } |
| |
| private: |
| const size_t max_buffer_size_; |
| DatagramBuffers free_list_; |
| }; |
| |
| // |DatagramBuffer|s can only be created via |
| // |DatagramBufferPool::Enqueue()|. |
| // |
| |
| // |DatagramBuffer|s should be recycled via |
| // |DatagramBufferPool::Dequeue|. Care must be taken when a |
| // |DatagramBuffer| is moved to another thread via |
| // |PostTask|. |Dequeue| is not expected to be thread-safe, so it |
| // is preferred to move the |DatagramBuffer|s back to the thread where |
| // the pool lives (e.g. using |PostTaskAndReturnWithResult|) and |
| // dequeuing them from there. In the exception of pathalogical |
| // cancellation (e.g. due to thread tear-down), the destructor will |
| // release its memory permanently rather than returning to the pool. |
| class NET_EXPORT_PRIVATE DatagramBuffer { |
| public: |
| DatagramBuffer() = delete; |
| DatagramBuffer(const DatagramBuffer&) = delete; |
| DatagramBuffer& operator=(const DatagramBuffer&) = delete; |
| ~DatagramBuffer(); |
| |
| char* data() const; |
| size_t length() const; |
| |
| protected: |
| DatagramBuffer(size_t max_packet_size); |
| |
| private: |
| friend class DatagramBufferPool; |
| |
| void Set(const char* buffer, size_t buf_len); |
| |
| std::unique_ptr<char[]> data_; |
| size_t length_; |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_BASE_DATAGRAM_BUFFER_H_ |