Skip to content

Commit

Permalink
Add AArch64 support to persistent<T>, and a unit test.
Browse files Browse the repository at this point in the history
  • Loading branch information
ned14 committed Apr 6, 2018
1 parent 83c0b0b commit 7f4dfb4
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .docs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
list(FIND CMAKE_MODULE_PATH "quickcpplib/cmake" quickcpplib_idx)
if(${quickcpplib_idx} EQUAL -1)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmakelib")
endif()
include(QuickCppLibUtils)

Expand Down
1 change: 1 addition & 0 deletions cmake/headers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ set(quickcpplib_HEADERS
"include/optional.hpp"
"include/optional/optional.hpp"
"include/packed_backtrace.hpp"
"include/persistent.hpp"
"include/revision.hpp"
"include/ringbuffer_log.hpp"
"include/scoped_undo.hpp"
Expand Down
1 change: 1 addition & 0 deletions cmake/tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ set(quickcpplib_TESTS
"test/test_guard2.hpp"
"test/open_hash_index.cpp"
"test/packed_backtrace.cpp"
"test/persistent.cpp"
"test/ringbuffer_log.cpp"
"test/spinlock_tribool.cpp"
"test/type_traits.cpp"
Expand Down
46 changes: 31 additions & 15 deletions include/persistent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,20 @@ namespace persistence
/*! \brief The kinds of cache line flushing which can be performed. */
enum class memory_flush
{
memory_flush_none, //!< No memory flushing.
memory_flush_retain, //!< Flush modified cache line to memory, but retain as unmodified in cache.
memory_flush_evict //!< Flush modified cache line to memory, and evict completely from all caches.
};
//! No memory flushing.
constexpr memory_flush memory_flush_none = memory_flush::memory_flush_none;
//! Flush modified cache line to memory, but retain as unmodified in cache.
constexpr memory_flush memory_flush_retain = memory_flush::memory_flush_retain;
//! Flush modified cache line to memory, and evict completely from all caches.
constexpr memory_flush memory_flush_evict = memory_flush::memory_flush_evict;

namespace detail
{
using flush_impl_type = bool (*)(const void *addr, memory_flush kind);
using flush_impl_type = memory_flush (*)(const void *addr, memory_flush kind);
inline QUICKCPPLIB_NOINLINE flush_impl_type make_flush_impl() noexcept
{
#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
Expand All @@ -65,34 +68,46 @@ namespace persistence
__cpuidex(nBuff, 0x7, 0x0);
if(nBuff[1] & (1 << 24)) // has CLWB instruction
{
return [](const void *addr, memory_flush kind) -> bool {
return [](const void *addr, memory_flush kind) -> memory_flush {
if(kind == memory_flush_retain)
{
_mm_clwb(addr);
_mm_sfence();
return true;
return memory_flush_retain;
}
_mm_clflushopt(addr);
_mm_sfence();
return true;
return memory_flush_evict;
};
}
if(nBuff[1] & (1 << 23)) // has CLFLUSHOPT instruction
{
return [](const void *addr, memory_flush kind) -> bool {
return [](const void *addr, memory_flush /*unused*/) -> memory_flush {
_mm_clflushopt(addr);
_mm_sfence();
return true;
return memory_flush_evict;
};
}
else
{
// Use CLFLUSH instruction
return [](const void *addr, memory_flush kind) -> bool {
return [](const void *addr, memory_flush /*unused*/) -> memory_flush {
_mm_clflush(addr);
return true;
return memory_flush_evict;
};
}
#elif defined(__aarch64__)
return [](const void *addr, memory_flush kind) -> memory_flush {
if(kind == memory_flush_retain)
{
__asm__ __volatile__("dc cvac, %0" : : "r"(addr) : "memory");
__asm__ __volatile__("dmb ish" : : : "memory");
return memory_flush_retain;
}
__asm__ __volatile__("dc civac, %0" : : "r"(addr) : "memory");
__asm__ __volatile__("dmb ish" : : : "memory");
return memory_flush_evict;
};
#else
#error Unsupported platform
#endif
Expand All @@ -114,20 +129,21 @@ namespace persistence
dirty cache lines in the CPU's caches to main memory/mapped remote memory. This
class extends `std::atomic<T>` with a `.flush()` member function which calls
architecture-specific opcodes to cause the CPU to immediately write the dirty cache
line to main memory, marking on completion the cache line as unmodified.
line specified to main memory, marking on completion the cache line as unmodified.
`.store()`, and all the other member functions which can modify state, will
call `.flush(memory_flush_retain)` on your behalf if the `memory_order` contains release or sequential
consistent semantics.
Note that calling `.flush()` repeatedly on the same cache line will likely produce
poor performance. You are advised to use non-release semantics to modify the cache
line, then your final modification ought to release. This will release the whole
cache line at once, avoiding constant read-modify-write cycles (unless the latter
is exactly what you need of course).
line, then your final modification to that cache line ought to release. This will
release the whole cache line at once, avoiding constant read-modify-write cycles
(unless the latter is exactly what you need of course).
\warning On older Intel CPUs, due to lack of hardware support, we always execute
`memory_flush_evict` even if asked for `memory_flush_retain`. This can produce
some very poor performance.
some very poor performance. Check the value returned by `.flush()` to see what
kind of flush was actually performed.
*/
template <class T> class persistent : protected std::atomic<T>
{
Expand Down Expand Up @@ -237,10 +253,10 @@ namespace persistence

/*! \brief Flush the cache line containing this object instance to main memory,
optionally evicting it from all caches entirely.
\return True if the cache line was successfully flushed.
\return The kind of flush actually performed.
\param kind What kind of flush to do. See `memory_flush`.
*/
bool flush(memory_flush kind = memory_flush_retain) const noexcept { return detail::flush_impl()(this, kind); }
memory_flush flush(memory_flush kind = memory_flush_retain) const noexcept { return detail::flush_impl()(this, kind); }
};
}

Expand Down
6 changes: 3 additions & 3 deletions include/revision.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time
#define QUICKCPPLIB_PREVIOUS_COMMIT_REF ee857b538ad751186d5d6b477a7784c15a0b071d
#define QUICKCPPLIB_PREVIOUS_COMMIT_DATE "2018-03-28 08:43:17 +00:00"
#define QUICKCPPLIB_PREVIOUS_COMMIT_UNIQUE ee857b53
#define QUICKCPPLIB_PREVIOUS_COMMIT_REF 83c0b0ba559bd748393b9d534dff0dd421e66740
#define QUICKCPPLIB_PREVIOUS_COMMIT_DATE "2018-04-05 20:09:59 +00:00"
#define QUICKCPPLIB_PREVIOUS_COMMIT_UNIQUE 83c0b0ba
36 changes: 36 additions & 0 deletions test/persistent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* Test persistent
(C) 2018 Niall Douglas <http://www.nedproductions.biz/> (4 commits)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License in the accompanying file
Licence.txt or at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file Licence.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
*/

#include "../include/boost/test/unit_test.hpp"
#include "../include/persistent.hpp"

BOOST_AUTO_TEST_SUITE(persistence)

BOOST_AUTO_TEST_CASE(persistence / works, "Tests that persistent<T> works as advertised")
{
QUICKCPPLIB_NAMESPACE::persistence::persistent<int> v(5);
v.store(6);
BOOST_CHECK(v == 6);
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 7f4dfb4

Please sign in to comment.