diff --git a/src/butil/compiler_specific.h b/src/butil/compiler_specific.h index d05da9dc6f..9eb4283e4d 100644 --- a/src/butil/compiler_specific.h +++ b/src/butil/compiler_specific.h @@ -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) diff --git a/src/butil/containers/flat_map.h b/src/butil/containers/flat_map.h index 40c44c5988..955753d2ee 100644 --- a/src/butil/containers/flat_map.h +++ b/src/butil/containers/flat_map.h @@ -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 { @@ -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(spaces); + return *element_; } const Element& element() const { - const void* spaces = &element_spaces; - return *reinterpret_cast(spaces); + return *element_; } Bucket *next; - typename std::aligned_storage::type - element_spaces; + + private: + ManualConstructor element_; }; allocator_type& get_allocator() { return _pool.get_allocator(); } diff --git a/src/butil/containers/mpsc_queue.h b/src/butil/containers/mpsc_queue.h index f9e1d78099..bfa4636be0 100644 --- a/src/butil/containers/mpsc_queue.h +++ b/src/butil/containers/mpsc_queue.h @@ -24,6 +24,7 @@ #include "butil/object_pool.h" #include "butil/type_traits.h" +#include "butil/memory/manual_constructor.h" namespace butil { @@ -32,8 +33,7 @@ struct BAIDU_CACHELINE_ALIGNMENT MPSCQueueNode { static MPSCQueueNode* const UNCONNECTED; MPSCQueueNode* next{NULL}; - char data_mem[sizeof(T)]{}; - + ManualConstructor data_mem; }; template @@ -95,7 +95,7 @@ template void MPSCQueue::Enqueue(typename add_const_reference::type data) { auto node = (MPSCQueueNode*)_alloc.Alloc(); node->next = MPSCQueueNode::UNCONNECTED; - new ((void*)&node->data_mem) T(data); + node->data_mem.Init(data); EnqueueImpl(node); } @@ -103,7 +103,7 @@ template void MPSCQueue::Enqueue(T&& data) { auto node = (MPSCQueueNode*)_alloc.Alloc(); node->next = MPSCQueueNode::UNCONNECTED; - new ((void*)&node->data_mem) T(std::forward(data)); + node->data_mem.Init(data); EnqueueImpl(node); } @@ -137,7 +137,7 @@ bool MPSCQueue::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* old_node = node; diff --git a/src/butil/memory/aligned_memory.h b/src/butil/memory/aligned_memory.h index f589ff9ced..c6aaaeef9d 100644 --- a/src/butil/memory/aligned_memory.h +++ b/src/butil/memory/aligned_memory.h @@ -51,24 +51,36 @@ namespace butil { template struct AlignedMemory {}; -#define BUTIL_DECL_ALIGNED_MEMORY(byte_alignment) \ - template \ - class AlignedMemory { \ - public: \ - ALIGNAS(byte_alignment) uint8_t data_[Size]; \ - void* void_data() { return static_cast(data_); } \ - const void* void_data() const { \ - return static_cast(data_); \ - } \ - template \ +// 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 \ + class AlignedMemory { \ + public: \ + DECL_ALIGNED_BUFFER(data_, byte_alignment, Size); \ + void* void_data() { return static_cast(data_); } \ + const void* void_data() const { \ + return static_cast(data_); \ + } \ + template \ Type* data_as() { return static_cast(void_data()); } \ - template \ - const Type* data_as() const { \ - return static_cast(void_data()); \ - } \ - private: \ - void* operator new(size_t); \ - void operator delete(void*); \ + template \ + const Type* data_as() const { \ + return static_cast(void_data()); \ + } \ + private: \ + void* operator new(size_t); \ + void operator delete(void*); \ } // Specialization for all alignments is required because MSVC (as of VS 2008) diff --git a/src/butil/memory/manual_constructor.h b/src/butil/memory/manual_constructor.h index 5d06b94923..266f6cca72 100644 --- a/src/butil/memory/manual_constructor.h +++ b/src/butil/memory/manual_constructor.h @@ -56,6 +56,11 @@ class ManualConstructor { inline Type& operator*() { return *get(); } inline const Type& operator*() const { return *get(); } + template + 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; diff --git a/src/butil/object_pool_inl.h b/src/butil/object_pool_inl.h index 6f10e03abb..71b9b89099 100644 --- a/src/butil/object_pool_inl.h +++ b/src/butil/object_pool_inl.h @@ -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 #ifdef BUTIL_OBJECT_POOL_NEED_FREE_ITEM_NUM @@ -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 items[BLOCK_NITEM]; size_t nitem; Block() : nitem(0) {} @@ -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; \ @@ -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::validate(obj)) { \ obj->~T(); \ return NULL; \ @@ -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::validate(obj)) { \ obj->~T(); \ return NULL; \ @@ -188,12 +193,12 @@ class BAIDU_CACHELINE_ALIGNMENT ObjectPool { template inline T* get(const A1& a1) { - BAIDU_OBJECT_POOL_GET((a1)); + BAIDU_OBJECT_POOL_GET(a1); } template 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 diff --git a/src/butil/resource_pool_inl.h b/src/butil/resource_pool_inl.h index 4ebe4ad19d..2ca858ad76 100644 --- a/src/butil/resource_pool_inl.h +++ b/src/butil/resource_pool_inl.h @@ -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 #ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM @@ -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 items[BLOCK_NITEM]; size_t nitem; Block() : nitem(0) {} @@ -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 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 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 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::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::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::validate(p)) { \ - p->~T(); \ - return NULL; \ - } \ - ++_cur_block->nitem; \ - return p; \ - } \ - return NULL; \ + (_cur_block->items + _cur_block->nitem)->InitBy(ctor); \ + if (!ResourcePoolValidator::validate(p)) { \ + p->~T(); \ + return NULL; \ + } \ + ++_cur_block->nitem; \ + return p; \ + } \ + return NULL; \ inline T* get(ResourceId* id) { @@ -212,12 +217,12 @@ class BAIDU_CACHELINE_ALIGNMENT ResourcePool { template inline T* get(ResourceId* id, const A1& a1) { - BAIDU_RESOURCE_POOL_GET((a1)); + BAIDU_RESOURCE_POOL_GET(a1); } template inline T* get(ResourceId* id, const A1& a1, const A2& a2) { - BAIDU_RESOURCE_POOL_GET((a1, a2)); + BAIDU_RESOURCE_POOL_GET(a1, a2); } #undef BAIDU_RESOURCE_POOL_GET @@ -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; }