aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-10-18 06:47:35 +0200
committerSven Gothel <[email protected]>2020-10-18 06:47:35 +0200
commit8c74d41705984afd02d0674dc45af94afd0609d8 (patch)
tree16fd2c5d55cb6def477ff8f4f90e626bc2804663
parent4f701e24f714f69f075a78f8623119207ad94b09 (diff)
Fix cow_vector's custom mutable write operation, use: get_write_mutex(), copy_store() and set_store(..)
Earlier we abused get_snapshot() for the custom write operation, leading the read-operations using the same snapshot ending in a mutated vector -> BUG. CoW write operations always need to operate on an actual copy of the vector, like push_back() and then replace the store_ref at the end. Hence added copy_store() to return a copy and refined set_store(..) as a move operator requiring std::move(..) to avoid a copy when replacing the store_ref.
-rw-r--r--include/jau/cow_vector.hpp70
1 files changed, 57 insertions, 13 deletions
diff --git a/include/jau/cow_vector.hpp b/include/jau/cow_vector.hpp
index 778f6fb..896a365 100644
--- a/include/jau/cow_vector.hpp
+++ b/include/jau/cow_vector.hpp
@@ -66,7 +66,12 @@ namespace jau {
* Naturally iterators are not supported directly,
* otherwise we would need to copy the store to iterate through.<br>
* However, one can utilize iteration using the shared snapshot
- * of the underlying vector store via jau::cow_vector::get_snapshot().
+ * of the underlying vector store via jau::cow_vector::get_snapshot() for read operations.
+ * </p>
+ * <p>
+ * Custom mutable write operations are also supported via
+ * jau::cow_vector::get_write_mutex(), jau::cow_vector::copy_store() and jau::cow_vector::set_store().<br>
+ * See example in jau::cow_vector::set_store()
* </p>
* See also:
* <pre>
@@ -124,24 +129,64 @@ namespace jau {
/**
* Returns this instances' recursive write mutex, allowing user to
* implement more complex mutable write operations.
+ * <p>
+ * See example in jau::cow_vector::set_store()
+ * </p>
*
- * @see jau::cow_vector::get_snapshot()
+ * @see jau::cow_vector::get_write_mutex()
+ * @see jau::cow_vector::copy_store()
* @see jau::cow_vector::set_store()
*/
std::recursive_mutex & get_write_mutex() { return mtx_write; }
/**
+ * Returns a new shared_ptr copy of the underlying store,
+ * i.e. using a new copy-constructed vectore.
+ * <p>
+ * See example in jau::cow_vector::set_store()
+ * </p>
+ * <p>
+ * This special operation uses a mutex lock and is blocking this instances' write operations only.
+ * </p>
+ * @see jau::cow_vector::get_write_mutex()
+ * @see jau::cow_vector::copy_store()
+ * @see jau::cow_vector::set_store()
+ */
+ std::shared_ptr<std::vector<Value_type>> copy_store() noexcept {
+ const std::lock_guard<std::recursive_mutex> lock(mtx_write);
+ return vector_ref( new vector_t(*store_ref) );
+ }
+
+ /**
* Special case facility allowing the user to replace the current store
* with the given value, potentially acquired via jau::cow_vector::get_snapshot()
* and mutated while holding the jau::cow_vector::get_write_mutex() lock.
+ * <p>
+ * This is a move operation, i.e. the given new_store_ref is invalid on the caller side
+ * after this operation. <br>
+ * User shall pass the store via std::move()
+ * <pre>
+ * cow_vector<std::shared_ptr<Thing>> list;
+ * ...
+ * {
+ * const std::lock_guard<std::recursive_mutex> lock(list.get_write_mutex());
+ * std::shared_ptr<std::vector<std::shared_ptr<Thing>>> snapshot = list.copy_store();
+ * ...
+ * some fancy mutation
+ * ...
+ * list.set_store(std::move(snapshot));
+ * }
+ * </pre>
+ * </p>
+ * @param new_store_ref the user store to be moved here, replacing the current store.
*
- * @param new_store_ref the user store to be set
* @see jau::cow_vector::get_write_mutex()
- * @see jau::cow_vector::get_snapshot()
+ * @see jau::cow_vector::copy_store()
+ * @see jau::cow_vector::set_store()
*/
- void set_store(std::shared_ptr<std::vector<Value_type>> & new_store_ref) noexcept {
+ void set_store(std::shared_ptr<std::vector<Value_type>> && new_store_ref) noexcept {
const std::lock_guard<std::recursive_mutex> lock(mtx_write);
- sc_atomic_critical sync( const_cast<cow_vector *>(this)->sync_atomic );
+ sc_atomic_critical sync(sync_atomic);
store_ref = new_store_ref;
}
@@ -150,15 +195,14 @@ namespace jau {
/**
* Returns the current snapshot of the underlying shared std::vector<T> reference.
* <p>
- * Note that this snapshot will be outdated by the next (concurrent) write operation,
- * i.e. the returned reference are still valid but do not represent the current content
- * of this cow_vector instance.
+ * Note that this snapshot will be outdated by the next (concurrent) write operation.<br>
+ * The returned referenced vector is still valid and not mutated,
+ * but does not represent the current content of this cow_vector instance.
* </p>
* <p>
* This read operation is <i>lock-free</i>.
* </p>
- * @see jau::cow_vector::get_write_mutex()
- * @see jau::cow_vector::set_store()
+ * @see jau::for_each_cow
*/
std::shared_ptr<std::vector<Value_type>> get_snapshot() const noexcept {
sc_atomic_critical sync( const_cast<cow_vector *>(this)->sync_atomic );
@@ -194,7 +238,7 @@ namespace jau {
* </p>
*/
Value_type & operator[](size_t i) noexcept {
- sc_atomic_critical sync( const_cast<cow_vector *>(this)->sync_atomic );
+ sc_atomic_critical sync(sync_atomic);
return (*store_ref)[i];
}
@@ -216,7 +260,7 @@ namespace jau {
* </p>
*/
Value_type & at(size_t i) noexcept {
- sc_atomic_critical sync( const_cast<cow_vector *>(this)->sync_atomic );
+ sc_atomic_critical sync(sync_atomic);
return store_ref->at(i);
}