Index: include/memory =================================================================== --- include/memory +++ include/memory @@ -3222,6 +3222,22 @@ {__alloc_traits::deallocate(__alloc_, __p, __s_);} }; +template +class __allocator_destroyer +{ + typedef allocator_traits<_Alloc> __alloc_traits; + _Alloc __alloc_; +public: + _LIBCPP_INLINE_VISIBILITY __allocator_destroyer(_Alloc const& __a) + _NOEXCEPT + : __alloc_(__a) {} + + template + _LIBCPP_INLINE_VISIBILITY + void operator()(_Tp* __p) _NOEXCEPT + {__alloc_traits::destroy(__alloc_, __p);} +}; + template _ForwardIterator uninitialized_copy(_InputIterator __f, _InputIterator __l, _ForwardIterator __r) @@ -3510,6 +3526,20 @@ #endif } +template +struct __shared_ptr_aligned_block +{ + typename std::aligned_storage< + sizeof(_CntrlBlk), + alignment_of<_CntrlBlk>::value + >::type __cntrl_buff[sizeof(_CntrlBlk)]; + + typename std::aligned_storage< + sizeof(_Tp), + alignment_of<_CntrlBlk>::value + >::type __value_buff[sizeof(_Tp)]; +}; + template class _LIBCPP_TEMPLATE_VIS weak_ptr; class _LIBCPP_TYPE_VIS __shared_count @@ -3607,10 +3637,14 @@ : public __shared_weak_count { __compressed_pair<__compressed_pair<_Tp, _Dp>, _Alloc> __data_; + size_t __size; + public: _LIBCPP_INLINE_VISIBILITY - __shared_ptr_pointer(_Tp __p, _Dp __d, _Alloc __a) - : __data_(__compressed_pair<_Tp, _Dp>(__p, _VSTD::move(__d)), _VSTD::move(__a)) {} + __shared_ptr_pointer(_Tp __p, _Dp __d, _Alloc __a, + size_t __size = sizeof(__shared_ptr_pointer)) + : __data_(__compressed_pair<_Tp, _Dp>(__p, _VSTD::move(__d)), _VSTD::move(__a)), + __size(__size) {} #ifndef _LIBCPP_NO_RTTI virtual const void* __get_deleter(const type_info&) const _NOEXCEPT; @@ -3644,13 +3678,13 @@ void __shared_ptr_pointer<_Tp, _Dp, _Alloc>::__on_zero_shared_weak() _NOEXCEPT { - typedef typename __allocator_traits_rebind<_Alloc, __shared_ptr_pointer>::type _Al; + typedef typename __allocator_traits_rebind<_Alloc, char>::type _Al; typedef allocator_traits<_Al> _ATraits; typedef pointer_traits _PTraits; _Al __a(__data_.second()); __data_.second().~_Alloc(); - __a.deallocate(_PTraits::pointer_to(*this), 1); + __a.deallocate(_PTraits::pointer_to(*reinterpret_cast(this)), __size); } template @@ -4457,17 +4491,27 @@ { static_assert(is_constructible<_Tp, _Args...>::value, "Can't construct object in allocate_shared"); - typedef __shared_ptr_emplace<_Tp, _Alloc> _CntrlBlk; - typedef typename __allocator_traits_rebind<_Alloc, _CntrlBlk>::type _A2; + typedef typename __allocator_traits_rebind<_Alloc, _Tp>::type _TAlloc; + typedef __allocator_destroyer<_TAlloc> _D1; + typedef __shared_ptr_pointer<_Tp*, _D1, _Alloc> _CntrlBlk; + typedef __shared_ptr_aligned_block<_CntrlBlk, _Tp> _AlignedBlk; + typedef typename __allocator_traits_rebind<_Alloc, _AlignedBlk>::type _A2; typedef __allocator_destructor<_A2> _D2; + typedef allocator_traits<_TAlloc> _ATraits; _A2 __a2(__a); - unique_ptr<_CntrlBlk, _D2> __hold2(__a2.allocate(1), _D2(__a2, 1)); - ::new(static_cast(_VSTD::addressof(*__hold2.get()))) - _CntrlBlk(__a, _VSTD::forward<_Args>(__args)...); - - return shared_ptr<_Tp>::__create_with_cntrl_block(__hold2.get()->get(), - _VSTD::addressof(*__hold2.release())); + _TAlloc __a3(__a); + unique_ptr<_AlignedBlk, _D2> __hold2(__a2.allocate(1), _D2(__a2, 1)); + _CntrlBlk* __cntrl_buff = reinterpret_cast<_CntrlBlk*>(__hold2.get()->__cntrl_buff); + _Tp* __value_buff = reinterpret_cast<_Tp*> (__hold2.get()->__value_buff); + + ::new(static_cast(__cntrl_buff)) + _CntrlBlk(__value_buff, _D1(__a), __a2, sizeof(_AlignedBlk)); + _ATraits::construct(__a3, __value_buff, _VSTD::forward<_Args>(__args)...); + + __hold2.release(); + return shared_ptr<_Tp>::__create_with_cntrl_block(__value_buff, + __cntrl_buff); } #else // _LIBCPP_HAS_NO_VARIADICS Index: test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp =================================================================== --- /dev/null +++ test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp @@ -0,0 +1,147 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// shared_ptr + +// template +// shared_ptr allocate_shared(const A& a, Args&&... args); + +// This patch tests that allocator_traits::construct is used in allocate_shared. + +// XFAIL UNTIL THE PATCH IS FINISHED + +#include + +static bool construct_called = false; +static bool destroy_called = false; +static unsigned allocator_id = 0; + +template +struct MyAllocator { +public: + typedef T value_type; + typedef T* pointer; + + unsigned id = 0; + + MyAllocator() = default; + MyAllocator(int id) : id(id) {} + + template + MyAllocator(MyAllocator const& other) + : id(other.id) {}; + + pointer allocate(std::ptrdiff_t n) + { + return pointer(static_cast(::operator new(n*sizeof(T)))); + } + + void deallocate(pointer p, std::ptrdiff_t) + { + return ::operator delete(p); + } + + void construct(T *p) + { + construct_called = true; + destroy_called = false; + allocator_id = id; + ::new(p) T; + } + + void construct(T *p, T x) + { + construct_called = true; + destroy_called = false; + allocator_id = id; + ::new(p) T(x); + } + + void construct(T *p, T x, T y) + { + construct_called = true; + destroy_called = false; + allocator_id = id; + ::new(p) T(x, y); + } + + void destroy(T *p) + { + destroy_called = true; + construct_called = false; + allocator_id = id; + p->~T(); + } +}; + +struct mchar { char c; }; + +struct Foo +{ + int val; + + Foo(int val) : val(val) {} + + Foo(Foo a, Foo b) + : val(a.val + b.val) {} +}; + +struct Bar { + std::max_align_t y; +}; + +void test_aligned(void *p, size_t align) { + assert(reinterpret_cast(p) % align == 0); +} + +int main(int, char**) +{ + { + std::shared_ptr p = std::allocate_shared(MyAllocator()); + assert(construct_called); + } + assert(destroy_called); + + { + std::shared_ptr p = std::allocate_shared(MyAllocator(), 42); + assert(construct_called); + assert(*p == 42); + } + assert(destroy_called); + + { + std::shared_ptr p = std::allocate_shared(MyAllocator(), Foo(42), Foo(100)); + assert(construct_called); + assert(p->val == 142); + } + assert(destroy_called); + + { // make sure allocator is properly re-bound + std::shared_ptr p = std::allocate_shared(MyAllocator(), 42); + assert(construct_called); + assert(*p == 42); + } + assert(destroy_called); + + { // make sure allocator is copied + std::shared_ptr p = std::allocate_shared(MyAllocator(3)); + assert(allocator_id == 3); + + allocator_id = 0; + } + assert(allocator_id == 3); + + { + std::shared_ptr p; + test_aligned(p.get(), alignof(Bar)); + } + + return 0; +}