Line data Source code
1 : /* 2 : * Copyright (c) 2016 Sebastian Weber, Henri Menke. All rights reserved. 3 : * 4 : * This file is part of the pairinteraction library. 5 : * 6 : * The pairinteraction library is free software: you can redistribute it and/or modify 7 : * it under the terms of the GNU Lesser General Public License as published by 8 : * the Free Software Foundation, either version 3 of the License, or 9 : * (at your option) any later version. 10 : * 11 : * The pairinteraction library is distributed in the hope that it will be useful, 12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 : * GNU Lesser General Public License for more details. 15 : * 16 : * You should have received a copy of the GNU Lesser General Public License 17 : * along with the pairinteraction library. If not, see <http://www.gnu.org/licenses/>. 18 : */ 19 : 20 : #ifndef CACHE_H 21 : #define CACHE_H 22 : 23 : #include <mutex> 24 : #include <optional> 25 : #include <stdexcept> 26 : #include <unordered_map> 27 : 28 : /** \brief Generic cache object 29 : * 30 : * This generic cache object strives to provide a thread-safe cache 31 : * based on a `std::unordered_map` protected from smashing by a mutex. 32 : * 33 : * There are only few public methods, which are save, restore, and 34 : * clear. Iteration over the cache was left out intentionally as the 35 : * user should not search the cache manually for entries. 36 : */ 37 : template <typename Key, typename Element, typename Hash = std::hash<Key>> 38 : class Cache { 39 : typedef std::unordered_map<Key, Element, Hash> cache_t; 40 : 41 : cache_t cache; 42 : std::mutex cache_mutex; 43 : 44 : public: 45 : /** \brief Save something in the cache 46 : * 47 : * To prevent race-conditions this function will throw if the 48 : * element is already in the cache. 49 : * 50 : * The usage is extremely straight-forward 51 : * \code 52 : * cache.save(key,element); 53 : * \endcode 54 : * 55 : * \param key Key 56 : * \param e Element 57 : * \throws std::runtime_error if the element is already in the cache 58 : */ 59 1449 : void save(Key const &key, Element const &e) { 60 2898 : std::lock_guard<std::mutex> lock(cache_mutex); 61 1449 : if (!cache.emplace(std::make_pair(key, e)).second) { 62 1 : throw std::runtime_error("Cache smashing detected!"); 63 : } 64 1448 : } 65 : 66 : /** \brief Restore something from the cache 67 : * 68 : * When restoring from the cache, the situation might occur that 69 : * an element is not yet available in the cache. We do not want 70 : * to throw an exception in that case because that case might be 71 : * very frequent when building the cache. Therefore restoring 72 : * returns an optional value, i.e. it might not void. Hence the 73 : * access pattern is also a little different. 74 : * 75 : * \code 76 : * if (auto optional_element = cache.restore(key,element)) { 77 : * // Cache hit! Restore cache... 78 : * element = optional_element.get(); 79 : * } else { 80 : * // Cache miss :-( 81 : * } 82 : * \endcode 83 : * 84 : * \param key Key 85 : * \returns Optional element 86 : */ 87 166125 : std::optional<Element> restore(Key const &key) { 88 332250 : std::lock_guard<std::mutex> lock(cache_mutex); 89 166125 : auto cached_it = cache.find(key); 90 166125 : if (cached_it != cache.end()) { 91 164685 : return cached_it->second; 92 : } 93 1440 : return std::nullopt; 94 : } 95 : 96 : /** \brief Clear the cache 97 : * 98 : * Delete all elements in the cache 99 : */ 100 1 : void clear() { cache.clear(); } 101 : }; 102 : 103 : #endif // CACHE_H