Program Listing for File safe_vector.hh

Return to documentation for file (include/reactor-cpp/safe_vector.hh)

/*
 * Copyright (C) 2022 TU Dresden
 * All rights reserved.
 *
 * Authors:
 *   Christian Menard
 */

#ifndef REACTOR_CPP_SAFE_VECTOR_HH
#define REACTOR_CPP_SAFE_VECTOR_HH

#include <atomic>
#include <cstddef>
#include <mutex>
#include <shared_mutex>
#include <vector>

namespace reactor {

template <class T> class SafeVector {
private:
  static constexpr std::size_t size_increment_{100};
  std::atomic<std::size_t> write_pos_{0};
  std::size_t vector_size_{size_increment_};
  std::vector<T> vector_{size_increment_};
  std::shared_mutex mutex_;

public:
  void push_back(const T& value) {
    auto pos = write_pos_.fetch_add(1, std::memory_order_release);

    {
      std::shared_lock<std::shared_mutex> shared_lock(mutex_);
      if (pos < vector_size_) {
        vector_[pos] = value;
      } else {
        shared_lock.unlock();
        {
          std::unique_lock<std::shared_mutex> unique_lock(mutex_);
          while (pos >= vector_size_) {
            vector_size_ += size_increment_;
            vector_.resize(vector_size_);
          }
          vector_[pos] = value;
        }
      }
    }
  }

  // careful! Using the iterators is only safe if no more elements are added to the vector!
  auto begin() -> auto { return vector_.begin(); }
  auto end() -> auto { return vector_.begin() + write_pos_.load(std::memory_order_acquire); }

  auto size() -> std::size_t { return write_pos_.load(std::memory_order_acquire); }
  auto empty() -> bool { return size() == 0; }

  void clear() { write_pos_.store(0, std::memory_order_release); }
};

} // namespace reactor

#endif // REACTOR_CPP_SAFE_VECTOR_HH