diff options
author | jbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-23 17:52:20 +0000 |
---|---|---|
committer | jbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-23 17:52:20 +0000 |
commit | cd924d6e5efdf80f66283ac66987f2b339c381cf (patch) | |
tree | 89d2ccd8588e12669b93eb98117e8d76a766fbf2 /base/memory | |
parent | 2e8bb832c7b3867b3a10b81621830add0dd5bfb5 (diff) | |
download | chromium_src-cd924d6e5efdf80f66283ac66987f2b339c381cf.zip chromium_src-cd924d6e5efdf80f66283ac66987f2b339c381cf.tar.gz chromium_src-cd924d6e5efdf80f66283ac66987f2b339c381cf.tar.bz2 |
Add ALIGNAS and ALIGNOF macros to ensure proper alignment of StaticMemorySingletonTraits
BUG=95006
Review URL: https://chromiumcodereview.appspot.com/9186057
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@123270 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/memory')
-rw-r--r-- | base/memory/aligned_memory.h | 77 | ||||
-rw-r--r-- | base/memory/aligned_memory_unittest.cc | 50 | ||||
-rw-r--r-- | base/memory/singleton.h | 18 | ||||
-rw-r--r-- | base/memory/singleton_unittest.cc | 37 |
4 files changed, 170 insertions, 12 deletions
diff --git a/base/memory/aligned_memory.h b/base/memory/aligned_memory.h new file mode 100644 index 0000000..dc9db90 --- /dev/null +++ b/base/memory/aligned_memory.h @@ -0,0 +1,77 @@ +// Copyright (c) 2012 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. + +// AlignedMemory is a POD type that gives you a portable way to specify static +// or local stack data of a given alignment and size. For example, if you need +// static storage for a class, but you want manual control over when the object +// is constructed and destructed (you don't want static initialization and +// destruction), use AlignedMemory: +// +// static AlignedMemory<sizeof(MyClass), ALIGNOF(MyClass)> my_class; +// +// // ... at runtime: +// new(my_class.void_data()) MyClass(); +// +// // ... use it: +// MyClass* mc = my_class.data_as<MyClass>(); +// +// // ... later, to destruct my_class: +// my_class.data_as<MyClass>()->MyClass::~MyClass(); + +#ifndef BASE_MEMORY_ALIGNED_MEMORY_H_ +#define BASE_MEMORY_ALIGNED_MEMORY_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/logging.h" + +namespace base { + +// AlignedMemory is specialized for all supported alignments. +// Make sure we get a compiler error if someone uses an unsupported alignment. +template <size_t Size, size_t ByteAlignment> +struct AlignedMemory {}; + +#define BASE_DECL_ALIGNED_MEMORY(byte_alignment) \ + template <size_t Size> \ + class AlignedMemory<Size, byte_alignment> { \ + public: \ + ALIGNAS(byte_alignment) uint8 data_[Size]; \ + void* void_data() { return reinterpret_cast<void*>(data_); } \ + const void* void_data() const { \ + return reinterpret_cast<const void*>(data_); \ + } \ + template<typename Type> \ + Type* data_as() { return reinterpret_cast<Type*>(void_data()); } \ + template<typename Type> \ + const Type* data_as() const { \ + return reinterpret_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) +// does not understand ALIGNAS(ALIGNOF(Type)) or ALIGNAS(template_param). +// Greater than 4096 alignment is not supported by some compilers, so 4096 is +// the maximum specified here. +BASE_DECL_ALIGNED_MEMORY(1); +BASE_DECL_ALIGNED_MEMORY(2); +BASE_DECL_ALIGNED_MEMORY(4); +BASE_DECL_ALIGNED_MEMORY(8); +BASE_DECL_ALIGNED_MEMORY(16); +BASE_DECL_ALIGNED_MEMORY(32); +BASE_DECL_ALIGNED_MEMORY(64); +BASE_DECL_ALIGNED_MEMORY(128); +BASE_DECL_ALIGNED_MEMORY(256); +BASE_DECL_ALIGNED_MEMORY(512); +BASE_DECL_ALIGNED_MEMORY(1024); +BASE_DECL_ALIGNED_MEMORY(2048); +BASE_DECL_ALIGNED_MEMORY(4096); + +} // base + +#endif // BASE_MEMORY_ALIGNED_MEMORY_H_ diff --git a/base/memory/aligned_memory_unittest.cc b/base/memory/aligned_memory_unittest.cc new file mode 100644 index 0000000..065e952 --- /dev/null +++ b/base/memory/aligned_memory_unittest.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2011 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. + +#include "base/memory/aligned_memory.h" +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define EXPECT_ALIGNED(ptr, align) \ + EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1)) + +namespace { + +using base::AlignedMemory; + +TEST(AlignedMemoryTest, StaticAlignment) { + static AlignedMemory<8, 8> raw8; + static AlignedMemory<8, 16> raw16; + static AlignedMemory<8, 256> raw256; + static AlignedMemory<8, 4096> raw4096; + + EXPECT_EQ(8u, ALIGNOF(raw8)); + EXPECT_EQ(16u, ALIGNOF(raw16)); + EXPECT_EQ(256u, ALIGNOF(raw256)); + EXPECT_EQ(4096u, ALIGNOF(raw4096)); + + EXPECT_ALIGNED(raw8.void_data(), 8); + EXPECT_ALIGNED(raw16.void_data(), 16); + EXPECT_ALIGNED(raw256.void_data(), 256); + EXPECT_ALIGNED(raw4096.void_data(), 4096); +} + +TEST(AlignedMemoryTest, StackAlignment) { + AlignedMemory<8, 8> raw8; + AlignedMemory<8, 16> raw16; + AlignedMemory<8, 256> raw256; + AlignedMemory<8, 4096> raw4096; + + EXPECT_EQ(8u, ALIGNOF(raw8)); + EXPECT_EQ(16u, ALIGNOF(raw16)); + EXPECT_EQ(256u, ALIGNOF(raw256)); + EXPECT_EQ(4096u, ALIGNOF(raw4096)); + + EXPECT_ALIGNED(raw8.void_data(), 8); + EXPECT_ALIGNED(raw16.void_data(), 16); + EXPECT_ALIGNED(raw256.void_data(), 256); + EXPECT_ALIGNED(raw4096.void_data(), 4096); +} + +} // namespace diff --git a/base/memory/singleton.h b/base/memory/singleton.h index d76070019..5549012 100644 --- a/base/memory/singleton.h +++ b/base/memory/singleton.h @@ -23,6 +23,7 @@ #include "base/at_exit.h" #include "base/atomicops.h" #include "base/base_export.h" +#include "base/memory/aligned_memory.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread_restrictions.h" @@ -106,18 +107,14 @@ struct StaticMemorySingletonTraits { // WARNING: User has to deal with get() in the singleton class // this is traits for returning NULL. static Type* New() { + // Only constructs once and returns pointer; otherwise returns NULL. if (base::subtle::NoBarrier_AtomicExchange(&dead_, 1)) return NULL; - Type* ptr = reinterpret_cast<Type*>(buffer_); - // We are protected by a memory barrier. - new(ptr) Type(); - return ptr; + return new(buffer_.void_data()) Type(); } static void Delete(Type* p) { - base::subtle::NoBarrier_Store(&dead_, 1); - base::subtle::MemoryBarrier(); if (p != NULL) p->Type::~Type(); } @@ -131,16 +128,13 @@ struct StaticMemorySingletonTraits { } private: - static const size_t kBufferSize = (sizeof(Type) + - sizeof(intptr_t) - 1) / sizeof(intptr_t); - static intptr_t buffer_[kBufferSize]; - + static base::AlignedMemory<sizeof(Type), ALIGNOF(Type)> buffer_; // Signal the object was already deleted, so it is not revived. static base::subtle::Atomic32 dead_; }; -template <typename Type> intptr_t - StaticMemorySingletonTraits<Type>::buffer_[kBufferSize]; +template <typename Type> base::AlignedMemory<sizeof(Type), ALIGNOF(Type)> + StaticMemorySingletonTraits<Type>::buffer_; template <typename Type> base::subtle::Atomic32 StaticMemorySingletonTraits<Type>::dead_ = 0; diff --git a/base/memory/singleton_unittest.cc b/base/memory/singleton_unittest.cc index 068148f..33928a7 100644 --- a/base/memory/singleton_unittest.cc +++ b/base/memory/singleton_unittest.cc @@ -110,6 +110,19 @@ struct CallbackSingletonWithStaticTrait::Trait } }; +template <class Type> +class AlignedTestSingleton { + public: + AlignedTestSingleton() {} + ~AlignedTestSingleton() {} + static AlignedTestSingleton* GetInstance() { + return Singleton<AlignedTestSingleton, + StaticMemorySingletonTraits<AlignedTestSingleton> >::get(); + } + + Type type_; +}; + void SingletonNoLeak(CallbackFunc CallOnQuit) { CallbackSingletonWithNoLeakTrait::GetInstance()->callback_ = CallOnQuit; @@ -250,3 +263,27 @@ TEST_F(SingletonTest, Basic) { // The leaky singleton shouldn't leak since SingletonLeak has not been called. VerifiesCallbacksNotCalled(); } + +#define EXPECT_ALIGNED(ptr, align) \ + EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1)) + +TEST_F(SingletonTest, Alignment) { + using base::AlignedMemory; + + // Create some static singletons with increasing sizes and alignment + // requirements. By ordering this way, the linker will need to do some work to + // ensure proper alignment of the static data. + AlignedTestSingleton<int32>* align4 = + AlignedTestSingleton<int32>::GetInstance(); + AlignedTestSingleton<AlignedMemory<32, 32> >* align32 = + AlignedTestSingleton<AlignedMemory<32, 32> >::GetInstance(); + AlignedTestSingleton<AlignedMemory<128, 128> >* align128 = + AlignedTestSingleton<AlignedMemory<128, 128> >::GetInstance(); + AlignedTestSingleton<AlignedMemory<4096, 4096> >* align4096 = + AlignedTestSingleton<AlignedMemory<4096, 4096> >::GetInstance(); + + EXPECT_ALIGNED(align4, 4); + EXPECT_ALIGNED(align32, 32); + EXPECT_ALIGNED(align128, 128); + EXPECT_ALIGNED(align4096, 4096); +} |