SpECTRE Documentation Coverage Report
Current view: top level - Domain/FunctionsOfTime - ThreadsafeList.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 20.4 % 54 11
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            0 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : #pragma once
       5              : 
       6              : #include <atomic>
       7              : #include <cstddef>
       8              : #include <iterator>
       9              : #include <limits>
      10              : #include <memory>
      11              : #include <mutex>
      12              : #include <optional>
      13              : 
      14              : /// \cond
      15              : namespace PUP {
      16              : class er;
      17              : }  // namespace PUP
      18              : /// \endcond
      19              : 
      20            0 : namespace domain::FunctionsOfTime::FunctionOfTimeHelpers {
      21              : namespace ThreadsafeList_detail {
      22              : template <typename T>
      23              : struct Interval;
      24              : }  // namespace ThreadsafeList_detail
      25              : 
      26              : /// A list of time intervals that allows safe access to existing
      27              : /// elements even during modification.
      28              : ///
      29              : /// Concurrent modification is not supported.  All non-modifying
      30              : /// operations except for serialization can be safely performed in
      31              : /// parallel with each other and with `insert` and will return a
      32              : /// consistent state.
      33              : template <typename T>
      34            1 : class ThreadsafeList {
      35              :  private:
      36            0 :   using Interval = ThreadsafeList_detail::Interval<T>;
      37              : 
      38              :  public:
      39              :   /// No operations are valid on a default-initialized object except
      40              :   /// for assignment and deserialization.
      41            1 :   ThreadsafeList();
      42            0 :   ThreadsafeList(ThreadsafeList&& other);
      43            0 :   ThreadsafeList(const ThreadsafeList& other);
      44            0 :   ThreadsafeList& operator=(ThreadsafeList&& other);
      45            0 :   ThreadsafeList& operator=(const ThreadsafeList& other);
      46            0 :   ~ThreadsafeList();
      47              : 
      48            0 :   explicit ThreadsafeList(double initial_time);
      49              : 
      50              :   /// Insert data valid over the interval from \p update_time to \p
      51              :   /// expiration_time.
      52              :   ///
      53              :   /// The \p update_time must be the same as the old expiration time.
      54              :   /// It is passed as a check that the calling code is computing the
      55              :   /// other arguments with the correct value.
      56            1 :   void insert(double update_time, T data, double expiration_time);
      57              : 
      58            0 :   struct IntervalInfo {
      59            0 :     double update;
      60            0 :     const T& data;
      61            0 :     double expiration;
      62              : 
      63            0 :     friend bool operator==(const IntervalInfo& a, const IntervalInfo& b) {
      64              :       return a.update == b.update and a.data == b.data and
      65              :              a.expiration == b.expiration;
      66              :     }
      67            0 :     friend bool operator!=(const IntervalInfo& a, const IntervalInfo& b) {
      68              :       return not(a == b);
      69              :     }
      70              :   };
      71              : 
      72              :   /// Obtain the start time, data, and expiration time for the time
      73              :   /// interval containing \p time.
      74              :   ///
      75              :   /// If \p time is at an interval boundary, the earlier interval is
      76              :   /// returned, except for the initial time, which returns the first
      77              :   /// interval.
      78            1 :   IntervalInfo operator()(double time) const;
      79              : 
      80            0 :   double initial_time() const;
      81              : 
      82              :   /// The expiration time of the last data interval.
      83            1 :   double expiration_time() const;
      84              : 
      85              :   /// The first interval boundary after \p time.  If \p time is an
      86              :   /// interval boundary, the one after it is returned.
      87            1 :   double expiration_after(double time) const;
      88              : 
      89              :   /// Remove the oldest data in the list, leaving at least \p length
      90              :   /// entries.
      91              :   ///
      92              :   /// If the list is truncated to zero length, the caller must ensure
      93              :   /// that no concurrent access or modification is performed.  The
      94              :   /// list is guaranteed to be empty afterwards.
      95              :   ///
      96              :   /// Otherwise, it is the caller's responsibility to ensure that,
      97              :   /// during this call, no reader attempts to look up what will become
      98              :   /// the new oldest interval.  Storing references to that interval's
      99              :   /// data is safe, and lookups of that interval are safe if they are
     100              :   /// sequenced after this call.  Concurrent insertions are safe, and
     101              :   /// concurrent access to newer intervals is safe.  Concurrent calls
     102              :   /// to truncate functions and serialization are protected by
     103              :   /// mutexes, but other concurrent calls are lock-free.
     104              :   ///
     105              :   /// If the list is initially shorter than \p length, it is left
     106              :   /// unchanged.  Otherwise, if concurrent insertions occur, the
     107              :   /// amount of removed data is approximate, but will not leave the
     108              :   /// list shorter than the requested value.  In the absence of
     109              :   /// concurrent modifications, the list will be left with exactly the
     110              :   /// requested length.
     111            1 :   void truncate_to_length(size_t length);
     112              : 
     113              :   /// Remove the oldest data in the list, retaining data needed to
     114              :   /// access at least as far back as \p time.
     115              :   ///
     116              :   /// If \p time is before `initial_time()`, the list is not modified.
     117              :   /// If \p time is after `expiration_time()`, the amount of removed
     118              :   /// data is unspecified.
     119              :   ///
     120              :   /// The amount of removed data is approximate.  There may be
     121              :   /// slightly more data remaining after a call than requested.
     122              :   ///
     123              :   /// It is safe to insert and access data concurrently with
     124              :   /// truncation, as long as the accessed times are not before the
     125              :   /// truncation time.  Concurrent calls to truncate functions and
     126              :   /// serialization are protected by mutexes, but other concurrent
     127              :   /// calls are lock-free.
     128            1 :   void truncate_at_time(double time);
     129              : 
     130              :   /// Empty the list.  Equivalent to `truncate_to_length(0)`.  See
     131              :   /// that method for details.
     132            1 :   void clear();
     133              : 
     134            0 :   void pup(PUP::er& p);
     135              : 
     136            0 :   class iterator {
     137              :    public:
     138            0 :     using iterator_category = std::input_iterator_tag;
     139            0 :     using value_type = IntervalInfo;
     140            0 :     using reference = value_type;
     141            0 :     using pointer = std::optional<value_type>;
     142            0 :     using difference_type = std::ptrdiff_t;
     143              : 
     144            0 :     iterator() = default;
     145            0 :     iterator& operator++();
     146            0 :     iterator operator++(int);
     147            0 :     reference operator*() const;
     148            0 :     pointer operator->() const;
     149              : 
     150            0 :     friend bool operator==(const iterator& a, const iterator& b) {
     151              :       return a.interval_ == b.interval_;
     152              :     }
     153            0 :     friend bool operator!=(const iterator& a, const iterator& b) {
     154              :       return not(a == b);
     155              :     }
     156              : 
     157              :    private:
     158            0 :     friend class ThreadsafeList;
     159              : 
     160            0 :     iterator(const ThreadsafeList* parent, const Interval* interval);
     161              : 
     162            0 :     const ThreadsafeList* parent_ = nullptr;
     163            0 :     const Interval* interval_ = nullptr;
     164              :   };
     165              : 
     166              :   /// Iterate over all the intervals in the list, in reverse order
     167              :   /// (i.e., most recent first).
     168              :   /// @{
     169            1 :   iterator begin() const;
     170            1 :   iterator end() const;
     171              :   /// @}
     172              : 
     173              :  private:
     174              :   // interval_after_boundary controls behavior at boundary points.
     175              :   // True -> later interval, false -> earlier.  Initial time always
     176              :   // gives the first interval.
     177            0 :   const Interval& find_interval(double time,
     178              :                                 bool interval_after_boundary) const;
     179              : 
     180            0 :   alignas(64) std::atomic<double> initial_time_ =
     181              :       std::numeric_limits<double>::signaling_NaN();
     182              :   // Pad memory to avoid false-sharing when accessing initial_time_
     183              :   // NOLINTNEXTLINE(modernize-avoid-c-arrays)
     184            0 :   char unused_padding_initial_time_[64 - (sizeof(initial_time_) % 64)] = {};
     185            0 :   std::unique_ptr<Interval> interval_list_{};
     186            0 :   alignas(64) std::atomic<Interval*> most_recent_interval_{};
     187              :   // Pad memory to avoid false-sharing when accessing most_recent_interval_
     188              :   // NOLINTNEXTLINE(modernize-avoid-c-arrays)
     189              :   char
     190            0 :       unused_padding_most_recent_interval_[64 - (sizeof(most_recent_interval_) %
     191              :                                                  64)] = {};
     192              :   // Mutex protecting methods that access the tail of the list, i.e.,
     193              :   // truncation and serialization.  Other methods are assumed to be
     194              :   // called with parameters that will not interact with truncation.
     195            0 :   std::mutex truncation_mutex_{};
     196              : };
     197              : 
     198              : template <typename T>
     199            0 : bool operator==(const ThreadsafeList<T>& a, const ThreadsafeList<T>& b);
     200              : 
     201              : template <typename T>
     202            0 : bool operator!=(const ThreadsafeList<T>& a, const ThreadsafeList<T>& b);
     203              : }  // namespace domain::FunctionsOfTime::FunctionOfTimeHelpers
        

Generated by: LCOV version 2.0-1