Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package

allocator_unittests.cc

// 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_EQ(false, should_throw) << "allocation should have thrown.";
  } catch(...) {
    EXPECT_EQ(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_EQ(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_EQ(true, Valid(dst, min(src_size, dst_size)));
      Fill(dst, dst_size);
      EXPECT_EQ(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_EQ(true, IsZeroed(src, src_size));
      Fill(src, src_size);
      unsigned char* dst =
          reinterpret_cast<unsigned char*>(_recalloc(src, 1, dst_size));
      EXPECT_EQ(true, Valid(dst, min(src_size, dst_size)));
      Fill(dst, dst_size);
      EXPECT_EQ(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();
}

Generated by  Doxygen 1.6.0   Back to index