/* Copyright 2012 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
/**
* Implementation of the AtomicIntrinsics::* operations for IA-32 and AMD64 systems using a
* GCC-compatible compiler toolchain.
*/
#pragma once
#include
namespace mongo {
/**
* Instantiation of AtomicIntrinsics<> for all word types T where sizeof <= sizeof(void *).
*
* On 32-bit systems, this handles 8-, 16- and 32-bit word types. On 64-bit systems,
* it handles 8-, 16, 32- and 64-bit types.
*/
template
class AtomicIntrinsics {
public:
static T compareAndSwap(volatile T* dest, T expected, T newValue) {
T result;
asm volatile ("lock cmpxchg %[src], %[dest]"
: [dest] "+m" (*dest),
"=a" (result)
: [src] "r" (newValue),
"a" (expected)
: "memory", "cc");
return result;
}
static T swap(volatile T* dest, T newValue) {
T result = newValue;
// No need for "lock" prefix on "xchg".
asm volatile ("xchg %[r], %[dest]"
: [dest] "+m" (*dest),
[r] "+r" (result)
:
: "memory");
return result;
}
static T load(volatile const T* value) {
asm volatile ("mfence" ::: "memory");
T result = *value;
asm volatile ("mfence" ::: "memory");
return result;
}
static T loadRelaxed(volatile const T* value) {
return *value;
}
static void store(volatile T* dest, T newValue) {
asm volatile ("mfence" ::: "memory");
*dest = newValue;
asm volatile ("mfence" ::: "memory");
}
static T fetchAndAdd(volatile T* dest, T increment) {
T result = increment;
asm volatile ("lock xadd %[src], %[dest]"
: [dest] "+m" (*dest),
[src] "+r" (result)
:
: "memory", "cc");
return result;
}
private:
AtomicIntrinsics();
~AtomicIntrinsics();
};
/**
* Instantiation of AtomicIntrinsics where sizeof exceeds sizeof(void*).
*
* On 32-bit systems, this handles the 64-bit word type. Not used on 64-bit systems.
*
* Note that the implementations of swap, store and fetchAndAdd spin until they succeed. This
* implementation is thread-safe, but may have poor performance in high-contention environments.
* However, no superior solution exists for IA-32 (32-bit x86) systems.
*/
template
class AtomicIntrinsics::type> {
public:
static T compareAndSwap(volatile T* dest, T expected, T newValue) {
T result = expected;
asm volatile ("push %%eax\n"
"push %%ebx\n"
"push %%ecx\n"
"push %%edx\n"
"mov (%%edx), %%ebx\n"
"mov 4(%%edx), %%ecx\n"
"mov (%%edi), %%eax\n"
"mov 4(%%edi), %%edx\n"
"lock cmpxchg8b (%%esi)\n"
"mov %%eax, (%%edi)\n"
"mov %%edx, 4(%%edi)\n"
"pop %%edx\n"
"pop %%ecx\n"
"pop %%ebx\n"
"pop %%eax\n"
:
: "S" (dest),
"D" (&result),
"d" (&newValue)
: "memory", "cc");
return result;
}
static T swap(volatile T* dest, T newValue) {
T expected;
T actual;
do {
expected = *dest;
actual = compareAndSwap(dest, expected, newValue);
} while (actual != expected);
return actual;
}
static T load(volatile const T* value) {
return compareAndSwap(const_cast(value), T(0), T(0));
}
static void store(volatile T* dest, T newValue) {
swap(dest, newValue);
}
static T fetchAndAdd(volatile T* dest, T increment) {
T expected;
T actual;
do {
expected = load(dest);
actual = compareAndSwap(dest, expected, expected + increment);
} while (actual != expected);
return actual;
}
private:
AtomicIntrinsics();
~AtomicIntrinsics();
};
} // namespace mongo