Your IP : 18.222.111.44
/*
* twemproxy - A fast and lightweight proxy for memcached protocol.
* Copyright (C) 2011 Twitter, Inc.
* Copyright (C) 2014-2017 Phusion Holding B.V.
*
* 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.
*/
#ifndef _PSG_MBUF_BLOCK_H_
#define _PSG_MBUF_BLOCK_H_
#include <psg_sysqueue.h>
#include <algorithm>
#include <cstddef>
#include <cassert>
#include <cstring>
#include <oxt/macros.hpp>
#include <boost/cstdint.hpp>
#include <boost/move/core.hpp>
/** A memory buffer allocator system taken from twemproxy and modified to
* suit our needs.
*
* In twemproxy, mbufs enables zero-copy because the same buffer on which a
* request was received from the client is used for forwarding it to the
* server. Similarly the same mbuf_block on which a response was received from
* the server is used for forwarding it to the client.
*
* Furthermore, memory for mbufs is managed using a reuse pool. This means
* that once mbuf_block is allocated, it is not deallocated, but just put back
* into the reuse pool. By default each mbuf_block chunk is set to 16K bytes in
* size. There is a trade-off between the mbuf_block size and number of concurrent
* connections twemproxy can support. A large mbuf_block size reduces the number
* of read syscalls made by twemproxy when reading requests or responses.
* However, with large mbuf_block size, every active connection would use up 16K
* bytes of buffer which might be an issue when twemproxy is handling large
* number of concurrent connections from clients. When twemproxy is meant
* to handle a large number of concurrent client connections, you should
* set chunk size to a small value like 512 bytes.
*
* In Phusion Passenger, we modified the mbuf system so that we can take subsets
* of an mbuf. The actual mbuf (now called mbuf_block in this modification) is
* not actually put back on the freelist until all subsets are destroyed too.
* This approach is similar to how Node.js manages buffer slices.
* We also got rid of the global variables, and put them in an mbuf_pool
* struct, which acts like a context structure.
*/
//#define MBUF_ENABLE_DEBUGGING
//#define MBUF_ENABLE_BACKTRACES
namespace Passenger {
namespace MemoryKit {
using namespace std;
using namespace boost;
struct mbuf_block;
struct mhdr;
typedef void (*mbuf_block_copy_t)(struct mbuf_block *, void *);
/* See _mbuf_block_init() for format description */
struct mbuf_block {
boost::uint32_t magic; /* mbuf_block magic (const) */
STAILQ_ENTRY(struct mbuf_block) next; /* next free mbuf_block */
#ifdef MBUF_ENABLE_DEBUGGING
TAILQ_ENTRY(struct mbuf_block) active_q; /* prev and next active mbuf_block */
#endif
#ifdef MBUF_ENABLE_BACKTRACES
char *backtrace;
#endif
char *start; /* start of buffer (const) */
char *end; /* end of buffer (const) */
struct mbuf_pool *pool; /* containing pool (const) */
boost::uint32_t refcount; /* number of references by mbuf subsets */
boost::uint32_t offset; /* standalone mbuf_block data size */
};
STAILQ_HEAD(mhdr, struct mbuf_block);
#ifdef MBUF_ENABLE_DEBUGGING
TAILQ_HEAD(active_mbuf_block_list, struct mbuf_block);
#endif
struct mbuf_pool {
boost::uint32_t nfree_mbuf_blockq; /* # free mbuf_block */
boost::uint32_t nactive_mbuf_blockq; /* # active (non-free) mbuf_block */
struct mhdr free_mbuf_blockq; /* free mbuf_block q */
#ifdef MBUF_ENABLE_DEBUGGING
struct active_mbuf_block_list active_mbuf_blockq; /* active mbuf_block q */
#endif
size_t mbuf_block_chunk_size; /* mbuf_block chunk size - header + data (const) */
size_t mbuf_block_offset; /* mbuf_block offset in chunk (const) */
};
#define MBUF_BLOCK_MAGIC 0xdeadbeef
#define MBUF_BLOCK_MIN_SIZE 512
#define MBUF_BLOCK_MAX_SIZE 16777216
#define MBUF_BLOCK_HSIZE sizeof(struct mbuf_block)
#define MBUF_BLOCK_EMPTY(mbuf_block) ((mbuf_block)->pos == (mbuf_block)->last)
#define MBUF_BLOCK_FULL(mbuf_block) ((mbuf_block)->last == (mbuf_block)->end)
void mbuf_pool_init(struct mbuf_pool *pool);
void mbuf_pool_deinit(struct mbuf_pool *pool);
size_t mbuf_pool_data_size(struct mbuf_pool *pool);
unsigned int mbuf_pool_compact(struct mbuf_pool *pool);
struct mbuf_block *mbuf_block_get(struct mbuf_pool *pool);
struct mbuf_block *mbuf_block_new_standalone(struct mbuf_pool *pool, size_t size);
void mbuf_block_put(struct mbuf_block *mbuf_block);
void mbuf_block_ref(struct mbuf_block *mbuf_block);
void mbuf_block_unref(struct mbuf_block *mbuf_block);
void _mbuf_block_assert_refcount_at_least_two(struct mbuf_block *mbuf_block);
/* A subset of an mbuf_block. */
class mbuf {
private:
BOOST_COPYABLE_AND_MOVABLE(mbuf)
void initialize_with_block(unsigned int start, unsigned int len);
void initialize_with_block_just_created(unsigned int start, unsigned int len);
void initialize_with_mbuf(const mbuf &other, unsigned int start, unsigned int len);
public:
struct just_created_t { };
struct mbuf_block *mbuf_block; /* container block */
char *start; /* start of subset (const) */
char *end; /* end of subset (const) */
mbuf()
: mbuf_block(NULL),
start(NULL),
end(NULL)
{ }
explicit mbuf(struct mbuf_block *block, unsigned int start = 0)
: mbuf_block(block)
{
initialize_with_block(start, block->end - block->start);
}
explicit mbuf(struct mbuf_block *block, unsigned int start, unsigned int len)
: mbuf_block(block)
{
initialize_with_block(start, len);
}
explicit mbuf(struct mbuf_block *block, unsigned int start, unsigned int len,
const just_created_t &t)
: mbuf_block(block)
{
initialize_with_block_just_created(start, len);
}
// Create an mbuf as a dumb wrapper around a memory buffer.
explicit mbuf(const char *data, unsigned int len)
: mbuf_block(NULL),
start(const_cast<char *>(data)),
end(const_cast<char *>(data) + len)
{ }
explicit mbuf(const char *data)
: mbuf_block(NULL),
start(const_cast<char *>(data)),
end(const_cast<char *>(data) + strlen(data))
{ }
// Copy constructor.
mbuf(const mbuf &mbuf, unsigned int start = 0) {
initialize_with_mbuf(mbuf, start, mbuf.end - mbuf.start);
}
// Take a subset of another mbuf.
mbuf(const mbuf &mbuf, unsigned int start, unsigned int len) {
initialize_with_mbuf(mbuf, start, len);
}
// Move constructor.
explicit
mbuf(BOOST_RV_REF(mbuf) mbuf)
: mbuf_block(mbuf.mbuf_block),
start(mbuf.start),
end(mbuf.end)
{
mbuf.mbuf_block = NULL;
mbuf.start = NULL;
mbuf.end = NULL;
}
~mbuf() {
if (mbuf_block != NULL) {
mbuf_block_unref(mbuf_block);
}
}
// Copy assignment.
mbuf &operator=(BOOST_COPY_ASSIGN_REF(mbuf) other) {
if (&other != this) {
#ifndef NDEBUG
if (mbuf_block != NULL && mbuf_block == other.mbuf_block) {
_mbuf_block_assert_refcount_at_least_two(mbuf_block);
}
#endif
if (mbuf_block != NULL) {
mbuf_block_unref(mbuf_block);
}
mbuf_block = other.mbuf_block;
start = other.start;
end = other.end;
// We reference 'other.mbuf_block' instead of 'this->mbuf_block' as
// a micro-optimization. This should decrease the number of data
// dependencies and allow the CPU to reorder the instructions better.
if (other.mbuf_block != NULL) {
mbuf_block_ref(other.mbuf_block);
}
}
return *this;
}
// Move assignment.
mbuf &operator=(BOOST_RV_REF(mbuf) other) {
if (&other != this) {
#ifndef NDEBUG
if (mbuf_block != NULL && mbuf_block == other.mbuf_block) {
_mbuf_block_assert_refcount_at_least_two(mbuf_block);
}
#endif
if (mbuf_block != NULL) {
mbuf_block_unref(mbuf_block);
}
mbuf_block = other.mbuf_block;
start = other.start;
end = other.end;
other.mbuf_block = NULL;
other.start = NULL;
other.end = NULL;
}
return *this;
}
OXT_FORCE_INLINE
size_t size() const {
return end - start;
}
OXT_FORCE_INLINE
bool empty() const {
return start == end;
}
OXT_FORCE_INLINE
bool is_null() const {
return start == NULL;
}
};
mbuf mbuf_block_subset(struct mbuf_block *mbuf_block, unsigned int start, unsigned int len);
mbuf mbuf_get(struct mbuf_pool *pool);
mbuf mbuf_get_with_size(struct mbuf_pool *pool, size_t size);
} // namespace MemoryKit
} // namespace Passenger
#endif /* _PSG_MBUF_BLOCK_H_ */