Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/butil/compiler_specific.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,23 @@
// Use like:
// class ALIGNAS(16) MyClass { ... }
// ALIGNAS(16) int array[4];
//
// In most places you can use the C++11 keyword "alignas", which is preferred.
//
// But compilers have trouble mixing __attribute__((...)) syntax with
// alignas(...) syntax.
//
// Doesn't work in clang or gcc:
// struct alignas(16) __attribute__((packed)) S { char c; };
// Works in clang but not gcc:
// struct __attribute__((packed)) alignas(16) S2 { char c; };
// Works in clang and gcc:
// struct alignas(16) S3 { char c; } __attribute__((packed));
//
// There are also some attributes that must be specified *before* a class
// definition: visibility (used for exporting functions/classes) is one of
// these attributes. This means that it is not possible to use alignas() with a
// class that is marked as exported.
#if defined(COMPILER_MSVC)
# define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
#elif defined(COMPILER_GCC)
Expand Down
23 changes: 13 additions & 10 deletions src/butil/containers/flat_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
#include "butil/bit_array.h" // bit_array_*
#include "butil/strings/string_piece.h" // StringPiece
#include "butil/memory/scope_guard.h"
#include "butil/memory/manual_constructor.h"

namespace butil {

Expand Down Expand Up @@ -265,24 +266,26 @@ class FlatMap {
BucketInfo bucket_info() const;

struct Bucket {
explicit Bucket(const _K& k) : next(NULL)
{ new (&element_spaces) Element(k); }
Bucket(const Bucket& other) : next(NULL)
{ new (&element_spaces) Element(other.element()); }
explicit Bucket(const _K& k) : next(NULL) {
element_.Init(k);
}
Bucket(const Bucket& other) : next(NULL) {
element_.Init(other.element());
}

bool is_valid() const { return next != (const Bucket*)-1UL; }
void set_invalid() { next = (Bucket*)-1UL; }
// NOTE: Only be called when is_valid() is true.
Element& element() {
void* spaces = &element_spaces; // Suppress strict-aliasing
return *reinterpret_cast<Element*>(spaces);
return *element_;
}
const Element& element() const {
const void* spaces = &element_spaces;
return *reinterpret_cast<const Element*>(spaces);
return *element_;
}
Bucket *next;
typename std::aligned_storage<sizeof(Element), alignof(Element)>::type
element_spaces;

private:
ManualConstructor<Element> element_;
};

allocator_type& get_allocator() { return _pool.get_allocator(); }
Expand Down
10 changes: 5 additions & 5 deletions src/butil/containers/mpsc_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "butil/object_pool.h"
#include "butil/type_traits.h"
#include "butil/memory/manual_constructor.h"

namespace butil {

Expand All @@ -32,8 +33,7 @@ struct BAIDU_CACHELINE_ALIGNMENT MPSCQueueNode {
static MPSCQueueNode* const UNCONNECTED;

MPSCQueueNode* next{NULL};
char data_mem[sizeof(T)]{};

ManualConstructor<T> data_mem;
};

template <typename T>
Expand Down Expand Up @@ -95,15 +95,15 @@ template <typename T, typename Alloc>
void MPSCQueue<T, Alloc>::Enqueue(typename add_const_reference<T>::type data) {
auto node = (MPSCQueueNode<T>*)_alloc.Alloc();
node->next = MPSCQueueNode<T>::UNCONNECTED;
new ((void*)&node->data_mem) T(data);
node->data_mem.Init(data);
EnqueueImpl(node);
}

template <typename T, typename Alloc>
void MPSCQueue<T, Alloc>::Enqueue(T&& data) {
auto node = (MPSCQueueNode<T>*)_alloc.Alloc();
node->next = MPSCQueueNode<T>::UNCONNECTED;
new ((void*)&node->data_mem) T(std::forward<T>(data));
node->data_mem.Init(data);
EnqueueImpl(node);
}

Expand Down Expand Up @@ -137,7 +137,7 @@ bool MPSCQueue<T, Alloc>::DequeueImpl(T* data) {

_cur_enqueue_node.store(NULL, memory_order_relaxed);
if (data) {
auto mem = (T* const)node->data_mem;
auto mem = (T* const)node->data_mem.get();
*data = std::move(*mem);
}
MPSCQueueNode<T>* old_node = node;
Expand Down
46 changes: 29 additions & 17 deletions src/butil/memory/aligned_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,36 @@ namespace butil {
template <size_t Size, size_t ByteAlignment>
struct AlignedMemory {};

#define BUTIL_DECL_ALIGNED_MEMORY(byte_alignment) \
template <size_t Size> \
class AlignedMemory<Size, byte_alignment> { \
public: \
ALIGNAS(byte_alignment) uint8_t data_[Size]; \
void* void_data() { return static_cast<void*>(data_); } \
const void* void_data() const { \
return static_cast<const void*>(data_); \
} \
template<typename Type> \
// std::aligned_storage has been deprecated in C++23,
// because aligned_* are harmful to codebases and should not be used.
// For details, see https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1413r3.pdf
#if (__cplusplus >= 201103L)
// In most places, use the C++11 keyword "alignas", which is preferred.
#define DECL_ALIGNED_BUFFER(buffer_name, byte_alignment, size) \
alignas(byte_alignment) uint8_t buffer_name[size]
#else
#define DECL_ALIGNED_BUFFER(buffer_name, byte_alignment, size) \
ALIGNAS(byte_alignment) uint8_t buffer_name[size]
#endif

#define BUTIL_DECL_ALIGNED_MEMORY(byte_alignment) \
template <size_t Size> \
class AlignedMemory<Size, byte_alignment> { \
public: \
DECL_ALIGNED_BUFFER(data_, byte_alignment, Size); \
void* void_data() { return static_cast<void*>(data_); } \
const void* void_data() const { \
return static_cast<const void*>(data_); \
} \
template<typename Type> \
Type* data_as() { return static_cast<Type*>(void_data()); } \
template<typename Type> \
const Type* data_as() const { \
return static_cast<const Type*>(void_data()); \
} \
private: \
void* operator new(size_t); \
void operator delete(void*); \
template<typename Type> \
const Type* data_as() const { \
return static_cast<const Type*>(void_data()); \
} \
private: \
void* operator new(size_t); \
void operator delete(void*); \
}

// Specialization for all alignments is required because MSVC (as of VS 2008)
Expand Down
5 changes: 5 additions & 0 deletions src/butil/memory/manual_constructor.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ class ManualConstructor {
inline Type& operator*() { return *get(); }
inline const Type& operator*() const { return *get(); }

template<typename Ctor>
inline void InitBy(Ctor ctor) {
ctor(space_.void_data());
}

// You can pass up to eight constructor arguments as arguments of Init().
inline void Init() {
new(space_.void_data()) Type;
Expand Down
17 changes: 11 additions & 6 deletions src/butil/object_pool_inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "butil/macros.h" // BAIDU_CACHELINE_ALIGNMENT
#include "butil/scoped_lock.h" // BAIDU_SCOPED_LOCK
#include "butil/thread_local.h" // BAIDU_THREAD_LOCAL
#include "butil/memory/manual_constructor.h"
#include <vector>

#ifdef BUTIL_OBJECT_POOL_NEED_FREE_ITEM_NUM
Expand Down Expand Up @@ -97,7 +98,7 @@ class BAIDU_CACHELINE_ALIGNMENT ObjectPool {
// items in the Block are only used by the thread.
// To support cache-aligned objects, align Block.items by cacheline.
struct BAIDU_CACHELINE_ALIGNMENT Block {
char items[sizeof(T) * BLOCK_NITEM];
ManualConstructor<T> items[BLOCK_NITEM];
size_t nitem;

Block() : nitem(0) {}
Expand Down Expand Up @@ -145,7 +146,7 @@ class BAIDU_CACHELINE_ALIGNMENT ObjectPool {
// which may include parenthesis because when T is POD, "new T()"
// and "new T" are different: former one sets all fields to 0 which
// we don't want.
#define BAIDU_OBJECT_POOL_GET(CTOR_ARGS) \
#define BAIDU_OBJECT_POOL_GET(...) \
/* Fetch local free ptr */ \
if (_cur_free.nfree) { \
BAIDU_OBJECT_POOL_FREE_ITEM_NUM_SUB1; \
Expand All @@ -158,9 +159,13 @@ class BAIDU_CACHELINE_ALIGNMENT ObjectPool {
BAIDU_OBJECT_POOL_FREE_ITEM_NUM_SUB1; \
return _cur_free.ptrs[--_cur_free.nfree]; \
} \
T* obj = NULL; \
auto ctor = [&](void* mem) { \
obj = new (mem) T(__VA_ARGS__); \
}; \
/* Fetch memory from local block */ \
if (_cur_block && _cur_block->nitem < BLOCK_NITEM) { \
T* obj = new ((T*)_cur_block->items + _cur_block->nitem) T CTOR_ARGS; \
(_cur_block->items + _cur_block->nitem)->InitBy(ctor); \
if (!ObjectPoolValidator<T>::validate(obj)) { \
obj->~T(); \
return NULL; \
Expand All @@ -171,7 +176,7 @@ class BAIDU_CACHELINE_ALIGNMENT ObjectPool {
/* Fetch a Block from global */ \
_cur_block = add_block(&_cur_block_index); \
if (_cur_block != NULL) { \
T* obj = new ((T*)_cur_block->items + _cur_block->nitem) T CTOR_ARGS; \
(_cur_block->items + _cur_block->nitem)->InitBy(ctor); \
if (!ObjectPoolValidator<T>::validate(obj)) { \
obj->~T(); \
return NULL; \
Expand All @@ -188,12 +193,12 @@ class BAIDU_CACHELINE_ALIGNMENT ObjectPool {

template <typename A1>
inline T* get(const A1& a1) {
BAIDU_OBJECT_POOL_GET((a1));
BAIDU_OBJECT_POOL_GET(a1);
}

template <typename A1, typename A2>
inline T* get(const A1& a1, const A2& a2) {
BAIDU_OBJECT_POOL_GET((a1, a2));
BAIDU_OBJECT_POOL_GET(a1, a2);
}

#undef BAIDU_OBJECT_POOL_GET
Expand Down
91 changes: 48 additions & 43 deletions src/butil/resource_pool_inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "butil/macros.h" // BAIDU_CACHELINE_ALIGNMENT
#include "butil/scoped_lock.h" // BAIDU_SCOPED_LOCK
#include "butil/thread_local.h" // thread_atexit
#include "butil/memory/manual_constructor.h"
#include <vector>

#ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM
Expand Down Expand Up @@ -113,7 +114,7 @@ class BAIDU_CACHELINE_ALIGNMENT ResourcePool {
// items in the Block are only used by the thread.
// To support cache-aligned objects, align Block.items by cacheline.
struct BAIDU_CACHELINE_ALIGNMENT Block {
char items[sizeof(T) * BLOCK_NITEM];
ManualConstructor<T> items[BLOCK_NITEM];
size_t nitem;

Block() : nitem(0) {}
Expand Down Expand Up @@ -162,48 +163,52 @@ class BAIDU_CACHELINE_ALIGNMENT ResourcePool {
// which may include parenthesis because when T is POD, "new T()"
// and "new T" are different: former one sets all fields to 0 which
// we don't want.
#define BAIDU_RESOURCE_POOL_GET(CTOR_ARGS) \
/* Fetch local free id */ \
if (_cur_free.nfree) { \
#define BAIDU_RESOURCE_POOL_GET(...) \
/* Fetch local free id */ \
if (_cur_free.nfree) { \
const ResourceId<T> free_id = _cur_free.ids[--_cur_free.nfree]; \
*id = free_id; \
BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_SUB1; \
return unsafe_address_resource(free_id); \
} \
/* Fetch a FreeChunk from global. \
TODO: Popping from _free needs to copy a FreeChunk which is \
costly, but hardly impacts amortized performance. */ \
if (_pool->pop_free_chunk(_cur_free)) { \
--_cur_free.nfree; \
const ResourceId<T> free_id = _cur_free.ids[_cur_free.nfree]; \
*id = free_id; \
BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_SUB1; \
return unsafe_address_resource(free_id); \
} \
/* Fetch memory from local block */ \
if (_cur_block && _cur_block->nitem < BLOCK_NITEM) { \
*id = free_id; \
BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_SUB1; \
return unsafe_address_resource(free_id); \
} \
/* Fetch a FreeChunk from global. \
TODO: Popping from _free needs to copy a FreeChunk which is \
costly, but hardly impacts amortized performance. */ \
if (_pool->pop_free_chunk(_cur_free)) { \
--_cur_free.nfree; \
const ResourceId<T> free_id = _cur_free.ids[_cur_free.nfree]; \
*id = free_id; \
BAIDU_RESOURCE_POOL_FREE_ITEM_NUM_SUB1; \
return unsafe_address_resource(free_id); \
} \
T* p = NULL; \
auto ctor = [&](void* mem) { \
p = new (mem) T(__VA_ARGS__); \
}; \
/* Fetch memory from local block */ \
if (_cur_block && _cur_block->nitem < BLOCK_NITEM) { \
id->value = _cur_block_index * BLOCK_NITEM + _cur_block->nitem; \
T* p = new ((T*)_cur_block->items + _cur_block->nitem) T CTOR_ARGS; \
if (!ResourcePoolValidator<T>::validate(p)) { \
p->~T(); \
return NULL; \
} \
++_cur_block->nitem; \
return p; \
} \
/* Fetch a Block from global */ \
_cur_block = add_block(&_cur_block_index); \
if (_cur_block != NULL) { \
(_cur_block->items + _cur_block->nitem)->InitBy(ctor); \
if (!ResourcePoolValidator<T>::validate(p)) { \
p->~T(); \
return NULL; \
} \
++_cur_block->nitem; \
return p; \
} \
/* Fetch a Block from global */ \
_cur_block = add_block(&_cur_block_index); \
if (_cur_block != NULL) { \
id->value = _cur_block_index * BLOCK_NITEM + _cur_block->nitem; \
T* p = new ((T*)_cur_block->items + _cur_block->nitem) T CTOR_ARGS; \
if (!ResourcePoolValidator<T>::validate(p)) { \
p->~T(); \
return NULL; \
} \
++_cur_block->nitem; \
return p; \
} \
return NULL; \
(_cur_block->items + _cur_block->nitem)->InitBy(ctor); \
if (!ResourcePoolValidator<T>::validate(p)) { \
p->~T(); \
return NULL; \
} \
++_cur_block->nitem; \
return p; \
} \
return NULL; \


inline T* get(ResourceId<T>* id) {
Expand All @@ -212,12 +217,12 @@ class BAIDU_CACHELINE_ALIGNMENT ResourcePool {

template <typename A1>
inline T* get(ResourceId<T>* id, const A1& a1) {
BAIDU_RESOURCE_POOL_GET((a1));
BAIDU_RESOURCE_POOL_GET(a1);
}

template <typename A1, typename A2>
inline T* get(ResourceId<T>* id, const A1& a1, const A2& a2) {
BAIDU_RESOURCE_POOL_GET((a1, a2));
BAIDU_RESOURCE_POOL_GET(a1, a2);
}

#undef BAIDU_RESOURCE_POOL_GET
Expand Down Expand Up @@ -384,7 +389,7 @@ class BAIDU_CACHELINE_ALIGNMENT ResourcePool {

// Create a Block and append it to right-most BlockGroup.
static Block* add_block(size_t* index) {
Block* const new_block = new(std::nothrow) Block;
Block* const new_block = new (std::nothrow) Block;
if (NULL == new_block) {
return NULL;
}
Expand Down