8157904: Atomic::cmpxchg for jbyte is missing a fence on initial failure
Reviewed-by: simonis, aph, kbarrett
/*
* Copyright (c) 1999, 2016, 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.
*
*/
#ifndef SHARE_VM_RUNTIME_ATOMIC_HPP
#define SHARE_VM_RUNTIME_ATOMIC_HPP
#include "memory/allocation.hpp"
#include "utilities/macros.hpp"
enum cmpxchg_memory_order {
memory_order_relaxed,
// Use value which doesn't interfere with C++2011. We need to be more conservative.
memory_order_conservative = 8
};
class Atomic : AllStatic {
public:
// Atomic operations on jlong types are not available on all 32-bit
// platforms. If atomic ops on jlongs are defined here they must only
// be used from code that verifies they are available at runtime and
// can provide an alternative action if not - see supports_cx8() for
// a means to test availability.
// The memory operations that are mentioned with each of the atomic
// function families come from src/share/vm/runtime/orderAccess.hpp,
// e.g., <fence> is described in that file and is implemented by the
// OrderAccess::fence() function. See that file for the gory details
// on the Memory Access Ordering Model.
// All of the atomic operations that imply a read-modify-write action
// guarantee a two-way memory barrier across that operation. Historically
// these semantics reflect the strength of atomic operations that are
// provided on SPARC/X86. We assume that strength is necessary unless
// we can prove that a weaker form is sufficiently safe.
// Atomically store to a location
inline static void store (jbyte store_value, jbyte* dest);
inline static void store (jshort store_value, jshort* dest);
inline static void store (jint store_value, jint* dest);
// See comment above about using jlong atomics on 32-bit platforms
inline static void store (jlong store_value, jlong* dest);
inline static void store_ptr(intptr_t store_value, intptr_t* dest);
inline static void store_ptr(void* store_value, void* dest);
inline static void store (jbyte store_value, volatile jbyte* dest);
inline static void store (jshort store_value, volatile jshort* dest);
inline static void store (jint store_value, volatile jint* dest);
// See comment above about using jlong atomics on 32-bit platforms
inline static void store (jlong store_value, volatile jlong* dest);
inline static void store_ptr(intptr_t store_value, volatile intptr_t* dest);
inline static void store_ptr(void* store_value, volatile void* dest);
// See comment above about using jlong atomics on 32-bit platforms
inline static jlong load(volatile jlong* src);
// Atomically add to a location. Returns updated value. add*() provide:
// <fence> add-value-to-dest <membar StoreLoad|StoreStore>
inline static jint add (jint add_value, volatile jint* dest);
inline static size_t add (size_t add_value, volatile size_t* dest);
inline static intptr_t add_ptr(intptr_t add_value, volatile intptr_t* dest);
inline static void* add_ptr(intptr_t add_value, volatile void* dest);
// See comment above about using jlong atomics on 32-bit platforms
inline static jlong add (jlong add_value, volatile jlong* dest);
// Atomically increment location. inc*() provide:
// <fence> increment-dest <membar StoreLoad|StoreStore>
inline static void inc (volatile jint* dest);
inline static void inc (volatile jshort* dest);
inline static void inc (volatile size_t* dest);
inline static void inc_ptr(volatile intptr_t* dest);
inline static void inc_ptr(volatile void* dest);
// Atomically decrement a location. dec*() provide:
// <fence> decrement-dest <membar StoreLoad|StoreStore>
inline static void dec (volatile jint* dest);
inline static void dec (volatile jshort* dest);
inline static void dec (volatile size_t* dest);
inline static void dec_ptr(volatile intptr_t* dest);
inline static void dec_ptr(volatile void* dest);
// Performs atomic exchange of *dest with exchange_value. Returns old
// prior value of *dest. xchg*() provide:
// <fence> exchange-value-with-dest <membar StoreLoad|StoreStore>
inline static jint xchg (jint exchange_value, volatile jint* dest);
inline static unsigned int xchg (unsigned int exchange_value, volatile unsigned int* dest);
inline static intptr_t xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest);
inline static void* xchg_ptr(void* exchange_value, volatile void* dest);
// Performs atomic compare of *dest and compare_value, and exchanges
// *dest with exchange_value if the comparison succeeded. Returns prior
// value of *dest. cmpxchg*() provide:
// <fence> compare-and-exchange <membar StoreLoad|StoreStore>
inline static jbyte cmpxchg (jbyte exchange_value, volatile jbyte* dest, jbyte compare_value, cmpxchg_memory_order order = memory_order_conservative);
inline static jint cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order = memory_order_conservative);
// See comment above about using jlong atomics on 32-bit platforms
inline static jlong cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value, cmpxchg_memory_order order = memory_order_conservative);
inline static unsigned int cmpxchg (unsigned int exchange_value, volatile unsigned int* dest, unsigned int compare_value, cmpxchg_memory_order order = memory_order_conservative);
inline static intptr_t cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value, cmpxchg_memory_order order = memory_order_conservative);
inline static void* cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value, cmpxchg_memory_order order = memory_order_conservative);
};
// platform specific in-line definitions - must come before shared definitions
#include OS_CPU_HEADER(atomic)
// shared in-line definitions
// size_t casts...
#if (SIZE_MAX != UINTPTR_MAX)
#error size_t is not WORD_SIZE, interesting platform, but missing implementation here
#endif
inline size_t Atomic::add(size_t add_value, volatile size_t* dest) {
return (size_t) add_ptr((intptr_t) add_value, (volatile intptr_t*) dest);
}
inline void Atomic::inc(volatile size_t* dest) {
inc_ptr((volatile intptr_t*) dest);
}
inline void Atomic::dec(volatile size_t* dest) {
dec_ptr((volatile intptr_t*) dest);
}
#ifndef VM_HAS_SPECIALIZED_CMPXCHG_BYTE
/*
* This is the default implementation of byte-sized cmpxchg. It emulates jbyte-sized cmpxchg
* in terms of jint-sized cmpxchg. Platforms may override this by defining their own inline definition
* as well as defining VM_HAS_SPECIALIZED_CMPXCHG_BYTE. This will cause the platform specific
* implementation to be used instead.
*/
inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest,
jbyte compare_value, cmpxchg_memory_order order) {
STATIC_ASSERT(sizeof(jbyte) == 1);
volatile jint* dest_int =
static_cast<volatile jint*>(align_ptr_down(dest, sizeof(jint)));
size_t offset = pointer_delta(dest, dest_int, 1);
jint cur = *dest_int;
jbyte* cur_as_bytes = reinterpret_cast<jbyte*>(&cur);
// current value may not be what we are looking for, so force it
// to that value so the initial cmpxchg will fail if it is different
cur_as_bytes[offset] = compare_value;
// always execute a real cmpxchg so that we get the required memory
// barriers even on initial failure
do {
// value to swap in matches current value ...
jint new_value = cur;
// ... except for the one jbyte we want to update
reinterpret_cast<jbyte*>(&new_value)[offset] = exchange_value;
jint res = cmpxchg(new_value, dest_int, cur, order);
if (res == cur) break; // success
// at least one jbyte in the jint changed value, so update
// our view of the current jint
cur = res;
// if our jbyte is still as cur we loop and try again
} while (cur_as_bytes[offset] == compare_value);
return cur_as_bytes[offset];
}
#endif // VM_HAS_SPECIALIZED_CMPXCHG_BYTE
inline unsigned Atomic::xchg(unsigned int exchange_value, volatile unsigned int* dest) {
assert(sizeof(unsigned int) == sizeof(jint), "more work to do");
return (unsigned int)Atomic::xchg((jint)exchange_value, (volatile jint*)dest);
}
inline unsigned Atomic::cmpxchg(unsigned int exchange_value,
volatile unsigned int* dest, unsigned int compare_value,
cmpxchg_memory_order order) {
assert(sizeof(unsigned int) == sizeof(jint), "more work to do");
return (unsigned int)Atomic::cmpxchg((jint)exchange_value, (volatile jint*)dest,
(jint)compare_value, order);
}
inline jlong Atomic::add(jlong add_value, volatile jlong* dest) {
jlong old = load(dest);
jlong new_value = old + add_value;
while (old != cmpxchg(new_value, dest, old)) {
old = load(dest);
new_value = old + add_value;
}
return old;
}
inline void Atomic::inc(volatile short* dest) {
// Most platforms do not support atomic increment on a 2-byte value. However,
// if the value occupies the most significant 16 bits of an aligned 32-bit
// word, then we can do this with an atomic add of 0x10000 to the 32-bit word.
//
// The least significant parts of this 32-bit word will never be affected, even
// in case of overflow/underflow.
//
// Use the ATOMIC_SHORT_PAIR macro (see macros.hpp) to get the desired alignment.
#ifdef VM_LITTLE_ENDIAN
assert((intx(dest) & 0x03) == 0x02, "wrong alignment");
(void)Atomic::add(0x10000, (volatile int*)(dest-1));
#else
assert((intx(dest) & 0x03) == 0x00, "wrong alignment");
(void)Atomic::add(0x10000, (volatile int*)(dest));
#endif
}
inline void Atomic::dec(volatile short* dest) {
#ifdef VM_LITTLE_ENDIAN
assert((intx(dest) & 0x03) == 0x02, "wrong alignment");
(void)Atomic::add(-0x10000, (volatile int*)(dest-1));
#else
assert((intx(dest) & 0x03) == 0x00, "wrong alignment");
(void)Atomic::add(-0x10000, (volatile int*)(dest));
#endif
}
#endif // SHARE_VM_RUNTIME_ATOMIC_HPP