Skip to content

Commit 0f15b48

Browse files
committed
Use thread-safe lock-free assignment in PackedVector::set_value
Other options: * use builtin functions boolean CompareAndSwapPointer(volatile * void * ptr, void * new_value, void * old_value) { if defined(_MSC_VER) if (InterlockedCompareExchange(ptr, new_value, old_value) == old_value) return false; else return true; elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100 return __sync_bool_compare_and_swap(ptr, old_value, new_value); else error No implementation endif } * use Boost.Atomic WordT local_lower_word = lower_word, new_lower_word; do { new_lower_word = set_lower_value<WordT, T>(local_lower_word, lower_mask[internal_index.element], lower_offset[internal_index.element], value); } while (!boost::atomics::detail::operations<sizeof(WordT), false>::compare_exchange_weak( lower_word, local_lower_word, new_lower_word, boost::memory_order_release, boost::memory_order_relaxed));
1 parent 383640c commit 0f15b48

File tree

1 file changed

+29
-10
lines changed

1 file changed

+29
-10
lines changed

include/util/packed_vector.hpp

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <boost/iterator/iterator_facade.hpp>
1212
#include <boost/iterator/reverse_iterator.hpp>
13+
#include <tbb/atomic.h>
1314

1415
#include <array>
1516
#include <cmath>
@@ -348,7 +349,7 @@ template <typename T, std::size_t Bits, storage::Ownership Ownership> class Pack
348349
fill(initial_value);
349350
}
350351

351-
PackedVector(util::ViewOrVector<std::uint64_t, Ownership> vec_, std::size_t num_elements)
352+
PackedVector(util::ViewOrVector<WordT, Ownership> vec_, std::size_t num_elements)
352353
: vec(std::move(vec_)), num_elements(num_elements)
353354
{
354355
initialize();
@@ -497,20 +498,38 @@ template <typename T, std::size_t Bits, storage::Ownership Ownership> class Pack
497498

498499
inline void set_value(const InternalIndex internal_index, const T value)
499500
{
501+
// ⚠ The method uses CAS spinlocks to prevent data races in parallel calls
502+
// Parallel read and writes are not allowed
503+
500504
auto &lower_word = vec[internal_index.lower_word];
501505
auto &upper_word = vec[internal_index.lower_word + 1];
502506

503-
lower_word = set_lower_value<WordT, T>(lower_word,
504-
lower_mask[internal_index.element],
505-
lower_offset[internal_index.element],
506-
value);
507-
upper_word = set_upper_value<WordT, T>(upper_word,
508-
upper_mask[internal_index.element],
509-
upper_offset[internal_index.element],
510-
value);
507+
// Lock-free update of the lower word
508+
WordT local_lower_word, new_lower_word;
509+
do
510+
{
511+
local_lower_word = lower_word;
512+
new_lower_word = set_lower_value<WordT, T>(local_lower_word,
513+
lower_mask[internal_index.element],
514+
lower_offset[internal_index.element],
515+
value);
516+
} while (tbb::internal::as_atomic(lower_word)
517+
.compare_and_swap(new_lower_word, local_lower_word) != local_lower_word);
518+
519+
// Lock-free update of the upper word
520+
WordT local_upper_word, new_upper_word;
521+
do
522+
{
523+
local_upper_word = upper_word;
524+
new_upper_word = set_upper_value<WordT, T>(local_upper_word,
525+
upper_mask[internal_index.element],
526+
upper_offset[internal_index.element],
527+
value);
528+
} while (tbb::internal::as_atomic(upper_word)
529+
.compare_and_swap(new_upper_word, local_upper_word) != local_upper_word);
511530
}
512531

513-
util::ViewOrVector<std::uint64_t, Ownership> vec;
532+
util::ViewOrVector<WordT, Ownership> vec;
514533
std::uint64_t num_elements = 0;
515534
};
516535
}

0 commit comments

Comments
 (0)