| // Copyright (c) 2009 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 <stdio.h> |
| #include <stdlib.h> |
| #include <algorithm> // for min() |
| #include "base/atomicops.h" |
| #include "base/logging.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| // Number of bits in a size_t. |
| static const int kSizeBits = 8 * sizeof(size_t); |
| // The maximum size of a size_t. |
| static const size_t kMaxSize = ~static_cast<size_t>(0); |
| // Maximum positive size of a size_t if it were signed. |
| static const size_t kMaxSignedSize = ((size_t(1) << (kSizeBits-1)) - 1); |
| // An allocation size which is not too big to be reasonable. |
| static const size_t kNotTooBig = 100000; |
| // An allocation size which is just too big. |
| static const size_t kTooBig = ~static_cast<size_t>(0); |
| |
| namespace { |
| |
| using std::min; |
| |
| // Fill a buffer of the specified size with a predetermined pattern |
| static void Fill(unsigned char* buffer, int n) { |
| for (int i = 0; i < n; i++) { |
| buffer[i] = (i & 0xff); |
| } |
| } |
| |
| // Check that the specified buffer has the predetermined pattern |
| // generated by Fill() |
| static bool Valid(unsigned char* buffer, int n) { |
| for (int i = 0; i < n; i++) { |
| if (buffer[i] != (i & 0xff)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Check that a buffer is completely zeroed. |
| static bool IsZeroed(unsigned char* buffer, int n) { |
| for (int i = 0; i < n; i++) { |
| if (buffer[i] != 0) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Check alignment |
| static void CheckAlignment(void* p, int align) { |
| EXPECT_EQ(0, reinterpret_cast<uintptr_t>(p) & (align-1)); |
| } |
| |
| // Return the next interesting size/delta to check. Returns -1 if no more. |
| static int NextSize(int size) { |
| if (size < 100) |
| return size+1; |
| |
| if (size < 100000) { |
| // Find next power of two |
| int power = 1; |
| while (power < size) |
| power <<= 1; |
| |
| // Yield (power-1, power, power+1) |
| if (size < power-1) |
| return power-1; |
| |
| if (size == power-1) |
| return power; |
| |
| assert(size == power); |
| return power+1; |
| } else { |
| return -1; |
| } |
| } |
| |
| #define GG_ULONGLONG(x) static_cast<uint64>(x) |
| |
| template <class AtomicType> |
| static void TestAtomicIncrement() { |
| // For now, we just test single threaded execution |
| |
| // use a guard value to make sure the NoBarrier_AtomicIncrement doesn't go |
| // outside the expected address bounds. This is in particular to |
| // test that some future change to the asm code doesn't cause the |
| // 32-bit NoBarrier_AtomicIncrement to do the wrong thing on 64-bit machines. |
| struct { |
| AtomicType prev_word; |
| AtomicType count; |
| AtomicType next_word; |
| } s; |
| |
| AtomicType prev_word_value, next_word_value; |
| memset(&prev_word_value, 0xFF, sizeof(AtomicType)); |
| memset(&next_word_value, 0xEE, sizeof(AtomicType)); |
| |
| s.prev_word = prev_word_value; |
| s.count = 0; |
| s.next_word = next_word_value; |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 1), 1); |
| EXPECT_EQ(s.count, 1); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 2), 3); |
| EXPECT_EQ(s.count, 3); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 3), 6); |
| EXPECT_EQ(s.count, 6); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -3), 3); |
| EXPECT_EQ(s.count, 3); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -2), 1); |
| EXPECT_EQ(s.count, 1); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), 0); |
| EXPECT_EQ(s.count, 0); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), -1); |
| EXPECT_EQ(s.count, -1); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -4), -5); |
| EXPECT_EQ(s.count, -5); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| |
| EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 5), 0); |
| EXPECT_EQ(s.count, 0); |
| EXPECT_EQ(s.prev_word, prev_word_value); |
| EXPECT_EQ(s.next_word, next_word_value); |
| } |
| |
| |
| #define NUM_BITS(T) (sizeof(T) * 8) |
| |
| |
| template <class AtomicType> |
| static void TestCompareAndSwap() { |
| AtomicType value = 0; |
| AtomicType prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 1); |
| EXPECT_EQ(1, value); |
| EXPECT_EQ(0, prev); |
| |
| // Use test value that has non-zero bits in both halves, more for testing |
| // 64-bit implementation on 32-bit platforms. |
| const AtomicType k_test_val = (GG_ULONGLONG(1) << |
| (NUM_BITS(AtomicType) - 2)) + 11; |
| value = k_test_val; |
| prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5); |
| EXPECT_EQ(k_test_val, value); |
| EXPECT_EQ(k_test_val, prev); |
| |
| value = k_test_val; |
| prev = base::subtle::NoBarrier_CompareAndSwap(&value, k_test_val, 5); |
| EXPECT_EQ(5, value); |
| EXPECT_EQ(k_test_val, prev); |
| } |
| |
| |
| template <class AtomicType> |
| static void TestAtomicExchange() { |
| AtomicType value = 0; |
| AtomicType new_value = base::subtle::NoBarrier_AtomicExchange(&value, 1); |
| EXPECT_EQ(1, value); |
| EXPECT_EQ(0, new_value); |
| |
| // Use test value that has non-zero bits in both halves, more for testing |
| // 64-bit implementation on 32-bit platforms. |
| const AtomicType k_test_val = (GG_ULONGLONG(1) << |
| (NUM_BITS(AtomicType) - 2)) + 11; |
| value = k_test_val; |
| new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val); |
| EXPECT_EQ(k_test_val, value); |
| EXPECT_EQ(k_test_val, new_value); |
| |
| value = k_test_val; |
| new_value = base::subtle::NoBarrier_AtomicExchange(&value, 5); |
| EXPECT_EQ(5, value); |
| EXPECT_EQ(k_test_val, new_value); |
| } |
| |
| |
| template <class AtomicType> |
| static void TestAtomicIncrementBounds() { |
| // Test increment at the half-width boundary of the atomic type. |
| // It is primarily for testing at the 32-bit boundary for 64-bit atomic type. |
| AtomicType test_val = GG_ULONGLONG(1) << (NUM_BITS(AtomicType) / 2); |
| AtomicType value = test_val - 1; |
| AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1); |
| EXPECT_EQ(test_val, value); |
| EXPECT_EQ(value, new_value); |
| |
| base::subtle::NoBarrier_AtomicIncrement(&value, -1); |
| EXPECT_EQ(test_val - 1, value); |
| } |
| |
| // This is a simple sanity check that values are correct. Not testing |
| // atomicity |
| template <class AtomicType> |
| static void TestStore() { |
| const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL); |
| const AtomicType kVal2 = static_cast<AtomicType>(-1); |
| |
| AtomicType value; |
| |
| base::subtle::NoBarrier_Store(&value, kVal1); |
| EXPECT_EQ(kVal1, value); |
| base::subtle::NoBarrier_Store(&value, kVal2); |
| EXPECT_EQ(kVal2, value); |
| |
| base::subtle::Acquire_Store(&value, kVal1); |
| EXPECT_EQ(kVal1, value); |
| base::subtle::Acquire_Store(&value, kVal2); |
| EXPECT_EQ(kVal2, value); |
| |
| base::subtle::Release_Store(&value, kVal1); |
| EXPECT_EQ(kVal1, value); |
| base::subtle::Release_Store(&value, kVal2); |
| EXPECT_EQ(kVal2, value); |
| } |
| |
| // This is a simple sanity check that values are correct. Not testing |
| // atomicity |
| template <class AtomicType> |
| static void TestLoad() { |
| const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL); |
| const AtomicType kVal2 = static_cast<AtomicType>(-1); |
| |
| AtomicType value; |
| |
| value = kVal1; |
| EXPECT_EQ(kVal1, base::subtle::NoBarrier_Load(&value)); |
| value = kVal2; |
| EXPECT_EQ(kVal2, base::subtle::NoBarrier_Load(&value)); |
| |
| value = kVal1; |
| EXPECT_EQ(kVal1, base::subtle::Acquire_Load(&value)); |
| value = kVal2; |
| EXPECT_EQ(kVal2, base::subtle::Acquire_Load(&value)); |
| |
| value = kVal1; |
| EXPECT_EQ(kVal1, base::subtle::Release_Load(&value)); |
| value = kVal2; |
| EXPECT_EQ(kVal2, base::subtle::Release_Load(&value)); |
| } |
| |
| template <class AtomicType> |
| static void TestAtomicOps() { |
| TestCompareAndSwap<AtomicType>(); |
| TestAtomicExchange<AtomicType>(); |
| TestAtomicIncrementBounds<AtomicType>(); |
| TestStore<AtomicType>(); |
| TestLoad<AtomicType>(); |
| } |
| |
| static void TestCalloc(size_t n, size_t s, bool ok) { |
| char* p = reinterpret_cast<char*>(calloc(n, s)); |
| if (!ok) { |
| EXPECT_EQ(NULL, p) << "calloc(n, s) should not succeed"; |
| } else { |
| EXPECT_NE(reinterpret_cast<void*>(NULL), p) << |
| "calloc(n, s) should succeed"; |
| for (int i = 0; i < n*s; i++) { |
| EXPECT_EQ('\0', p[i]); |
| } |
| free(p); |
| } |
| } |
| |
| |
| // A global test counter for number of times the NewHandler is called. |
| static int news_handled = 0; |
| static void TestNewHandler() { |
| ++news_handled; |
| throw std::bad_alloc(); |
| } |
| |
| // Because we compile without exceptions, we expect these will not throw. |
| static void TestOneNewWithoutExceptions(void* (*func)(size_t), |
| bool should_throw) { |
| // success test |
| try { |
| void* ptr = (*func)(kNotTooBig); |
| EXPECT_NE(reinterpret_cast<void*>(NULL), ptr) << |
| "allocation should not have failed."; |
| } catch(...) { |
| EXPECT_EQ(0, 1) << "allocation threw unexpected exception."; |
| } |
| |
| // failure test |
| try { |
| void* rv = (*func)(kTooBig); |
| EXPECT_EQ(NULL, rv); |
| EXPECT_FALSE(should_throw) << "allocation should have thrown."; |
| } catch(...) { |
| EXPECT_TRUE(should_throw) << "allocation threw unexpected exception."; |
| } |
| } |
| |
| static void TestNothrowNew(void* (*func)(size_t)) { |
| news_handled = 0; |
| |
| // test without new_handler: |
| std::new_handler saved_handler = std::set_new_handler(0); |
| TestOneNewWithoutExceptions(func, false); |
| |
| // test with new_handler: |
| std::set_new_handler(TestNewHandler); |
| TestOneNewWithoutExceptions(func, true); |
| EXPECT_EQ(news_handled, 1) << "nothrow new_handler was not called."; |
| std::set_new_handler(saved_handler); |
| } |
| |
| } // namespace |
| |
| //----------------------------------------------------------------------------- |
| |
| TEST(Atomics, AtomicIncrementWord) { |
| TestAtomicIncrement<AtomicWord>(); |
| } |
| |
| TEST(Atomics, AtomicIncrement32) { |
| TestAtomicIncrement<Atomic32>(); |
| } |
| |
| TEST(Atomics, AtomicOpsWord) { |
| TestAtomicIncrement<AtomicWord>(); |
| } |
| |
| TEST(Atomics, AtomicOps32) { |
| TestAtomicIncrement<Atomic32>(); |
| } |
| |
| TEST(Allocators, Malloc) { |
| // Try allocating data with a bunch of alignments and sizes |
| for (int size = 1; size < 1048576; size *= 2) { |
| unsigned char* ptr = reinterpret_cast<unsigned char*>(malloc(size)); |
| CheckAlignment(ptr, 2); // Should be 2 byte aligned |
| Fill(ptr, size); |
| EXPECT_TRUE(Valid(ptr, size)); |
| free(ptr); |
| } |
| } |
| |
| TEST(Allocators, Calloc) { |
| TestCalloc(0, 0, true); |
| TestCalloc(0, 1, true); |
| TestCalloc(1, 1, true); |
| TestCalloc(1<<10, 0, true); |
| TestCalloc(1<<20, 0, true); |
| TestCalloc(0, 1<<10, true); |
| TestCalloc(0, 1<<20, true); |
| TestCalloc(1<<20, 2, true); |
| TestCalloc(2, 1<<20, true); |
| TestCalloc(1000, 1000, true); |
| |
| TestCalloc(kMaxSize, 2, false); |
| TestCalloc(2, kMaxSize, false); |
| TestCalloc(kMaxSize, kMaxSize, false); |
| |
| TestCalloc(kMaxSignedSize, 3, false); |
| TestCalloc(3, kMaxSignedSize, false); |
| TestCalloc(kMaxSignedSize, kMaxSignedSize, false); |
| } |
| |
| TEST(Allocators, New) { |
| TestNothrowNew(&::operator new); |
| TestNothrowNew(&::operator new[]); |
| } |
| |
| // This makes sure that reallocing a small number of bytes in either |
| // direction doesn't cause us to allocate new memory. |
| TEST(Allocators, Realloc1) { |
| int start_sizes[] = { 100, 1000, 10000, 100000 }; |
| int deltas[] = { 1, -2, 4, -8, 16, -32, 64, -128 }; |
| |
| for (int s = 0; s < sizeof(start_sizes)/sizeof(*start_sizes); ++s) { |
| void* p = malloc(start_sizes[s]); |
| CHECK(p); |
| // The larger the start-size, the larger the non-reallocing delta. |
| for (int d = 0; d < s*2; ++d) { |
| void* new_p = realloc(p, start_sizes[s] + deltas[d]); |
| CHECK_EQ(p, new_p); // realloc should not allocate new memory |
| } |
| // Test again, but this time reallocing smaller first. |
| for (int d = 0; d < s*2; ++d) { |
| void* new_p = realloc(p, start_sizes[s] - deltas[d]); |
| CHECK_EQ(p, new_p); // realloc should not allocate new memory |
| } |
| free(p); |
| } |
| } |
| |
| TEST(Allocators, Realloc2) { |
| for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) { |
| for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) { |
| unsigned char* src = reinterpret_cast<unsigned char*>(malloc(src_size)); |
| Fill(src, src_size); |
| unsigned char* dst = |
| reinterpret_cast<unsigned char*>(realloc(src, dst_size)); |
| EXPECT_TRUE(Valid(dst, min(src_size, dst_size))); |
| Fill(dst, dst_size); |
| EXPECT_TRUE(Valid(dst, dst_size)); |
| if (dst != NULL) free(dst); |
| } |
| } |
| |
| // Now make sure realloc works correctly even when we overflow the |
| // packed cache, so some entries are evicted from the cache. |
| // The cache has 2^12 entries, keyed by page number. |
| const int kNumEntries = 1 << 14; |
| int** p = reinterpret_cast<int**>(malloc(sizeof(*p) * kNumEntries)); |
| int sum = 0; |
| for (int i = 0; i < kNumEntries; i++) { |
| // no page size is likely to be bigger than 8192? |
| p[i] = reinterpret_cast<int*>(malloc(8192)); |
| p[i][1000] = i; // use memory deep in the heart of p |
| } |
| for (int i = 0; i < kNumEntries; i++) { |
| p[i] = reinterpret_cast<int*>(realloc(p[i], 9000)); |
| } |
| for (int i = 0; i < kNumEntries; i++) { |
| sum += p[i][1000]; |
| free(p[i]); |
| } |
| EXPECT_EQ(kNumEntries/2 * (kNumEntries - 1), sum); // assume kNE is even |
| free(p); |
| } |
| |
| TEST(Allocators, ReallocZero) { |
| // Test that realloc to zero does not return NULL. |
| for (int size = 0; size >= 0; size = NextSize(size)) { |
| char* ptr = reinterpret_cast<char*>(malloc(size)); |
| EXPECT_NE(static_cast<char*>(NULL), ptr); |
| ptr = reinterpret_cast<char*>(realloc(ptr, 0)); |
| EXPECT_NE(static_cast<char*>(NULL), ptr); |
| if (ptr) |
| free(ptr); |
| } |
| } |
| |
| #ifdef WIN32 |
| // Test recalloc |
| TEST(Allocators, Recalloc) { |
| for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) { |
| for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) { |
| unsigned char* src = |
| reinterpret_cast<unsigned char*>(_recalloc(NULL, 1, src_size)); |
| EXPECT_TRUE(IsZeroed(src, src_size)); |
| Fill(src, src_size); |
| unsigned char* dst = |
| reinterpret_cast<unsigned char*>(_recalloc(src, 1, dst_size)); |
| EXPECT_TRUE(Valid(dst, min(src_size, dst_size))); |
| Fill(dst, dst_size); |
| EXPECT_TRUE(Valid(dst, dst_size)); |
| if (dst != NULL) |
| free(dst); |
| } |
| } |
| } |
| #endif |
| |
| |
| int main(int argc, char** argv) { |
| testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |