/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #ifdef WITH_TBB # include #endif #include #include #include "BLI_map.hh" #include "BLI_utility_mixins.hh" namespace blender::threading { #ifndef WITH_TBB namespace enumerable_thread_specific_utils { inline std::atomic next_id = 0; inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed); } // namespace enumerable_thread_specific_utils #endif /** * This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we * want to be able to build without tbb. * * More features of the tbb version can be wrapped when they are used. */ template class EnumerableThreadSpecific : NonCopyable, NonMovable { #ifdef WITH_TBB private: tbb::enumerable_thread_specific values_; public: using iterator = typename tbb::enumerable_thread_specific::iterator; EnumerableThreadSpecific() = default; template EnumerableThreadSpecific(F initializer) : values_(std::move(initializer)) { } T &local() { return values_.local(); } iterator begin() { return values_.begin(); } iterator end() { return values_.end(); } #else /* WITH_TBB */ private: std::mutex mutex_; /* Maps thread ids to their corresponding values. The values are not embedded in the map, so that * their addresses do not change when the map grows. */ Map> values_; Vector> owned_values_; std::function initializer_; public: using iterator = typename Map>::MutableValueIterator; EnumerableThreadSpecific() : initializer_([](void *buffer) { new (buffer) T(); }) { } template EnumerableThreadSpecific(F initializer) : initializer_([=](void *buffer) { new (buffer) T(initializer()); }) { } T &local() { const int thread_id = enumerable_thread_specific_utils::thread_id; std::lock_guard lock{mutex_}; return values_.lookup_or_add_cb(thread_id, [&]() { T *value = (T *)::operator new(sizeof(T)); initializer_(value); owned_values_.append(std::unique_ptr{value}); return std::reference_wrapper{*value}; }); } iterator begin() { return values_.values().begin(); } iterator end() { return values_.values().end(); } #endif /* WITH_TBB */ }; } // namespace blender::threading