/* * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #include "precompiled.hpp" #include "runtime/atomic.hpp" #include "utilities/macros.hpp" #include "unittest.hpp" #include struct AtomicShortAddTest: public testing::Test { ATOMIC_SHORT_PAIR( volatile int16_t _atomic, int16_t _not_atomic ); void add_max() { Atomic::add(std::numeric_limits::max(), &_atomic); } void sub_max() { Atomic::add(std::numeric_limits::min(), &_atomic); } AtomicShortAddTest() : _atomic(0), _not_atomic(0) {} }; // This test tests whether the neighbouring short will be // touched by an overflow. TEST_VM_F(AtomicShortAddTest, test_short_add_overflow) { EXPECT_EQ(_atomic, 0); EXPECT_EQ(_not_atomic, 0); add_max(); EXPECT_EQ(_atomic, std::numeric_limits::max()); EXPECT_EQ(_not_atomic, 0); add_max(); EXPECT_EQ(_not_atomic, 0); } // This test tests whether the neighbouring short will be // touched by an underflow. TEST_VM_F(AtomicShortAddTest, test_short_add_underflow) { EXPECT_EQ(0, _atomic); EXPECT_EQ(_not_atomic, 0); sub_max(); EXPECT_EQ(_atomic, std::numeric_limits::min()); EXPECT_EQ(_not_atomic, 0); sub_max(); EXPECT_EQ(_not_atomic, 0); } class AtomicIntegerTest: public testing::Test { public: template void test_add() { volatile T value = 0; T result; const T zero = 0; const T one = 1; const T max = std::numeric_limits::max(); const T min = std::numeric_limits::min(); EXPECT_EQ(zero, value); result = Atomic::add(1, &value); EXPECT_EQ(one, value); EXPECT_EQ(result, value); result = Atomic::add(-1, &value); EXPECT_EQ(zero, value); EXPECT_EQ(result, value); result = Atomic::add(max, &value); EXPECT_EQ(max, value); EXPECT_EQ(result, value); } template void test_inc_dec() { volatile T value = 0; const T zero = 0; const T one = 1; const T max = std::numeric_limits::max(); const T min = std::numeric_limits::min(); EXPECT_EQ(zero, value); Atomic::inc(&value); EXPECT_EQ(one, value); Atomic::dec(&value); EXPECT_EQ(zero, value); Atomic::add(max - 1, &value); EXPECT_EQ(max - 1, value); Atomic::inc(&value); EXPECT_EQ(max, value); } template void test_cas() { const T max = std::numeric_limits::max(); const T min = std::numeric_limits::min(); volatile T value = min; // Successful cas T result = Atomic::cmpxchg(max, &value, min); EXPECT_EQ(max, value); EXPECT_EQ(min, result); // Unsuccessful cas result = Atomic::cmpxchg(max, &value, min); EXPECT_EQ(max, value); EXPECT_EQ(max, result); // Another successful cas result = Atomic::cmpxchg(min, &value, max); EXPECT_EQ(min, value); EXPECT_EQ(max, result); } template void test_swap() { const T zero = 0; const T one = 1; const T max = std::numeric_limits::max(); const T min = std::numeric_limits::min(); volatile T value = zero; T result = Atomic::xchg(one, &value); EXPECT_EQ(one, value); EXPECT_EQ(zero, result); value = min; result = Atomic::xchg(max, &value); EXPECT_EQ(max, value); EXPECT_EQ(min, result); result = Atomic::xchg(min, &value); EXPECT_EQ(min, value); EXPECT_EQ(max, result); } }; #define GENERATE_ATOMIC_INTEGER_TEST(T) \ TEST_VM_F(AtomicIntegerTest, atomic_add_##T) { \ test_add(); \ test_inc_dec(); \ test_cas(); \ test_swap(); \ } GENERATE_ATOMIC_INTEGER_TEST(int32) GENERATE_ATOMIC_INTEGER_TEST(intptr) GENERATE_ATOMIC_INTEGER_TEST(uint32) GENERATE_ATOMIC_INTEGER_TEST(uintptr) #undef GENERATE_ATOMIC_INTEGER_TEST TEST(AtomicTest, pointer_arithmetic_byte) { const intptr_t max = std::numeric_limits::max(); const intptr_t min = std::numeric_limits::min(); int8_t *volatile byte_array = NULL; Atomic::inc(&byte_array); EXPECT_EQ(1, IntegerTypes::cast_to_signed(byte_array)); int8_t* result = Atomic::add(5, &byte_array); EXPECT_EQ(6, IntegerTypes::cast_to_signed(byte_array)); EXPECT_EQ(6, IntegerTypes::cast_to_signed(result)); byte_array = NULL; result = Atomic::add(max, &byte_array); EXPECT_EQ(max, IntegerTypes::cast_to_signed(byte_array)); EXPECT_EQ(max, IntegerTypes::cast_to_signed(result)); Atomic::dec(&byte_array); EXPECT_EQ(max - 1, IntegerTypes::cast_to_signed(byte_array)); } TEST(AtomicTest, pointer_arithmetic_pointer) { const intptr_t max = std::numeric_limits::max() / sizeof(void*); const intptr_t min = std::numeric_limits::min() / sizeof(void*); void **volatile pointer_array = NULL; Atomic::inc(&pointer_array); EXPECT_EQ(IntegerTypes::cast_to_signed(1 * sizeof(void*)), IntegerTypes::cast_to_signed(pointer_array)); void** result = Atomic::add(5, &pointer_array); EXPECT_EQ(IntegerTypes::cast_to_signed(6 * sizeof(void*)), IntegerTypes::cast_to_signed(pointer_array)); EXPECT_EQ(IntegerTypes::cast_to_signed(6 * sizeof(void*)), IntegerTypes::cast_to_signed(result)); pointer_array = NULL; result = Atomic::add(max, &pointer_array); EXPECT_EQ(IntegerTypes::cast_to_signed(max * sizeof(void*)), IntegerTypes::cast_to_signed(pointer_array)); EXPECT_EQ(IntegerTypes::cast_to_signed(max * sizeof(void*)), IntegerTypes::cast_to_signed(result)); Atomic::dec(&pointer_array); EXPECT_EQ(IntegerTypes::cast_to_signed((max - 1) * sizeof(void*)), IntegerTypes::cast_to_signed(pointer_array)); }