diff --git a/include/openmc/event.h b/include/openmc/event.h index ff57121bf..93a3314c2 100644 --- a/include/openmc/event.h +++ b/include/openmc/event.h @@ -45,11 +45,10 @@ struct QueueItem{ namespace simulation { -// Event queues. These are allocated pointer variables rather than vectors, -// because they are shared between threads and writing to them must be -// coordinated with atomics. This means that normal vector methods (e.g., -// push_back(), size()) would cause undefined or unintended behavior. Rather, -// adding particles to queues will be done via the enqueue_particle() function. +// Event queues. These use the special SharedArray type, rather than a normal +// vector, as they will be shared between threads and may be appended to at the +// same time. To facilitate this, the SharedArray thread_safe_append() method +// is provided which controls the append operations using atomics. extern SharedArray calculate_fuel_xs_queue; extern SharedArray calculate_nonfuel_xs_queue; extern SharedArray advance_particle_queue; diff --git a/include/openmc/shared_array.h b/include/openmc/shared_array.h index 45addb82a..03cf4a05a 100644 --- a/include/openmc/shared_array.h +++ b/include/openmc/shared_array.h @@ -14,7 +14,13 @@ namespace openmc { //============================================================================== // The SharedArray is an array that is capable of being appended to in an -// thread safe manner by use of atomics. +// thread safe manner by use of atomics. It only provides protection for the +// use cases currently present in OpenMC. Namely, it covers the scenario where +// multiple threads are appending to an array, but no threads are reading from +// or operating on it in any other way at the same time. Multiple threads can +// call the thread_safe_append() function concurrently and store data to the +// object at the index returned from thread_safe_append() safely, but no other +// operations are protected. template class SharedArray { @@ -23,17 +29,18 @@ private: // Data members std::unique_ptr data_; //!< A pointer to hold the data - int64_t size_ {0}; //!< The current size of the shared array. - int64_t capacity_ {0}; //!< The maximum capacity of the shared array. + int64_t size_ {0}; //!< The current size of the SharedArray. + int64_t capacity_ {0}; //!< The maximum capacity of the SharedArray. public: //========================================================================== // Constructors - // Creates an empty shared array + //! Creates an empty SharedArray SharedArray() = default; - // Creates a shared array of desired capacity with zero size. + //! Creates a SharedArray of desired capacity with zero size. + //! \param capacity The desired capacity to allocate for the array SharedArray(int64_t capacity) : capacity_(capacity) { data_ = std::make_unique(capacity); @@ -42,18 +49,25 @@ public: //========================================================================== // Methods and Accessors - // Array accessor + //! Array accessor T& operator[](int64_t i) {return data_[i];} - // Allocates space for the shared array to hold the indicated capacity + //! Allocates space for the SharedArray + //! \param capacity The number of elements to allocate in the array. void reserve(int64_t capacity) { data_ = std::make_unique(capacity); capacity_ = capacity; } - // Increases the size of the SharedArray by one and returns an index to the - // last element of the array. + //! Increases the size of the SharedArray by one and returns an index to the + //! last element of the array. Also tests to enforce that the append + //! operation does not read off the end of the array. In the event that this + //! does happen, the size is set to be equal to the capacity, and -1 is + //! returned. + //! \return The last index in the array, which is safe to write to. In the + //! event that this index would be greater than what was allocated for the + //! SharedArray, -1 is returned. int64_t thread_safe_append() { // Atomically capture the index we want to write to @@ -71,8 +85,8 @@ public: return idx; } - // Free's any space that was allocated to the shared array and resets - // size and capacity to zero. + //! Frees any space that was allocated to the SharedArray and resets + //! size and capacity to zero. void clear() { data_.reset(); @@ -80,17 +94,22 @@ public: capacity_ = 0; } - // Size getter + //! Size getter function + //! \return The current size of the SharedArray int64_t size() {return size_;} - // Sets the size of the shared array. This is useful in cases where - // we want to write to the shared array in a non-thread safe manner. - int64_t resize(int64_t size) {size_ = size;} + //! Sets the size of the SharedArray. This is useful in cases where + //! we want to write to the SharedArray in a non-thread safe manner + //! and need to update the internal size of the array after doing so. + //! \param size The new size for the array + void resize(int64_t size) {size_ = size;} - // Capacity getter + //! Capacity getter functon + //! \return The maximum allocated capacity for the SharedArray int64_t capacity() {return capacity_;} - // Returns a pointer to the data allocation + //! This function exposes the pointer that the SharedArray is protecting. + //! \return The pointer to the data allocation T* data() {return data_.get();} }; diff --git a/src/event.cpp b/src/event.cpp index ed61349a9..50e64f46a 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -3,7 +3,6 @@ #include "openmc/simulation.h" #include "openmc/timer.h" - namespace openmc { //============================================================================== @@ -89,7 +88,7 @@ void process_calculate_xs_events(SharedArray& queue) // to C++17, std::sort is a serial only operation, which in this case // makes it too slow to be practical for most test problems. // - // std::sort(std::execution::par_unseq, queue.data(), queue.data() + n); + // std::sort(std::execution::par_unseq, queue.data(), queue.data() + queue.size()); int64_t offset = simulation::advance_particle_queue.size();;