├── .gitignore ├── LICENSE.txt ├── Makefile ├── README.md ├── concurrent ├── bounded_queue.h ├── cache │ ├── lookahead_cache.hpp │ ├── priority_cache.hpp │ └── priority_cache_details.hpp ├── common.hpp ├── details │ ├── call_type_traits.hpp │ └── queue_base.hpp ├── notifier.hpp ├── queue.hpp ├── queue_adaptor.hpp └── slot.hpp ├── examples ├── BoundedQueueSingleWorker.cpp ├── ConcurrentSlot.cpp ├── LookAheadCache.cpp ├── QueueManyWorkers.cpp └── QueueSingleWorker.cpp └── tests ├── benchmark ├── cache_benchmark_tests.cpp └── data │ ├── gch.txt │ └── nro.txt ├── cache_tests.cpp ├── concurrent_queue_tests.cpp └── concurrent_slot_tests.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | dist 3 | .cproject 4 | .project 5 | BoundedQueueSingleWorker 6 | ConcurrentSlot 7 | LookAheadCache 8 | QueueManyWorkers 9 | QueueSingleWorker 10 | test 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | (The MIT Licence) 2 | 3 | Copyright (c) 2011-2012 Guillaume Chatelet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | CFLAGS=-I. -Wall -fmessage-length=0 -std=c++11 3 | CFLAGS+=-D_GLIBCXX_USE_NANOSLEEP # g++ std::this_thread::sleep_for in benchmark 4 | CFLAGS+=-O0 -g 5 | LDFLAGS=-lpthread 6 | 7 | .PHONY: clean examples 8 | 9 | all:examples test 10 | 11 | EXAMPLES=BoundedQueueSingleWorker ConcurrentSlot LookAheadCache QueueManyWorkers QueueSingleWorker 12 | 13 | examples: $(EXAMPLES) 14 | 15 | BoundedQueueSingleWorker: examples/BoundedQueueSingleWorker.cpp 16 | $(CC) $(CFLAGS) $(LDFLAGS) -o BoundedQueueSingleWorker $^ 17 | 18 | ConcurrentSlot: examples/ConcurrentSlot.cpp 19 | $(CC) $(CFLAGS) $(LDFLAGS) -o ConcurrentSlot $^ 20 | 21 | LookAheadCache: examples/LookAheadCache.cpp 22 | $(CC) $(CFLAGS) $(LDFLAGS) -o LookAheadCache $^ 23 | 24 | QueueManyWorkers: examples/QueueManyWorkers.cpp 25 | $(CC) $(CFLAGS) $(LDFLAGS) -o QueueManyWorkers $^ 26 | 27 | QueueSingleWorker: examples/QueueSingleWorker.cpp 28 | $(CC) $(CFLAGS) $(LDFLAGS) -o QueueSingleWorker $^ 29 | 30 | test:tests/*.cpp tests/benchmark/*.cpp 31 | $(CC) $(CFLAGS) $(LDFLAGS) -lgtest -lgtest_main -o test $^ 32 | 33 | clean: 34 | rm -f $(EXAMPLES) test 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Concurrent utils 2 | ================ 3 | 4 | A set of C++11 header-only data structures to handle multithreaded tasks. 5 | 6 | This code is highly inspired by [Anthony Williams](https://twitter.com/a_williams) [C++ Concurrency in Action](http://www.manning.com/williams/) 7 | 8 | - - - 9 | 10 | Provided facilities 11 | ------------------- 12 | 13 | Have a look at the _example_ folder for some code. But here is a quick tour : 14 | 15 | ### concurrent::notifier 16 | * A simple object allowing to wait for an acknowledgement. 17 | 18 | ### concurrent::slot 19 | * An object holder with notification capabilities. 20 | 21 | ### concurrent::queue 22 | * An unlimited concurrent queue for passing messages between threads. 23 | 24 | ### concurrent::bounded_queue 25 | * A bounded concurrent queue for passing messages between threads. 26 | 27 | ### concurrent::cache::lookahead_cache 28 | * A cache that fills itself automagically with the help of one or more worker threads. 29 | This component is currently in use within [Duke](https://github.com/mikrosimage/duke) to enable image preloading but could be used whenever you need to hide latencies (i.e. I/O over disk or network). 30 | 31 | - - - 32 | 33 | Use 34 | --- 35 | 36 | * include this folder 37 | 38 | - - - 39 | 40 | Quick start 41 | ----------- 42 | 43 | ### Tests and examples 44 | > make examples 45 | 46 | Provides a few examples of how to use the library. 47 | 48 | > make test 49 | 50 | You will need [gtest](http://code.google.com/p/googletest/) to build the test suite. 51 | 52 | - - - 53 | 54 | Tested compilers 55 | ---------------- 56 | 57 | * GCC 4.7.2 on Gentoo Linux 58 | * GCC 4.6.3 on Gentoo Linux 59 | ** use -std=c++0x instead of -std=c++11 60 | * ~~Clang 3.2~~ runs into this [GCC issue](http://clang-developers.42468.n3.nabble.com/Problem-with-gnu-libc-4-7-s-chrono-in-Clang-3-2-td4029343.html) 61 | 62 | - - - 63 | 64 | Licence 65 | --------- 66 | MIT license 67 | 68 | Copyright 69 | --------- 70 | 71 | Copyright (C) 2011-2013 Guillaume Chatelet. See LICENSE.txt for further details. -------------------------------------------------------------------------------- /concurrent/bounded_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef BOUNDEDQUEUE_H_ 2 | #define BOUNDEDQUEUE_H_ 3 | 4 | #include "details/queue_base.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace concurrent { 11 | 12 | /** 13 | * Bounded concurrent queue for safe access from several threads. 14 | * 15 | * Please note that a single Mutex is used for synchronization of front() and back() 16 | * thus leading to contention if consumer and producer are accessing the container at the same time. 17 | */ 18 | template > 19 | struct bounded_queue : public details::queue_base< bounded_queue, Container > { 20 | typedef Container container_type; 21 | typedef typename container_type::value_type value_type; 22 | typedef typename container_type::size_type size_type; 23 | 24 | explicit bounded_queue(size_type capacity) : m_unread(0), m_container(capacity) { 25 | } 26 | private: 27 | typedef bounded_queue ME; 28 | 29 | template 30 | friend struct details::queue_base; 31 | 32 | inline void _clear() { 33 | m_container.clear(); 34 | m_unread = 0; 35 | } 36 | inline void _push(value_type value) { 37 | m_container.push_front(value); 38 | ++m_unread; 39 | } 40 | inline value_type _pop() { 41 | return m_container[--m_unread]; 42 | } 43 | inline void wait_not_empty(std::unique_lock &lock) { 44 | m_not_empty.wait(lock, std::bind(&ME::is_not_empty, this)); 45 | } 46 | inline void wait_not_full(std::unique_lock &lock) { 47 | m_not_full.wait(lock, std::bind(&ME::is_not_full, this)); 48 | } 49 | inline bool is_not_empty() const { 50 | return m_unread > 0; 51 | } 52 | inline bool is_not_full() const { 53 | return m_unread < m_container.size(); 54 | } 55 | inline void notify_not_full() { 56 | m_not_full.notify_one(); 57 | } 58 | inline void notify_not_empty() { 59 | m_not_empty.notify_one(); 60 | } 61 | private: 62 | size_type m_unread; 63 | container_type m_container; 64 | std::condition_variable m_not_empty; 65 | std::condition_variable m_not_full; 66 | }; 67 | 68 | } /* namespace concurrent */ 69 | #endif /* BOUNDEDQUEUE_H_ */ 70 | -------------------------------------------------------------------------------- /concurrent/cache/lookahead_cache.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LookAheadCache.hpp 3 | * 4 | * Created on: 22 nov. 2011 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef LOOK_AHEAD_CACHE_HPP_ 9 | #define LOOK_AHEAD_CACHE_HPP_ 10 | 11 | #include "priority_cache_details.hpp" 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace concurrent { 21 | 22 | namespace cache { 23 | 24 | /** 25 | * Synchronized look ahead cache 26 | * Basic usage : 27 | * - create cache 28 | * - create one or several worker thread who will 'pop' units of work 29 | * - for each unit the result of the process must be put back in the cache with 'push' 30 | * - add an iterator to process whenever you want 31 | * 32 | * Cache will ensure every thread will stop by firing a 'terminated' exception 33 | * upon 'pop' when terminate is set to true 34 | */ 35 | template 36 | struct lookahead_cache { 37 | typedef ID_TYPE id_type; 38 | typedef METRIC_TYPE metric_type; 39 | typedef DATA_TYPE data_type; 40 | typedef WORK_UNIT_RANGE WorkUnitItr; 41 | 42 | #if __cplusplus >= 201103L 43 | static_assert(std::is_default_constructible::value, "WorkUnitItr should be default constructible"); 44 | #endif 45 | 46 | lookahead_cache(const metric_type cache_limit) : 47 | m_SharedCache(cache_limit) { 48 | } 49 | 50 | // Cache functions 51 | inline bool get(const id_type &id, data_type &data) const { 52 | std::lock_guard lock(m_CacheMutex); 53 | return m_SharedCache.get(id, data); 54 | } 55 | 56 | inline metric_type dumpKeys(std::vector &allKeys) const { 57 | std::lock_guard lock(m_CacheMutex); 58 | m_SharedCache.dumpKeys(allKeys); 59 | return m_SharedCache.weight(); 60 | } 61 | 62 | void process(const WorkUnitItr &job) { 63 | m_PendingJob.set(job); 64 | } 65 | 66 | inline void setMaxWeight(const metric_type size) { 67 | std::lock_guard lock(m_CacheMutex); 68 | m_SharedCache.setMaxWeight(size); 69 | } 70 | 71 | void terminate(bool value = true) { 72 | m_PendingJob.terminate(value); 73 | } 74 | 75 | // worker functions 76 | void pop(id_type &unit) { 77 | std::lock_guard lock(m_WorkerMutex); 78 | do { 79 | unit = nextWorkUnit(); 80 | D_( std::cout << "next unit is : " << unit.filename << std::endl); 81 | std::lock_guard lock(m_CacheMutex); 82 | switch (m_SharedCache.update(unit)) { 83 | case FULL: 84 | D_( std::cout << "cache is full, emptying current job" << std::endl); 85 | m_SharedWorkUnitItr.clear(); 86 | break; 87 | case NOT_NEEDED: 88 | D_( std::cout << "unit updated, checking another one" << std::endl); 89 | break; 90 | case NEEDED: 91 | D_( std::cout << "serving " << unit << std::endl); 92 | return; 93 | } 94 | } while (true); 95 | } 96 | 97 | inline bool push(const id_type &id, const metric_type weight, const data_type &data) { 98 | std::lock_guard lock(m_CacheMutex); 99 | return m_SharedCache.put(id, weight, data); 100 | } 101 | 102 | private: 103 | inline id_type nextWorkUnit() { 104 | if (updateJob()) { 105 | std::lock_guard lock(m_CacheMutex); 106 | m_SharedCache.discardPending(); 107 | } 108 | return m_SharedWorkUnitItr.next(); 109 | } 110 | 111 | inline bool updateJob() { 112 | bool updated = m_PendingJob.tryGet(m_SharedWorkUnitItr); 113 | while (m_SharedWorkUnitItr.empty()) { 114 | m_PendingJob.waitGet(m_SharedWorkUnitItr); 115 | updated = true; 116 | } 117 | assert(!m_SharedWorkUnitItr.empty()); 118 | return updated; 119 | } 120 | 121 | mutable std::mutex m_WorkerMutex; 122 | mutable std::mutex m_CacheMutex; 123 | priority_cache_details m_SharedCache; 124 | slot m_PendingJob; 125 | WorkUnitItr m_SharedWorkUnitItr; 126 | }; 127 | 128 | } // namespace cache 129 | 130 | } // namespace concurrent 131 | 132 | #endif /* LOOK_AHEAD_CACHE_HPP_ */ 133 | -------------------------------------------------------------------------------- /concurrent/cache/priority_cache.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * priority_cache.hpp 3 | * 4 | * Created on: 22 nov. 2011 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef PRIORITY_CACHE_HPP_ 9 | #define PRIORITY_CACHE_HPP_ 10 | 11 | #include "priority_cache_details.hpp" 12 | 13 | #include 14 | #include 15 | 16 | namespace concurrent { 17 | 18 | namespace cache { 19 | 20 | /** 21 | * Unsynchronized cache 22 | * Basic usage : 23 | * - create cache 24 | * - add an iterator to process 25 | * - loop on pop until false, for each unit process and push to cache 26 | */ 27 | template 28 | struct priority_cache { 29 | typedef ID_TYPE id_type; 30 | typedef METRIC_TYPE metric_type; 31 | typedef DATA_TYPE data_type; 32 | typedef WORK_UNIT_RANGE WorkUnitItr; 33 | 34 | #if __cplusplus >= 201103L 35 | static_assert(std::is_default_constructible::value, "WorkUnitItr should be default constructible"); 36 | #endif 37 | 38 | priority_cache(const metric_type cache_limit) : 39 | m_Cache(cache_limit) { 40 | } 41 | 42 | // Cache functions 43 | inline bool get(const id_type &id, data_type &data) const { 44 | return m_Cache.get(id, data); 45 | } 46 | 47 | inline metric_type dumpKeys(std::vector &allKeys) const { 48 | m_Cache.dumpKeys(allKeys); 49 | return m_Cache.weight(); 50 | } 51 | 52 | void process(const WorkUnitItr &job) { 53 | m_WorkUnitItr = job; 54 | } 55 | 56 | inline void setMaxWeight(const metric_type size) { 57 | m_Cache.setMaxWeight(size); 58 | } 59 | 60 | // worker functions 61 | bool pop(id_type &unit) { 62 | do { 63 | if(m_WorkUnitItr.empty()) 64 | return false; 65 | unit = m_WorkUnitItr.next(); 66 | D_( std::cout << "next unit is : " << unit.filename << std::endl); 67 | switch (m_Cache.update(unit)) { 68 | case FULL: 69 | D_( std::cout << "cache is full, emptying current job" << std::endl); 70 | m_WorkUnitItr.clear(); 71 | break; 72 | case NOT_NEEDED: 73 | D_( std::cout << "unit updated, checking another one" << std::endl); 74 | break; 75 | case NEEDED: 76 | D_( std::cout << "serving " << unit << std::endl); 77 | return true; 78 | } 79 | } while (true); 80 | } 81 | 82 | inline bool push(const id_type &id, const metric_type weight, const data_type &data) { 83 | return m_Cache.put(id, weight, data); 84 | } 85 | 86 | private: 87 | priority_cache_details m_Cache; 88 | WorkUnitItr m_WorkUnitItr; 89 | }; 90 | 91 | } // namespace cache 92 | 93 | } // namespace concurrent 94 | 95 | #endif /* PRIORITY_CACHE_HPP_ */ 96 | -------------------------------------------------------------------------------- /concurrent/cache/priority_cache_details.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PriorityCache.hpp 3 | * 4 | * Created on: 22 nov. 2011 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef PRIORITYCACHE_DETAILS_HPP_ 9 | #define PRIORITYCACHE_DETAILS_HPP_ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | //#define DEBUG_CACHE 22 | 23 | #ifdef DEBUG_CACHE 24 | #include 25 | #define D_(X) (X) 26 | #else 27 | #define D_(X) 28 | #endif 29 | 30 | namespace concurrent { 31 | namespace cache { 32 | 33 | enum UpdateStatus { 34 | FULL, NEEDED, NOT_NEEDED 35 | }; 36 | 37 | /** 38 | * This is the backend for the look ahead cache. It is thread unsafe and 39 | * not meant to be used directly. 40 | */ 41 | template 42 | struct priority_cache_details: private noncopyable { 43 | typedef ID_TYPE id_type; 44 | typedef METRIC_TYPE metric_type; 45 | typedef DATA_TYPE data_type; 46 | 47 | static_assert(std::is_unsigned::value, "metric_type must be unsigned"); 48 | 49 | private: 50 | typedef std::deque IdContainer; 51 | typedef typename IdContainer::iterator IdItr; 52 | 53 | struct WeightedData { 54 | metric_type weight; 55 | data_type data; 56 | WeightedData(const metric_type &weight, const data_type &data) : 57 | weight(weight), data(data) { 58 | } 59 | }; 60 | 61 | typedef std::map CacheContainer; 62 | typedef typename CacheContainer::const_iterator CacheConstItr; 63 | typedef typename CacheContainer::iterator CacheItr; 64 | 65 | public: 66 | priority_cache_details(metric_type limit) : 67 | m_MaxWeight(limit) { 68 | D_( std::cout << "########################################" << std::endl); 69 | } 70 | 71 | void dumpKeys(std::vector &key_container) const { 72 | key_container.clear(); 73 | key_container.reserve(m_Cache.size()); 74 | for (const auto& pair : m_Cache) 75 | key_container.push_back(pair.first); 76 | } 77 | 78 | inline bool full() const { 79 | return m_MaxWeight == 0 || contiguousWeight() > m_MaxWeight; 80 | } 81 | 82 | inline bool contains(id_type id) const { 83 | return m_Cache.find(id) != m_Cache.end(); 84 | } 85 | 86 | inline bool pending(id_type id) const { 87 | return in(m_PendingIds, id); 88 | } 89 | 90 | inline metric_type weight() const { 91 | return currentWeight(); 92 | } 93 | 94 | void discardPending() { 95 | m_DiscardableIds.insert(m_DiscardableIds.begin(), m_PendingIds.begin(), m_PendingIds.end()); 96 | m_PendingIds.clear(); 97 | } 98 | 99 | UpdateStatus update(id_type id) { 100 | if (full()) 101 | return FULL; // 102 | D_( std::cout << "Updating " << id << std::endl); 103 | const bool wasRequested = remove(id); 104 | m_PendingIds.push_back(id); 105 | const UpdateStatus status = wasRequested || contains(id) ? NOT_NEEDED : NEEDED; 106 | if (status == NEEDED) 107 | dump("update dump"); 108 | return status; 109 | } 110 | 111 | bool put(const id_type &id, const metric_type weight, const data_type &data) { 112 | D_( std::cout << "========================================" << std::endl); 113 | if (weight == 0) 114 | throw std::logic_error("can't put an id with no weight"); 115 | if (contains(id)) 116 | throw std::logic_error("id is already present in cache"); 117 | 118 | if (full()) { 119 | D_( std::cout << "cache is *full*, discarding " << id << std::endl); 120 | remove(id); // no more pending 121 | dump("cache full dump"); 122 | return false; 123 | } 124 | if (!canFit(weight)) { 125 | D_( std::cout << "trying to make room for " << id << std::endl); 126 | makeRoomFor(id, weight); 127 | } 128 | if (full()) 129 | return false; 130 | addToCache(id, weight, data); 131 | return true; 132 | } 133 | 134 | bool get(const id_type &id, data_type &data) const { 135 | const CacheConstItr itr = m_Cache.find(id); 136 | if (itr == m_Cache.end()) 137 | return false; 138 | data = itr->second.data; 139 | return true; 140 | } 141 | 142 | inline void setMaxWeight(const metric_type size) { 143 | m_MaxWeight = size; 144 | } 145 | private: 146 | inline void dump(const char* dumpMessage) const { 147 | #ifdef DEBUG_CACHE 148 | using namespace std; 149 | cout << dumpMessage << endl; 150 | cout << "pendings {" << endl; 151 | display(m_PendingIds); 152 | cout << "}" << endl; 153 | cout << "discardables {" << endl; 154 | display(m_DiscardableIds); 155 | cout << "}" << endl; 156 | cout << "----------------------------------------" << endl; 157 | #endif 158 | } 159 | 160 | inline static bool in(const IdContainer &container, const id_type &value) { 161 | return std::find(container.begin(), container.end(), value) != container.end(); 162 | } 163 | 164 | inline static bool remove(IdContainer &container, const id_type &value) { 165 | IdItr itr = std::remove(container.begin(), container.end(), value); 166 | if (itr == container.end()) 167 | return false; 168 | container.erase(itr, container.end()); 169 | return true; 170 | } 171 | 172 | inline bool remove(const id_type &value) { 173 | return remove(m_PendingIds, value) || remove(m_DiscardableIds, value); 174 | } 175 | 176 | inline metric_type contiguousWeight() const { 177 | metric_type sum = 0; 178 | const CacheConstItr &end = m_Cache.end(); 179 | for (const auto& id : m_PendingIds) { 180 | const CacheConstItr &itr = m_Cache.find(id); 181 | if (itr == end) 182 | return sum; 183 | sum += itr->second.weight; 184 | } 185 | return sum; 186 | } 187 | 188 | metric_type currentWeight() const { 189 | metric_type sum = 0; 190 | for (const auto &pair : m_Cache) 191 | sum += pair.second.weight; 192 | return sum; 193 | } 194 | 195 | inline bool canFit(const metric_type weight) const { 196 | if (weight > m_MaxWeight) 197 | return false; 198 | const metric_type maxWeight = m_MaxWeight - weight; 199 | return currentWeight() <= maxWeight; 200 | } 201 | 202 | void makeRoomFor(const id_type currentId, const metric_type weight) { 203 | D_( std::cout << "{ " << currentWeight() << std::endl); 204 | 205 | const IdItr firstMissing = std::find_if(m_PendingIds.begin(), m_PendingIds.end(), [&](const id_type& id){ 206 | return m_Cache.find(id) == m_Cache.end(); 207 | }); 208 | 209 | IdContainer discardables(firstMissing, m_PendingIds.end()); 210 | discardables.insert(discardables.end(), m_DiscardableIds.begin(), m_DiscardableIds.end()); 211 | 212 | const metric_type maxWeight = m_MaxWeight - weight; 213 | std::reverse(discardables.begin(), discardables.end()); 214 | for (const auto &id : discardables) { 215 | evict(id); 216 | if (currentWeight() <= maxWeight) 217 | break; 218 | } // 219 | D_( std::cout << "} " << currentWeight() << std::endl); 220 | } 221 | 222 | inline void evict(id_type id) { 223 | CacheItr itr = m_Cache.find(id); 224 | if (itr == m_Cache.end()) 225 | return; // not found 226 | m_Cache.erase(itr); 227 | remove(id); 228 | D_( std::cout << "\t- " << id << std::endl); 229 | } 230 | 231 | inline void addToCache(const id_type &id, const metric_type weight, const data_type &data) { 232 | if (!(in(m_PendingIds, id) || in(m_DiscardableIds, id))) 233 | m_DiscardableIds.push_back(id); 234 | m_Cache.insert(std::make_pair(id, WeightedData(weight, data))); 235 | D_( std::cout << "+ " << id << std::endl); 236 | } 237 | 238 | private: 239 | metric_type m_MaxWeight; 240 | IdContainer m_DiscardableIds; 241 | IdContainer m_PendingIds; 242 | CacheContainer m_Cache; 243 | }; 244 | 245 | } // namespace cache 246 | } // namespace concurrent 247 | 248 | #endif /* PRIORITYCACHE_DETAILS_HPP_ */ 249 | -------------------------------------------------------------------------------- /concurrent/common.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Common.hpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef COMMON_HPP_ 9 | #define COMMON_HPP_ 10 | 11 | #include 12 | 13 | namespace concurrent { 14 | 15 | /** 16 | * Exception thrown when a container will not serve more items 17 | */ 18 | struct terminated : public std::exception {}; 19 | 20 | struct noncopyable { 21 | noncopyable() = default; 22 | noncopyable(const noncopyable&) = delete; 23 | noncopyable & operator=(const noncopyable&) = delete; 24 | }; 25 | 26 | } 27 | 28 | #endif /* COMMON_HPP_ */ 29 | -------------------------------------------------------------------------------- /concurrent/details/call_type_traits.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * type_traits.hpp 3 | * 4 | * Created on: Jan 5, 2013 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef TYPE_TRAITS_HPP_ 9 | #define TYPE_TRAITS_HPP_ 10 | 11 | #include 12 | 13 | namespace concurrent { 14 | namespace details { 15 | 16 | template 17 | struct ct_imp2 { 18 | typedef const T& param_type; 19 | }; 20 | 21 | template 22 | struct ct_imp2 { 23 | typedef const T param_type; 24 | }; 25 | 26 | template 27 | struct ct_imp { 28 | typedef const T& param_type; 29 | }; 30 | 31 | template 32 | struct ct_imp { 33 | typedef typename ct_imp2::param_type param_type; 34 | }; 35 | 36 | template 37 | struct ct_imp { 38 | typedef const T param_type; 39 | }; 40 | 41 | template 42 | struct call_traits { 43 | public: 44 | typedef T value_type; 45 | typedef T& reference; 46 | typedef const T& const_reference; 47 | // 48 | // C++ Builder workaround: we should be able to define a compile time 49 | // constant and pass that as a single template parameter to ct_imp, 50 | // however compiler bugs prevent this - instead pass three bool's to 51 | // ct_imp and add an extra partial specialization 52 | // of ct_imp to handle the logic. (JM) 53 | typedef typename ct_imp::value, ::std::is_arithmetic::value>::param_type param_type; 54 | }; 55 | 56 | template 57 | struct call_traits { 58 | typedef T& value_type; 59 | typedef T& reference; 60 | typedef const T& const_reference; 61 | typedef T& param_type; // hh removed const 62 | }; 63 | template 64 | struct call_traits { 65 | private: 66 | typedef T array_type[N]; 67 | public: 68 | // degrades array to pointer: 69 | typedef const T* value_type; 70 | typedef array_type& reference; 71 | typedef const array_type& const_reference; 72 | typedef const T* const param_type; 73 | }; 74 | 75 | template 76 | struct call_traits { 77 | private: 78 | typedef const T array_type[N]; 79 | public: 80 | // degrades array to pointer: 81 | typedef const T* value_type; 82 | typedef array_type& reference; 83 | typedef const array_type& const_reference; 84 | typedef const T* const param_type; 85 | }; 86 | 87 | } // namespace details 88 | } // namespace concurrent 89 | 90 | #endif /* TYPE_TRAITS_HPP_ */ 91 | -------------------------------------------------------------------------------- /concurrent/details/queue_base.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * common_queue.hpp 3 | * 4 | * Created on: Mar 22, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef COMMON_QUEUE_HPP_ 9 | #define COMMON_QUEUE_HPP_ 10 | 11 | #include "call_type_traits.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace concurrent { 18 | namespace details { 19 | 20 | /** 21 | * base implementation of the queues functionalities via Static Polymorphism 22 | * and use of the CRTP ( Curiously Recurring Template Pattern ) 23 | */ 24 | template 25 | struct queue_base: private noncopyable { 26 | typedef Container container_type; 27 | typedef typename container_type::size_type size_type; 28 | typedef typename container_type::value_type value_type; 29 | typedef typename call_traits::reference reference; 30 | typedef typename call_traits::param_type param_type; 31 | 32 | static_assert(std::is_trivial::value,"size_type must be trivial"); 33 | static_assert(std::is_copy_assignable::value,"value_type must be copy assignable"); 34 | 35 | void push(param_type value) { 36 | std::unique_lock lock(m_mutex); 37 | exact()->wait_not_full(lock); 38 | exact()->_push(value); 39 | exact()->notify_not_empty(); 40 | } 41 | 42 | bool tryPush(param_type value) { 43 | std::lock_guard lock(m_mutex); 44 | if (!exact()->is_not_full()) 45 | return false; // full 46 | exact()->_push(value); 47 | exact()->notify_not_empty(); 48 | return true; 49 | } 50 | 51 | void pop(reference value) { 52 | std::unique_lock lock(m_mutex); 53 | exact()->wait_not_empty(lock); 54 | value = exact()->_pop(); 55 | exact()->notify_not_full(); 56 | } 57 | 58 | bool tryPop(reference value) { 59 | std::lock_guard lock(m_mutex); 60 | if (!exact()->is_not_empty()) 61 | return false; // empty 62 | value = exact()->_pop(); 63 | exact()->notify_not_full(); 64 | return true; 65 | } 66 | 67 | void clear() { 68 | std::lock_guard lock(m_mutex); 69 | if (exact()->is_not_empty()) { 70 | exact()->_clear(); 71 | exact()->notify_not_full(); 72 | } 73 | } 74 | 75 | template 76 | void drainFrom(CompatibleContainer &collection) { 77 | if (collection.empty()) 78 | return; 79 | std::lock_guard lock(m_mutex); 80 | drain(collection, exact()->m_container); 81 | exact()->notify_not_empty(); 82 | } 83 | 84 | template 85 | bool drainTo(CompatibleContainer& collection) { 86 | std::lock_guard lock(m_mutex); 87 | if (exact()->is_not_empty()) { 88 | drain(exact()->m_container, collection); 89 | exact()->notify_not_full(); 90 | return true; 91 | } 92 | return false; 93 | } 94 | 95 | private: 96 | Derived* exact() { 97 | return static_cast(this); 98 | } 99 | 100 | template 101 | inline static void drain(C1& from, C2& to) { 102 | std::copy(from.begin(), from.end(), std::back_inserter(to)); 103 | from.clear(); 104 | } 105 | 106 | std::mutex m_mutex; 107 | }; 108 | 109 | } // namespace details 110 | } // namespace concurrent 111 | 112 | #endif /* COMMON_QUEUE_HPP_ */ 113 | -------------------------------------------------------------------------------- /concurrent/notifier.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Acknowledge.hpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef RESPONSE_HPP_ 9 | #define RESPONSE_HPP_ 10 | 11 | #include "slot.hpp" 12 | 13 | namespace concurrent { 14 | 15 | struct notifier : private slot { 16 | void ack() { 17 | slot::set(true); 18 | } 19 | 20 | void wait() { 21 | bool dummy; 22 | slot::waitGet(dummy); 23 | } 24 | }; 25 | 26 | } // namespace concurrent 27 | 28 | #endif /* RESPONSE_HPP_ */ 29 | -------------------------------------------------------------------------------- /concurrent/queue.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONCURRENTQUEUE_H_ 2 | #define CONCURRENTQUEUE_H_ 3 | 4 | #include "details/queue_base.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace concurrent { 11 | 12 | /** 13 | * Unbounded concurrent queue for safe access from several threads. 14 | * 15 | * Please note that a single Mutex is used for synchronization of front() and back() 16 | * thus leading to contention if consumer and producer are accessing the container at the same time. 17 | */ 18 | template > 19 | struct queue : public details::queue_base< queue, Container > { 20 | typedef Container container_type; 21 | typedef typename Container::value_type value_type; 22 | typedef typename Container::const_reference const_reference; 23 | 24 | private: 25 | typedef queue ME; 26 | 27 | template 28 | friend struct details::queue_base; 29 | 30 | inline void _clear() { 31 | m_container.clear(); 32 | } 33 | inline void _push(value_type value) { 34 | m_container.push_back(value); 35 | } 36 | inline value_type _pop() { 37 | value_type tmp(m_container.front()); 38 | m_container.pop_front(); 39 | return tmp; 40 | } 41 | inline void wait_not_empty(std::unique_lock &lock) { 42 | m_not_empty.wait(lock, std::bind(&ME::is_not_empty, this)); 43 | } 44 | inline void wait_not_full(std::unique_lock &lock) { 45 | } 46 | inline bool is_not_empty() const { 47 | return !m_container.empty(); 48 | } 49 | inline bool is_not_full() const { 50 | return true; 51 | } 52 | inline void notify_not_full() { 53 | } 54 | inline void notify_not_empty() { 55 | m_not_empty.notify_one(); 56 | } 57 | private: 58 | container_type m_container; 59 | std::condition_variable m_not_empty; 60 | }; 61 | 62 | } // namespace concurrent 63 | 64 | #endif /* CONCURRENTQUEUE_H_ */ 65 | -------------------------------------------------------------------------------- /concurrent/queue_adaptor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ConcurrentQueueAdaptor.hpp 3 | * 4 | * Created on: 16 mars 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef CONCURRENTQUEUEADAPTOR_HPP_ 9 | #define CONCURRENTQUEUEADAPTOR_HPP_ 10 | 11 | #include 12 | 13 | namespace concurrent { 14 | 15 | template 16 | struct queue_adapter { 17 | typedef typename Queue::value_type value_type; 18 | typedef typename Queue::const_reference const_reference; 19 | queue_adapter(Queue& q) : 20 | m_Queue(q) { 21 | } 22 | void push_back(const_reference & t) { 23 | m_Queue.push(t); 24 | } 25 | private: 26 | Queue& m_Queue; 27 | }; 28 | 29 | } // namespace concurrent 30 | 31 | 32 | #endif /* CONCURRENTQUEUEADAPTOR_HPP_ */ 33 | -------------------------------------------------------------------------------- /concurrent/slot.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ConcurrentSlot.hpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #ifndef CONCURRENTSLOT_HPP_ 9 | #define CONCURRENTSLOT_HPP_ 10 | 11 | #include "common.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace concurrent { 18 | 19 | /** 20 | * Thread safe access to a T object 21 | * 22 | * By setting terminate to true, getters will throw a terminated exception 23 | */ 24 | template 25 | struct slot : private noncopyable { 26 | slot() : m_SharedObjectSet(false), m_SharedTerminate(false) { 27 | } 28 | 29 | slot(const T&object) : m_SharedObject(object), m_SharedObjectSet(true), m_SharedTerminate(false) { 30 | } 31 | 32 | void set(const T& object) { 33 | // locking the shared object 34 | std::unique_lock lock(m_Mutex); 35 | internal_set(object); 36 | lock.unlock(); 37 | // notifying shared structure is updated 38 | m_Condition.notify_one(); 39 | } 40 | 41 | void terminate(bool value = true) { 42 | std::unique_lock lock(m_Mutex); 43 | m_SharedTerminate = value; 44 | lock.unlock(); 45 | m_Condition.notify_all(); 46 | } 47 | 48 | void waitGet(T& value) { 49 | std::unique_lock lock(m_Mutex); 50 | 51 | checkTermination(); 52 | 53 | // blocking until set or terminate 54 | while (!m_SharedObjectSet) { 55 | m_Condition.wait(lock); 56 | checkTermination(); 57 | } 58 | 59 | internal_unset(value); 60 | } 61 | 62 | bool tryGet(T& holder) { 63 | // locking the shared object 64 | ::std::lock_guard lock(m_Mutex); 65 | checkTermination(); 66 | 67 | if (!m_SharedObjectSet) 68 | return false; 69 | 70 | internal_unset(holder); 71 | return true; 72 | } 73 | private: 74 | inline void checkTermination() const { 75 | // mutex *must* be locked here, we are reading a shared variable 76 | if (m_SharedTerminate) 77 | throw terminated(); 78 | } 79 | 80 | inline void internal_set(const T& value){ 81 | m_SharedObject = value; 82 | m_SharedObjectSet = true; 83 | } 84 | 85 | inline void internal_unset(T& value){ 86 | assert(m_SharedObjectSet); 87 | value = m_SharedObject; 88 | m_SharedObjectSet = false; 89 | } 90 | 91 | mutable std::mutex m_Mutex; 92 | ::std::condition_variable m_Condition; 93 | T m_SharedObject; 94 | bool m_SharedObjectSet; 95 | bool m_SharedTerminate; 96 | }; 97 | 98 | } // namespace concurrent 99 | 100 | #endif /* CONCURRENTSLOT_HPP_ */ 101 | -------------------------------------------------------------------------------- /examples/BoundedQueueSingleWorker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ConcurrentQueue.cpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | 16 | const int sentinel_value = -1; 17 | const size_t queue_max_element = 3; 18 | static concurrent::bounded_queue g_queue(queue_max_element); ///< shared 19 | 20 | static void worker() { 21 | int value = 0; 22 | for (;;) { 23 | g_queue.pop(value); 24 | if (value == sentinel_value) 25 | return; 26 | printf("worker popped %d\n", value); 27 | } 28 | } 29 | 30 | int main(int argc, char **argv) { 31 | std::thread worker_thread(&worker); 32 | 33 | for (int i = 0; i < 10; ++i) { 34 | g_queue.push(i); 35 | printf("main thread pushed %d\n", i); 36 | } 37 | 38 | g_queue.push(sentinel_value); 39 | 40 | worker_thread.join(); 41 | return EXIT_SUCCESS; 42 | } 43 | -------------------------------------------------------------------------------- /examples/ConcurrentSlot.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ConcurrentSlot.cpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | static concurrent::slot input; ///< shared 17 | static concurrent::notifier notifier; ///< shared 18 | 19 | static void worker() { 20 | try { 21 | for (;;) { 22 | int value; 23 | input.waitGet(value); 24 | printf("worker got : %d\n", value); 25 | notifier.ack(); 26 | } 27 | } catch (concurrent::terminated &e) { 28 | printf("worker terminates\n"); 29 | } 30 | } 31 | 32 | int main(int argc, char **argv) { 33 | std::thread worker_thread(&::worker); 34 | 35 | for (int i = 0; i < 10; ++i) { 36 | printf("main sending : %d\n", i); 37 | input.set(i); 38 | notifier.wait(); 39 | } 40 | 41 | printf("main sending termination\n"); 42 | input.terminate(); 43 | 44 | worker_thread.join(); 45 | 46 | return EXIT_SUCCESS; 47 | } 48 | -------------------------------------------------------------------------------- /examples/LookAheadCache.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ConcurrentSlot.cpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | typedef int id_type; 21 | typedef size_t metric_type; 22 | typedef std::string data_type; 23 | 24 | /** 25 | * A job is a lazy list of id_types. It is used by the cache to feed the working threads. 26 | * 27 | * It must be default constructible and have three public methods : 28 | * - id_type next(); 29 | * - bool empty() const; 30 | * - void clear(); 31 | * 32 | * This simple implementation gives numbers in the range [from, from+count[ but it 33 | * could be whatever you may imagine. 34 | */ 35 | struct Job { 36 | id_type from; 37 | size_t count; 38 | 39 | Job() : 40 | from(0), count(0) { 41 | } 42 | 43 | Job(id_type from, size_t count) : 44 | from(from), count(count) { 45 | } 46 | 47 | id_type next() { 48 | assert(!empty()); 49 | --count; 50 | return from++; 51 | } 52 | 53 | bool empty() const { 54 | return count == 0; 55 | } 56 | 57 | void clear() { 58 | count = 0; 59 | } 60 | }; 61 | 62 | // In this example each item has a cost of one, we're limiting the cache to 100 items. 63 | static const size_t max_weight = 100; 64 | // The actual cache. 65 | static concurrent::cache::lookahead_cache cache(max_weight); 66 | 67 | // a simple signal to the main thread that the worker is launched and processed a least one job. 68 | static concurrent::notifier workerStarted; 69 | 70 | /** 71 | * The worker function will ask work from the cache, process it and 72 | * put the result back in the cache. 73 | * 74 | * If the cache is terminated, every call to popWorkItem will throw a 75 | * concurrent::terminated exception, an easy and safe way to stop the workers 76 | */ 77 | static void worker() { 78 | try { 79 | id_type id; 80 | for (;;) { 81 | // get some work 82 | cache.pop(id); 83 | // process 84 | std::ostringstream str; 85 | str << "data with value " << id; 86 | // push back to cache 87 | cache.push(id, 1, str.str()); 88 | // notifying main thread we did at least one element 89 | workerStarted.ack(); 90 | } 91 | } catch (concurrent::terminated &e) { 92 | printf("worker : terminates\n"); 93 | } 94 | } 95 | 96 | void check(id_type id) { 97 | std::string result; 98 | printf("main : cache has value %d ? ", id); 99 | const bool success = cache.get(id, result); 100 | if (success) 101 | printf("yes, was '%s'\n", result.c_str()); 102 | else 103 | printf("no\n"); 104 | } 105 | 106 | int main(int argc, char **argv) { 107 | // For the sake of simplicity we're starting only one thread here 108 | // but you can add as many workers as you want. 109 | std::thread worker_thread(&::worker); 110 | 111 | // posting a first job 112 | cache.process(Job(1, 10)); 113 | 114 | // waiting for the worker to start 115 | workerStarted.wait(); 116 | 117 | // checking if some data is in the cache 118 | check(1005); 119 | check(1); 120 | check(9); 121 | 122 | // posting another job 123 | cache.process(Job(1000, 20)); 124 | 125 | // checking some more data 126 | check(2); 127 | check(5); 128 | check(1000); 129 | 130 | // requesting termination 131 | printf("main : sending termination\n"); 132 | cache.terminate(); 133 | 134 | // waiting for the thread to finish 135 | worker_thread.join(); 136 | 137 | // as a bonus we can see what was actually processed by the worker 138 | std::vector keys; 139 | printf("main : worker processed %lu elements : ", cache.dumpKeys(keys)); 140 | std::copy(keys.begin(), keys.end(), std::ostream_iterator(std::cout, " ")); 141 | std::cout << std::endl; 142 | 143 | return EXIT_SUCCESS; 144 | } 145 | -------------------------------------------------------------------------------- /examples/QueueManyWorkers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ConcurrentQueue.cpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | static concurrent::queue g_queue; ///< shared 17 | 18 | static void worker() { 19 | int value = 0; 20 | while (g_queue.tryPop(value)) { 21 | std::ostringstream line; 22 | line << "worker " << std::this_thread::get_id() << " popped " << value; 23 | printf("%s\n", line.str().c_str()); 24 | } 25 | } 26 | 27 | int main(int argc, char **argv) { 28 | for (int i = 0; i < 20; ++i) 29 | g_queue.push(i); 30 | 31 | std::vector thread_group; 32 | 33 | for (size_t i = 0; i < 3; ++i) 34 | thread_group.emplace_back(&worker); 35 | 36 | for (std::thread &thread : thread_group) 37 | thread.join(); 38 | 39 | return EXIT_SUCCESS; 40 | } 41 | -------------------------------------------------------------------------------- /examples/QueueSingleWorker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ConcurrentQueue.cpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | static concurrent::queue g_queue; ///< shared 16 | 17 | static void worker() { 18 | int value = 0; 19 | while (g_queue.tryPop(value)) 20 | printf("worker popped %d\n", value); 21 | } 22 | 23 | int main(int argc, char **argv) { 24 | for (int i = 0; i < 10; ++i) 25 | g_queue.push(i); 26 | 27 | std::thread worker_thread(&worker); 28 | 29 | worker_thread.join(); 30 | return EXIT_SUCCESS; 31 | } 32 | -------------------------------------------------------------------------------- /tests/benchmark/cache_benchmark_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | using namespace chrono; 13 | using namespace concurrent::cache; 14 | 15 | struct JobData { 16 | size_t loadTime; 17 | size_t decodeTime; 18 | 19 | JobData() : 20 | loadTime(-1), decodeTime(-1) { 21 | } 22 | 23 | inline bool operator<(const JobData& other) const { 24 | if (loadTime == other.loadTime) 25 | return decodeTime < other.decodeTime; 26 | return loadTime < other.loadTime; 27 | } 28 | }; 29 | 30 | typedef JobData* id_type; 31 | 32 | struct Job { 33 | Job() = default; 34 | 35 | Job(const deque &data) : 36 | m_Data(data), m_Index(0) { 37 | } 38 | 39 | inline void clear() { 40 | m_Index = m_Data.size(); 41 | } 42 | 43 | inline bool empty() const { 44 | return m_Index >= m_Data.size(); 45 | } 46 | 47 | inline id_type next() { 48 | return &m_Data[m_Index++]; 49 | } 50 | 51 | private: 52 | deque m_Data; 53 | size_t m_Index; 54 | }; 55 | 56 | typedef size_t metric_type; 57 | typedef size_t data_type; 58 | typedef lookahead_cache CACHE; 59 | 60 | concurrent::queue decodeQueue; 61 | 62 | inline static void sleepFor(const size_t ms) { 63 | this_thread::sleep_for(milliseconds(ms)); 64 | } 65 | 66 | inline static void decode(const JobData &unit) { 67 | sleepFor(unit.decodeTime); 68 | } 69 | 70 | inline static void load(const JobData &unit) { 71 | sleepFor(unit.loadTime); 72 | } 73 | 74 | inline static bool lastUnit(const JobData &unit) { 75 | return unit.loadTime == size_t(-1) && unit.decodeTime == size_t(-1); 76 | } 77 | 78 | void worker(CACHE &jobProducer) { 79 | JobData *pUnit = NULL; 80 | try { 81 | while (true) { 82 | if (decodeQueue.tryPop(pUnit)) { 83 | decode(*pUnit); 84 | jobProducer.push(pUnit, 1, 0); 85 | } else { 86 | jobProducer.pop(pUnit); 87 | if (lastUnit(*pUnit)) { 88 | jobProducer.terminate(); 89 | break; 90 | } else { 91 | load(*pUnit); 92 | decodeQueue.push(pUnit); 93 | } 94 | } 95 | } 96 | } catch (concurrent::terminated &e) { 97 | } 98 | while (decodeQueue.tryPop(pUnit)) { 99 | decode(*pUnit); 100 | jobProducer.push(pUnit, 1, 0); 101 | } 102 | } 103 | 104 | deque loadData(const char* filename) { 105 | deque data; 106 | ifstream file(filename); 107 | if (!file.is_open()) 108 | throw runtime_error("unable to load file"); 109 | while (file.good()) { 110 | JobData entry; 111 | file >> entry.loadTime; 112 | file >> entry.decodeTime; 113 | data.push_back(entry); 114 | } 115 | // adding sentinel 116 | data.push_back(JobData()); 117 | return data; 118 | } 119 | 120 | static inline milliseconds launchBench(const char *filename, const size_t threads) { 121 | CACHE cache(-1); // unlimited cache 122 | const deque data = loadData(filename); 123 | 124 | // launching the worker 125 | vector group; 126 | for (size_t i = 0; i < threads; ++i) 127 | group.emplace_back(bind(&worker, ref(cache))); 128 | 129 | // getting time before 130 | const auto start = high_resolution_clock::now(); 131 | 132 | //pushing the job 133 | cache.process(Job(data)); 134 | 135 | // waiting for worker to stop 136 | for (thread &thread : group) 137 | thread.join(); 138 | const auto end = high_resolution_clock::now(); 139 | 140 | return duration_cast(end - start); 141 | } 142 | 143 | TEST(cache, DISABLED_benchmark) { 144 | const size_t max_thread = 16; 145 | const vector filenames = { "tests/benchmark/data/gch.txt", "tests/benchmark/data/nro.txt" }; 146 | 147 | typedef map Times; 148 | typedef map TimeMap; 149 | TimeMap data; 150 | for (const string& filename : filenames) { 151 | Times times; 152 | cout << "performing test for " << filename << endl; 153 | for (size_t i = 1; i <= max_thread; ++i) { 154 | cout << "*thread " << i << " : "; 155 | cout.flush(); 156 | times.insert(make_pair(i, launchBench(filename.c_str(), i))); 157 | cout << times.rbegin()->second.count() << "ms" << endl; 158 | } 159 | data.insert(make_pair(filename, times)); 160 | } 161 | 162 | // analyzing the results 163 | for (const auto&filenamePair : data) { 164 | cout << filenamePair.first << endl; 165 | milliseconds reference; 166 | const Times × = filenamePair.second; 167 | for (const Times::value_type &timePair : times) { 168 | const milliseconds &time = timePair.second; 169 | cout << '#' << timePair.first << '\t' << time.count() << " ms"; 170 | if (timePair.first == 1) { 171 | reference = time; 172 | } else { 173 | cout << "\t speedup x" << (double(reference.count()) / time.count()); 174 | } 175 | cout << endl; 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /tests/benchmark/data/gch.txt: -------------------------------------------------------------------------------- 1 | 49 43 2 | 32 19 3 | 17 19 4 | 19 19 5 | 28 19 6 | 38 19 7 | 34 19 8 | 32 19 9 | 14 19 10 | 43 19 11 | 22 19 12 | 16 19 13 | 14 19 14 | 10 20 15 | 88 19 16 | 16 19 17 | 18 19 18 | 34 19 19 | 7 19 20 | 21 19 21 | 19 19 22 | 11 20 23 | 17 19 24 | 10 19 25 | 8 19 26 | 24 19 27 | 34 19 28 | 26 19 29 | 3 19 30 | 30 19 31 | 12 19 32 | 6 19 33 | 7 19 34 | 12 19 35 | 34 20 36 | 35 19 37 | 32 19 38 | 22 20 39 | 9 20 40 | 9 20 41 | 21 20 42 | 15 20 43 | 13 20 44 | 5 20 45 | 39 20 46 | 37 20 47 | 30 19 48 | 15 19 49 | 24 20 50 | 38 20 51 | 7 20 52 | 30 19 53 | 3 20 54 | 32 19 55 | 28 20 56 | 32 20 57 | 50 20 58 | 15 20 59 | 31 20 60 | 10 20 61 | 5 20 62 | 7 20 63 | 36 20 64 | 61 20 65 | 19 20 66 | 35 20 67 | 23 20 68 | 9 19 69 | 11 19 70 | 17 19 71 | 17 19 72 | 13 19 73 | 8 19 74 | 88 19 75 | 36 20 76 | 68 19 77 | 94 20 78 | 83 20 79 | 109 20 80 | 88 20 81 | 46 20 82 | 62 20 83 | 31 20 84 | 55 20 85 | 34 19 86 | 10 20 87 | 21 20 88 | 7 20 89 | 15 20 90 | 9 20 91 | 11 20 92 | 47 20 93 | 21 20 94 | 41 20 95 | 12 20 96 | 23 20 97 | 22 20 98 | 21 21 99 | 22 20 100 | 23 20 101 | 19 20 102 | 14 20 103 | 10 20 104 | 39 20 105 | 11 20 106 | 27 20 107 | 20 20 108 | 31 20 109 | 16 20 110 | 13 20 111 | 39 20 112 | 16 20 113 | 26 20 114 | 20 20 115 | 22 20 116 | 10 20 117 | 15 21 118 | 10 20 119 | 18 20 120 | 17 20 121 | 17 20 122 | 26 20 123 | 20 20 124 | 15 20 125 | 13 20 126 | 7 20 127 | 13 20 128 | 13 20 129 | 5 20 130 | 14 20 131 | 17 20 132 | 15 20 133 | 29 20 134 | 18 20 135 | 23 20 136 | 16 20 137 | 10 20 138 | 28 21 139 | 3 20 140 | 22 20 141 | 4 20 142 | 19 20 143 | 3 20 144 | 43 20 145 | 3 20 146 | 14 20 147 | 3 20 148 | 20 20 149 | 3 20 150 | 14 20 151 | 3 20 152 | 25 20 153 | 22 20 154 | 20 20 155 | 3 20 156 | 25 20 157 | 19 20 158 | 19 20 159 | 3 20 160 | 8 20 161 | 3 20 162 | 9 19 163 | 11 19 164 | 7 19 165 | 11 19 166 | 13 19 167 | 9 19 168 | 20 19 169 | 3 19 170 | 18 19 171 | 7 19 172 | 31 20 173 | 39 19 174 | 21 19 175 | 22 20 176 | 17 19 177 | 8 19 178 | 8 19 179 | 10 19 180 | 7 19 181 | 8 19 182 | 14 20 183 | 9 19 184 | 8 19 185 | 10 19 186 | 10 19 187 | 9 19 188 | 10 20 189 | 42 19 190 | 40 19 191 | 139 19 192 | 201 20 193 | 3 20 194 | 15 20 195 | 25 20 196 | 11 20 197 | 9 20 198 | 13 20 199 | 11 20 200 | 11 20 201 | 16 20 202 | 9 20 203 | 3 20 204 | 17 20 205 | 14 20 206 | 7 20 207 | 29 20 208 | 36 20 209 | 24 20 210 | 26 20 211 | 29 20 212 | 17 20 213 | 15 20 214 | 12 20 215 | 3 20 216 | 23 20 217 | 3 20 218 | 24 20 219 | 12 20 220 | 22 20 221 | 4 20 222 | 77 20 223 | 4 20 224 | 20 20 225 | 3 20 226 | 45 20 227 | 3 20 228 | 23 20 229 | 3 20 230 | 90 20 231 | 3 20 232 | 36 20 233 | 4 20 234 | 27 20 235 | 3 20 236 | 84 20 237 | 26 20 238 | 3 20 239 | 26 20 240 | 3 20 241 | 3 20 242 | 57 20 243 | 4 20 244 | 3 20 245 | 34 20 246 | 3 20 247 | 3 20 248 | 19 20 249 | 3 20 250 | 3 20 251 | 17 20 252 | 13 20 253 | 9 20 254 | 11 20 255 | 9 20 256 | 11 20 257 | 12 20 258 | 12 20 259 | 12 20 260 | 11 20 261 | 13 20 262 | 11 20 263 | 28 20 264 | 34 20 265 | 18 20 266 | 17 20 267 | 13 20 268 | 23 20 269 | 12 20 270 | 15 20 271 | 7 20 272 | 10 20 273 | 13 20 274 | 10 20 275 | 16 20 276 | 14 20 277 | 8 20 278 | 8 20 279 | 11 20 280 | 9 19 281 | 14 20 282 | 16 19 283 | 15 20 284 | 7 20 285 | 52 20 286 | 241 20 287 | 23 19 288 | 7 19 289 | 8 20 290 | 20 20 291 | 19 19 292 | 5 19 293 | 10 19 294 | 9 19 295 | 9 20 296 | 14 19 297 | 13 19 298 | 13 19 299 | 12 19 300 | 15 19 301 | 30 19 302 | 30 19 303 | 21 20 304 | 17 20 305 | 10 19 306 | 14 19 307 | 8 20 308 | 15 20 309 | 7 19 310 | 11 20 311 | 16 20 312 | 9 20 313 | 11 20 314 | 11 20 315 | 11 20 316 | 15 19 317 | 21 19 318 | 7 19 319 | 8 19 320 | 16 19 321 | 7 19 322 | 20 19 323 | 35 19 324 | 21 19 325 | 26 19 326 | 22 20 327 | 14 19 328 | 11 20 329 | 12 20 330 | 9 19 331 | 16 20 332 | 9 20 333 | 28 19 334 | 51 19 335 | 22 19 336 | 6 20 337 | 11 19 338 | 10 19 339 | 10 19 340 | 13 19 341 | 56 20 342 | 59 19 343 | 21 19 344 | 93 19 345 | 25 19 346 | 14 19 347 | 14 19 348 | 10 19 349 | 14 19 350 | 12 19 351 | 10 19 352 | 12 19 353 | 9 20 354 | 10 20 355 | 6 20 356 | 8 19 357 | 8 19 358 | 10 19 359 | 10 19 360 | 24 20 361 | 28 19 362 | 45 19 363 | 25 19 364 | 19 20 365 | 11 19 366 | 15 20 367 | 9 19 368 | 11 20 369 | 10 20 370 | 12 20 371 | 17 20 372 | 12 20 373 | 15 20 374 | 16 20 375 | 24 20 376 | 3 20 377 | 3 20 378 | 20 20 379 | 2 20 380 | 3 20 381 | 3 20 382 | 18 20 383 | 3 20 384 | 3 20 385 | 3 20 386 | 10 20 387 | 3 20 388 | 3 20 389 | 3 20 390 | 12 20 391 | 3 20 392 | 3 20 393 | 3 20 394 | 18 19 395 | 15 19 396 | 10 19 397 | 18 19 398 | 14 19 399 | 28 19 400 | 11 19 401 | 11 19 402 | 33 19 403 | 23 19 404 | 35 19 405 | 21 19 406 | 12 20 407 | 14 19 408 | 8 21 409 | 10 19 410 | 12 21 411 | 13 20 412 | 13 20 413 | 22 20 414 | 3 20 415 | 4 20 416 | 3 20 417 | 23 20 418 | 3 20 419 | 3 20 420 | 3 20 421 | 14 20 422 | 3 20 423 | 3 20 424 | 3 20 425 | 21 20 426 | 3 20 427 | 3 20 428 | 3 20 429 | 13 20 430 | 3 20 431 | 3 20 432 | 3 20 433 | 18 20 434 | 3 20 435 | 3 20 436 | 3 20 437 | 9 20 438 | 3 20 439 | 3 20 440 | 3 20 441 | 22 20 442 | 33 20 443 | 4 20 444 | 3 20 445 | 16 20 446 | 12 19 447 | 15 19 448 | 13 20 449 | 15 20 450 | 8 19 451 | 10 19 452 | 8 19 453 | 12 20 454 | 13 20 455 | 15 20 456 | 10 19 457 | 19 20 458 | 3 20 459 | 3 20 460 | 3 19 461 | 3 19 462 | 23 20 463 | 3 19 464 | 236 20 465 | 3 20 466 | 3 20 467 | 10 20 468 | 3 20 469 | 3 20 470 | 3 20 471 | 3 20 472 | 22 20 473 | 3 20 474 | 3 20 475 | 3 20 476 | 3 20 477 | 13 20 478 | 3 20 479 | 3 20 480 | 3 20 481 | 3 20 482 | 12 20 483 | 4 20 484 | 3 20 485 | 3 20 486 | 3 20 487 | 24 20 488 | 4 20 489 | 3 20 490 | 3 20 491 | 3 20 492 | 11 20 493 | 3 20 494 | 3 20 495 | 3 20 496 | 3 20 497 | 16 20 498 | 3 20 499 | 3 20 500 | 3 20 501 | 3 20 502 | 24 20 503 | 3 20 504 | 3 20 505 | 3 20 506 | 3 20 507 | 21 20 508 | 3 20 509 | 3 20 510 | 3 20 511 | 3 20 512 | 19 20 513 | 3 20 514 | 18 20 515 | 3 20 516 | 9 20 517 | 9 20 518 | 3 20 519 | 3 20 520 | 3 20 521 | 3 20 522 | 19 20 523 | 3 20 524 | 3 20 525 | 11 20 526 | 4 21 527 | 21 21 528 | 13 21 529 | 3 21 530 | 10 21 531 | 4 21 532 | 4 21 533 | 13 21 534 | 3 21 535 | 4 21 536 | 15 21 537 | 4 21 538 | 4 21 539 | 12 21 540 | 4 21 541 | 4 21 542 | 74 21 543 | 4 21 544 | 4 21 545 | 14 21 546 | 4 21 547 | 3 21 548 | 21 21 549 | 19 21 550 | 4 21 551 | 22 21 552 | 3 21 553 | 4 21 554 | 4 21 555 | 10 21 556 | 4 21 557 | 4 21 558 | 4 21 559 | 12 21 560 | 4 21 561 | 4 21 562 | 4 21 563 | 30 21 564 | 4 21 565 | 13 21 566 | 17 21 567 | 7 20 568 | 3 20 569 | 3 21 570 | 3 21 571 | 3 20 572 | 13 20 573 | 3 21 574 | 4 20 575 | 12 21 576 | 15 20 577 | 3 20 578 | 4 20 579 | 9 20 580 | 3 21 581 | 4 21 582 | 3 20 583 | 35 20 584 | 3 20 585 | 4 20 586 | 26 20 587 | 12 20 588 | 28 21 589 | 3 20 590 | 3 20 591 | 12 20 592 | 3 20 593 | 3 20 594 | 11 20 595 | 3 20 596 | 3 20 597 | 10 20 598 | 3 20 599 | 3 21 600 | 9 20 601 | 3 20 602 | 4 20 603 | 8 20 604 | 193 20 605 | 3 20 606 | 3 20 607 | 3 20 608 | 3 20 609 | 37 20 610 | 3 20 611 | 4 20 612 | 3 20 613 | 3 20 614 | 7 20 615 | 4 20 616 | 4 20 617 | 3 20 618 | 8 20 619 | 4 20 620 | 3 21 621 | 22 20 622 | 4 20 623 | 3 21 624 | 12 20 625 | 3 20 626 | 3 20 627 | 22 20 628 | 3 20 629 | 3 20 630 | 3 20 631 | 7 20 632 | 3 21 633 | 3 20 634 | 6 20 635 | 58 20 636 | 3 21 637 | 4 20 638 | 4 21 639 | 19 20 640 | 4 20 641 | 4 20 642 | 10 21 643 | 4 20 644 | 4 22 645 | 10 22 646 | 5 21 647 | 4 21 648 | 7 21 649 | 4 21 650 | 4 21 651 | 29 21 652 | 12 20 653 | 12 20 654 | 3 20 655 | 3 21 656 | 4 21 657 | 28 21 658 | 4 20 659 | 5 20 660 | 10 21 661 | 3 21 662 | 3 20 663 | 26 21 664 | 3 20 665 | 4 20 666 | 8 20 667 | 3 21 668 | 4 21 669 | 6 21 670 | 3 21 671 | 3 21 672 | 5 21 673 | 3 20 674 | 4 21 675 | 23 21 676 | 3 21 677 | 3 20 678 | 4 20 679 | 9 21 680 | 3 20 681 | 3 21 682 | 17 20 683 | 3 20 684 | 3 21 685 | 8 20 686 | 4 21 687 | 3 20 688 | 8 21 689 | 3 21 690 | 20 18 691 | 9 18 692 | 7 18 693 | 8 18 694 | 8 18 695 | 9 18 696 | 12 18 697 | 13 18 698 | 13 18 699 | 11 18 700 | 7 18 701 | 8 18 702 | 6 18 703 | 29 18 704 | 8 18 705 | 6 18 706 | 9 18 707 | 34 18 708 | 7 18 709 | 6 18 710 | 2 18 711 | 9 18 712 | 11 18 713 | 8 18 714 | 11 18 715 | 28 18 716 | 6 18 717 | 5 18 718 | 12 18 719 | 11 18 720 | 12 18 721 | 6 18 722 | 10 18 723 | 25 18 724 | 24 18 725 | 33 18 726 | 28 18 727 | 5 18 728 | 11 18 729 | 7 18 730 | 13 19 731 | 8 18 732 | 9 18 733 | 14 18 734 | 10 18 735 | 6 18 736 | 6 18 737 | 58 18 738 | 16 18 739 | 7 18 740 | 11 18 741 | 10 18 742 | 11 18 743 | 43 18 744 | 22 18 745 | 22 18 746 | 42 18 747 | 7 18 748 | 14 18 749 | 6 18 750 | 5 18 751 | 21 18 -------------------------------------------------------------------------------- /tests/benchmark/data/nro.txt: -------------------------------------------------------------------------------- 1 | 39 36 2 | 35 33 3 | 1 35 4 | 6 35 5 | 18 35 6 | 7 35 7 | 1 35 8 | 1 35 9 | 1 35 10 | 1 35 11 | 1 35 12 | 10 35 13 | 10 35 14 | 1 35 15 | 1 35 16 | 1 35 17 | 14 34 18 | 4 34 19 | 2 34 20 | 7 34 21 | 1 35 22 | 2 35 23 | 1 35 24 | 1 36 25 | 1 35 26 | 1 35 27 | 1 35 28 | 1 36 29 | 1 35 30 | 1 36 31 | 1 36 32 | 1 38 33 | 1 35 34 | 1 36 35 | 57 35 36 | 28 36 37 | 1 35 38 | 11 36 39 | 1 36 40 | 1 37 41 | 1 37 42 | 1 36 43 | 16 36 44 | 1 36 45 | 1 36 46 | 1 36 47 | 1 36 48 | 1 36 49 | 1 36 50 | 11 36 51 | 1 36 52 | 1 36 53 | 1 36 54 | 1 35 55 | 1 36 56 | 1 36 57 | 1 35 58 | 1 36 59 | 1 36 60 | 1 36 61 | 1 36 62 | 1 36 63 | 1 36 64 | 1 36 65 | 3 38 66 | 1 36 67 | 1 36 68 | 7 34 69 | 1 34 70 | 1 35 71 | 1 35 72 | 1 34 73 | 1 34 74 | 1 34 75 | 1 34 76 | 1 34 77 | 1 34 78 | 1 34 79 | 1 35 80 | 1 36 81 | 1 35 82 | 1 35 83 | 1 35 84 | 1 36 85 | 1 36 86 | 11 36 87 | 1 36 88 | 1 36 89 | 1 36 90 | 1 36 91 | 1 36 92 | 1 36 93 | 1 38 94 | 1 38 95 | 1 38 96 | 1 38 97 | 7 38 98 | 1 38 99 | 1 38 100 | 1 38 101 | 1 37 102 | 10 37 103 | 1 37 104 | 1 38 105 | 1 38 106 | 1 38 107 | 1 38 108 | 1 38 109 | 1 38 110 | 1 40 111 | 1 38 112 | 1 38 113 | 6 38 114 | 2 38 115 | 1 38 116 | 1 38 117 | 1 37 118 | 1 38 119 | 1 38 120 | 1 38 121 | 1 38 122 | 1 38 123 | 1 38 124 | 1 37 125 | 1 37 126 | 0 37 127 | 1 37 128 | 1 37 129 | 1 37 130 | 1 37 131 | 1 37 132 | 1 38 133 | 1 37 134 | 1 37 135 | 1 37 136 | 0 37 137 | 1 37 138 | 1 37 139 | 1 37 140 | 1 37 141 | 0 37 142 | 1 38 143 | 1 37 144 | 1 37 145 | 0 37 146 | 1 38 147 | 1 37 148 | 1 38 149 | 1 37 150 | 1 40 151 | 1 37 152 | 1 37 153 | 1 37 154 | 1 37 155 | 1 37 156 | 1 37 157 | 1 37 158 | 1 37 159 | 1 37 160 | 1 38 161 | 1 38 162 | 1 34 163 | 0 34 164 | 0 34 165 | 0 34 166 | 0 34 167 | 0 34 168 | 0 35 169 | 0 35 170 | 0 35 171 | 0 35 172 | 0 35 173 | 0 34 174 | 0 34 175 | 0 34 176 | 0 34 177 | 0 34 178 | 1 33 179 | 0 35 180 | 0 35 181 | 0 35 182 | 0 35 183 | 0 35 184 | 1 35 185 | 0 35 186 | 0 35 187 | 0 35 188 | 0 35 189 | 0 35 190 | 0 35 191 | 0 35 192 | 1 37 193 | 1 38 194 | 1 37 195 | 1 37 196 | 1 37 197 | 1 37 198 | 1 36 199 | 2 37 200 | 1 37 201 | 1 37 202 | 1 37 203 | 1 37 204 | 1 37 205 | 14 36 206 | 1 37 207 | 1 37 208 | 1 37 209 | 0 37 210 | 1 37 211 | 1 37 212 | 1 37 213 | 1 37 214 | 1 38 215 | 1 38 216 | 8 38 217 | 1 38 218 | 1 37 219 | 1 38 220 | 1 38 221 | 1 38 222 | 1 38 223 | 1 38 224 | 1 38 225 | 1 38 226 | 2 37 227 | 1 37 228 | 1 37 229 | 1 37 230 | 1 37 231 | 1 39 232 | 1 37 233 | 1 37 234 | 1 37 235 | 1 37 236 | 1 37 237 | 1 37 238 | 1 37 239 | 1 39 240 | 1 38 241 | 1 38 242 | 1 38 243 | 1 38 244 | 1 38 245 | 1 38 246 | 1 38 247 | 1 38 248 | 1 38 249 | 1 38 250 | 1 38 251 | 1 38 252 | 1 36 253 | 0 36 254 | 1 36 255 | 0 36 256 | 1 36 257 | 1 36 258 | 1 37 259 | 1 36 260 | 1 35 261 | 0 35 262 | 1 35 263 | 1 35 264 | 1 35 265 | 1 35 266 | 1 35 267 | 1 35 268 | 1 35 269 | 7 35 270 | 1 35 271 | 1 35 272 | 1 35 273 | 0 35 274 | 1 35 275 | 0 35 276 | 0 35 277 | 0 35 278 | 0 34 279 | 0 36 280 | 0 35 281 | 0 36 282 | 0 36 283 | 0 35 284 | 0 35 285 | 0 35 286 | 0 35 287 | 0 35 288 | 0 35 289 | 0 35 290 | 1 35 291 | 0 35 292 | 0 35 293 | 0 35 294 | 0 35 295 | 0 36 296 | 0 34 297 | 0 34 298 | 0 35 299 | 0 35 300 | 0 35 301 | 0 35 302 | 0 35 303 | 0 41 304 | 33 36 305 | 44 37 306 | 73 36 307 | 2 35 308 | 2 35 309 | 4 38 310 | 3 35 311 | 2 35 312 | 1 35 313 | 1 35 314 | 22 36 315 | 0 36 316 | 0 34 317 | 0 34 318 | 0 33 319 | 0 33 320 | 0 33 321 | 0 34 322 | 1 33 323 | 0 34 324 | 0 34 325 | 0 35 326 | 0 34 327 | 0 35 328 | 0 35 329 | 0 35 330 | 0 35 331 | 0 35 332 | 0 35 333 | 0 35 334 | 0 35 335 | 0 35 336 | 0 34 337 | 24 35 338 | 1 35 339 | 1 35 340 | 1 35 341 | 1 35 342 | 1 35 343 | 1 35 344 | 1 36 345 | 5 35 346 | 0 35 347 | 0 35 348 | 0 34 349 | 0 34 350 | 1 35 351 | 0 34 352 | 0 34 353 | 0 34 354 | 1 34 355 | 0 35 356 | 0 35 357 | 0 35 358 | 0 35 359 | 0 34 360 | 0 35 361 | 0 35 362 | 0 35 363 | 0 35 364 | 0 35 365 | 0 35 366 | 0 34 367 | 0 34 368 | 0 34 369 | 0 35 370 | 1 35 371 | 0 35 372 | 1 35 373 | 1 35 374 | 0 36 375 | 1 36 376 | 0 36 377 | 1 36 378 | 0 35 379 | 1 36 380 | 1 36 381 | 1 36 382 | 0 36 383 | 0 36 384 | 0 36 385 | 0 36 386 | 1 36 387 | 0 37 388 | 0 37 389 | 1 37 390 | 0 37 391 | 1 39 392 | 1 37 393 | 0 36 394 | 0 35 395 | 0 35 396 | 0 35 397 | 0 35 398 | 0 34 399 | 0 35 400 | 0 35 401 | 0 34 402 | 1 34 403 | 0 34 404 | 0 34 405 | 0 35 406 | 0 34 407 | 0 35 408 | 0 35 409 | 0 35 410 | 0 35 411 | 1 35 412 | 1 36 413 | 1 36 414 | 1 36 415 | 1 36 416 | 1 36 417 | 1 36 418 | 4 36 419 | 1 36 420 | 0 36 421 | 0 36 422 | 0 36 423 | 0 36 424 | 0 36 425 | 0 36 426 | 1 36 427 | 0 36 428 | 1 35 429 | 1 35 430 | 0 35 431 | 0 35 432 | 1 35 433 | 0 35 434 | 1 35 435 | 1 35 436 | 0 35 437 | 1 35 438 | 1 35 439 | 0 35 440 | 0 35 441 | 0 35 442 | 0 35 443 | 0 35 444 | 1 35 445 | 0 34 446 | 0 34 447 | 0 34 448 | 0 34 449 | 0 34 450 | 1 34 451 | 0 34 452 | 0 34 453 | 0 34 454 | 0 34 455 | 0 34 456 | 0 35 457 | 0 34 458 | 0 34 459 | 0 34 460 | 0 34 461 | 0 34 462 | 0 34 463 | 0 34 464 | 0 34 465 | 0 34 466 | 1 34 467 | 1 35 468 | 24 35 469 | 1 35 470 | 1 35 471 | 1 35 472 | 1 36 473 | 0 35 474 | 1 38 475 | 0 35 476 | 1 35 477 | 1 35 478 | 1 36 479 | 1 35 480 | 0 35 481 | 0 36 482 | 1 35 483 | 0 35 484 | 1 35 485 | 0 35 486 | 1 35 487 | 1 36 488 | 0 35 489 | 1 36 490 | 1 36 491 | 1 36 492 | 1 36 493 | 1 36 494 | 0 36 495 | 1 36 496 | 1 36 497 | 1 36 498 | 1 36 499 | 1 36 500 | 1 36 501 | 1 36 502 | 0 37 503 | 1 36 504 | 1 36 505 | 1 36 506 | 1 36 507 | 1 36 508 | 1 36 509 | 1 36 510 | 1 36 511 | 1 36 512 | 1 36 513 | 7 36 514 | 45 36 515 | 1 36 516 | 1 36 517 | 1 36 518 | 1 36 519 | 1 36 520 | 1 36 521 | 12 36 522 | 0 36 523 | 1 36 524 | 31 36 525 | 1 36 526 | 1 40 527 | 1 40 528 | 1 40 529 | 7 40 530 | 2 40 531 | 1 40 532 | 1 40 533 | 1 40 534 | 1 40 535 | 1 40 536 | 1 40 537 | 1 40 538 | 1 40 539 | 1 42 540 | 1 41 541 | 1 41 542 | 1 41 543 | 1 40 544 | 1 40 545 | 1 41 546 | 2 40 547 | 1 40 548 | 1 40 549 | 1 40 550 | 1 40 551 | 1 40 552 | 1 43 553 | 1 40 554 | 1 40 555 | 1 40 556 | 1 41 557 | 1 40 558 | 1 40 559 | 1 41 560 | 1 40 561 | 1 40 562 | 2 40 563 | 1 40 564 | 1 40 565 | 1 40 566 | 1 40 567 | 1 38 568 | 1 38 569 | 1 38 570 | 1 38 571 | 1 38 572 | 1 38 573 | 1 38 574 | 1 38 575 | 1 38 576 | 1 38 577 | 1 38 578 | 2 38 579 | 1 38 580 | 1 38 581 | 1 38 582 | 1 38 583 | 1 38 584 | 1 38 585 | 1 38 586 | 1 38 587 | 1 37 588 | 1 38 589 | 1 38 590 | 1 38 591 | 1 38 592 | 1 38 593 | 1 38 594 | 2 38 595 | 1 38 596 | 1 37 597 | 1 38 598 | 1 37 599 | 1 37 600 | 1 38 601 | 1 38 602 | 2 37 603 | 1 38 604 | 1 37 605 | 1 37 606 | 1 37 607 | 11 37 608 | 1 37 609 | 1 37 610 | 1 37 611 | 1 37 612 | 1 37 613 | 1 37 614 | 11 37 615 | 1 37 616 | 1 37 617 | 1 37 618 | 1 37 619 | 1 37 620 | 1 37 621 | 1 37 622 | 1 37 623 | 1 37 624 | 1 37 625 | 1 37 626 | 2 38 627 | 1 37 628 | 1 37 629 | 1 38 630 | 1 38 631 | 1 37 632 | 1 38 633 | 1 38 634 | 1 37 635 | 1 37 636 | 1 38 637 | 1 37 638 | 1 37 639 | 1 37 640 | 1 37 641 | 1 38 642 | 2 38 643 | 1 38 644 | 1 38 645 | 1 38 646 | 1 38 647 | 1 38 648 | 1 38 649 | 1 38 650 | 1 38 651 | 1 38 652 | 1 38 653 | 1 38 654 | 1 38 655 | 1 38 656 | 1 38 657 | 1 38 658 | 2 38 659 | 1 42 660 | 1 38 661 | 1 38 662 | 1 38 663 | 1 38 664 | 1 40 665 | 1 38 666 | 1 38 667 | 1 38 668 | 1 38 669 | 1 40 670 | 1 38 671 | 1 38 672 | 1 38 673 | 1 38 674 | 2 38 675 | 1 39 676 | 28 38 677 | 1 38 678 | 1 38 679 | 1 38 680 | 1 38 681 | 6 38 682 | 1 38 683 | 1 38 684 | 1 38 685 | 1 38 686 | 1 38 687 | 1 38 688 | 1 38 689 | 1 38 690 | 1 28 691 | 0 28 692 | 0 28 693 | 0 28 694 | 0 28 695 | 0 28 696 | 0 28 697 | 0 28 698 | 0 28 699 | 0 28 700 | 0 28 701 | 0 28 702 | 0 28 703 | 0 28 704 | 0 28 705 | 0 28 706 | 1 28 707 | 0 28 708 | 0 28 709 | 0 28 710 | 0 28 711 | 1 28 712 | 1 30 713 | 1 28 714 | 1 28 715 | 0 28 716 | 1 28 717 | 1 28 718 | 0 28 719 | 1 28 720 | 1 28 721 | 1 28 722 | 1 28 723 | 1 28 724 | 7 28 725 | 0 28 726 | 0 28 727 | 0 28 728 | 0 28 729 | 0 28 730 | 0 28 731 | 0 28 732 | 0 28 733 | 0 28 734 | 0 28 735 | 0 28 736 | 0 28 737 | 0 28 738 | 1 28 739 | 0 28 740 | 0 28 741 | 0 28 742 | 0 28 743 | 0 28 744 | 0 28 745 | 0 28 746 | 0 28 747 | 0 28 748 | 0 28 749 | 0 28 750 | 0 28 751 | 0 28 -------------------------------------------------------------------------------- /tests/cache_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace concurrent::cache; 9 | 10 | typedef priority_cache_details CACHE; 11 | 12 | TEST(Cache, cacheFullness ) 13 | { 14 | EXPECT_TRUE( CACHE(0).full() ); 15 | EXPECT_FALSE( CACHE(1).full() ); 16 | } 17 | 18 | TEST(Cache, cacheBasics ) 19 | { 20 | CACHE cache(10); 21 | EXPECT_FALSE( cache.pending(0) ); 22 | EXPECT_FALSE( cache.contains(0) ); 23 | EXPECT_EQ( NEEDED, cache.update(0) ); // creating 24 | EXPECT_EQ( NOT_NEEDED, cache.update(0)); // creating 25 | EXPECT_TRUE( cache.pending(0) );// now pending 26 | EXPECT_FALSE( cache.contains(0) );// but still not present 27 | EXPECT_TRUE( cache.put(0,1,-1) );// putting 28 | EXPECT_TRUE( cache.contains(0) );// now present 29 | 30 | EXPECT_EQ( 1u, cache.weight() );// now present 31 | 32 | CACHE::data_type data; 33 | EXPECT_TRUE( cache.get(0, data) );// getting is ok 34 | EXPECT_EQ( -1, data );// data is correct 35 | } 36 | 37 | TEST(Cache, noWeight ) 38 | { 39 | CACHE cache(1); 40 | EXPECT_THROW( cache.put(0,0,-1), std::logic_error ); // no weight 41 | } 42 | 43 | TEST(Cache, putEvenIfNotRequestedCanFit ) 44 | { 45 | CACHE cache(1); 46 | // putting an unneeded element 47 | EXPECT_TRUE( cache.put(5,1,-1) ); 48 | EXPECT_TRUE( cache.contains(5) );// now present 49 | 50 | // a new request would be priority and discard the previous one 51 | cache.update(0);// requesting 0 52 | EXPECT_TRUE( cache.put(0,1,2) );// should be accepted 53 | EXPECT_TRUE( cache.contains(0) );// should be present 54 | EXPECT_FALSE( cache.contains(5) );// should be discarded 55 | } 56 | 57 | TEST(Cache, putEvenIfNotRequestedButCantFit ) 58 | { 59 | CACHE cache(1); 60 | cache.update(0); // requesting 0 61 | EXPECT_TRUE( cache.put(0,2,2) );// should be accepted 62 | EXPECT_TRUE( cache.contains(0) );// should be present 63 | 64 | // putting an unneeded element that can't fit 65 | EXPECT_FALSE( cache.put(5,1,-1) );// not added 66 | EXPECT_FALSE( cache.contains(5) );// not present 67 | } 68 | 69 | TEST(Cache, alreadyInCache ) 70 | { 71 | CACHE cache(1); 72 | cache.update(0); 73 | cache.put(0,1,-1); 74 | EXPECT_THROW( cache.put(0,1,-1), std::logic_error ); // already in cache 75 | } 76 | 77 | TEST(Cache, cacheFull ) 78 | { 79 | CACHE cache(10); 80 | cache.update(0); 81 | cache.update(1); 82 | EXPECT_FALSE( cache.full() ); // not yet full 83 | EXPECT_TRUE( cache.put(0,11,-1) ); 84 | EXPECT_TRUE( cache.full() );// full 85 | EXPECT_EQ( 11u, cache.weight() );// now present 86 | EXPECT_FALSE( cache.put(1,1,1) );// can't put, we're full here 87 | EXPECT_FALSE( cache.contains(1) );// data is not here 88 | CACHE::data_type data; 89 | EXPECT_FALSE( cache.get(1, data) );// can't get 1 90 | } 91 | 92 | TEST(Cache, fullButHigherPriorityDiscardsRequested ) 93 | { 94 | CACHE cache(1); 95 | 96 | // expected [0,1,2] 97 | cache.update(0); 98 | cache.update(1); 99 | cache.update(2); 100 | 101 | // [_,_,2] 102 | EXPECT_TRUE( cache.put(2,2,0) ); // pushing 2 103 | EXPECT_FALSE( cache.full() ); // not full, 0 is not there 104 | EXPECT_TRUE( cache.contains(2) ); 105 | 106 | // [_,1,_] 107 | EXPECT_TRUE( cache.put(1,2,0) ); // pushing 1, removes 2 108 | EXPECT_FALSE( cache.full() ); // not full, 0 is not there 109 | EXPECT_TRUE( cache.contains(1) ); 110 | EXPECT_FALSE( cache.contains(2) ); 111 | 112 | // [0,_,_] 113 | EXPECT_TRUE( cache.put(0,2,0) ); // pushing 0, removes 1 114 | EXPECT_TRUE( cache.full() ); // 0 is here, and cache is full 115 | EXPECT_TRUE( cache.contains(0) ); 116 | EXPECT_FALSE( cache.contains(1) ); 117 | } 118 | 119 | TEST(Cache, discardPendings ) 120 | { 121 | CACHE cache(10); 122 | 123 | cache.update(0); 124 | cache.update(1); 125 | cache.update(3); 126 | // requested [0,1,3], discardable [] 127 | // [_,_,_] [] 128 | 129 | cache.put(1,2,42); 130 | // requested [0,1,3], discardable [] 131 | // [_,X,_] [] 132 | 133 | cache.discardPending(); 134 | // requested [], discardable [0,1,3] 135 | // [] [_,X,_] 136 | 137 | EXPECT_EQ( 2U, cache.weight() ); 138 | // jobs are not pending anymore 139 | EXPECT_FALSE( cache.pending(0) ); 140 | EXPECT_FALSE( cache.pending(1) ); 141 | EXPECT_FALSE( cache.pending(3) ); 142 | // but content is here 143 | EXPECT_TRUE( cache.contains(1) ); 144 | 145 | CACHE::data_type data; 146 | EXPECT_TRUE( cache.get(1, data) ); 147 | EXPECT_EQ( 42, data ); 148 | 149 | EXPECT_EQ( NOT_NEEDED, cache.update(1) ); 150 | // requested [1], discardable [0,3] 151 | // [X] [_,_] 152 | } 153 | -------------------------------------------------------------------------------- /tests/concurrent_queue_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | typedef concurrent::queue IntQueue; 13 | 14 | TEST(ConcurrentQueue,pushPop ) { 15 | IntQueue q; 16 | q.push(5); 17 | int unused; 18 | EXPECT_TRUE( q.tryPop(unused)); 19 | // can pop 20 | EXPECT_EQ( unused, 5); 21 | // value is 5 22 | } 23 | 24 | TEST(ConcurrentQueue, clean ) { 25 | IntQueue q; 26 | q.push(5); 27 | q.clear(); 28 | int unused; 29 | EXPECT_FALSE( q.tryPop(unused)); 30 | // queue is empty 31 | } 32 | 33 | TEST(ConcurrentQueue, drainToCompatible ) { 34 | typedef list IntList; 35 | typedef vector IntVector; 36 | const IntList initialValues = { 5, 2, 3, -1, 6, 9, 10, 55 }; 37 | 38 | { 39 | /** 40 | * Pushing elements one by one 41 | */ 42 | IntQueue queue; 43 | 44 | concurrent::queue_adapter adapted(queue); 45 | copy(initialValues.begin(), initialValues.end(), back_inserter(adapted)); 46 | 47 | IntVector result; 48 | queue.drainTo(result); 49 | 50 | EXPECT_TRUE(equal(initialValues.begin(), initialValues.end(), result.begin())); 51 | int unused; 52 | EXPECT_FALSE( queue.tryPop(unused)); 53 | // queue is empty 54 | } 55 | { 56 | /** 57 | * Pushing elements at once 58 | */ 59 | IntList mutableCopy(initialValues); 60 | IntQueue queue; 61 | queue.drainFrom(mutableCopy); 62 | EXPECT_TRUE( mutableCopy.empty()); 63 | // source is empty 64 | 65 | IntVector result; 66 | queue.drainTo(result); 67 | 68 | EXPECT_TRUE(equal(initialValues.begin(), initialValues.end(), result.begin())); 69 | int unused; 70 | EXPECT_FALSE( queue.tryPop(unused)); 71 | // queue is empty 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /tests/concurrent_slot_tests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * concurrent_slot_tests.cpp 3 | * 4 | * Created on: Mar 21, 2012 5 | * Author: Guillaume Chatelet 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | using namespace concurrent; 13 | 14 | TEST(ConcurrentSlot,uninitialized ) { 15 | bool dummy; 16 | slot uninitialized; 17 | EXPECT_FALSE(uninitialized.tryGet(dummy)); 18 | } 19 | 20 | TEST(ConcurrentSlot, initialized ) { 21 | bool dummy = false; 22 | slot initialized(true); 23 | // taking value 24 | EXPECT_TRUE(initialized.tryGet(dummy)); 25 | EXPECT_TRUE(dummy); 26 | // no more value available 27 | EXPECT_FALSE(initialized.tryGet(dummy)); 28 | } 29 | 30 | TEST(ConcurrentSlot, termination ) { 31 | bool dummy = false; 32 | slot slot(true); 33 | slot.terminate(); 34 | // termination mode, getters should throw 35 | EXPECT_THROW(slot.tryGet(dummy), concurrent::terminated); 36 | EXPECT_THROW(slot.waitGet(dummy), concurrent::terminated); 37 | // back to normal operations 38 | slot.terminate(false); 39 | EXPECT_TRUE(slot.tryGet(dummy)); 40 | EXPECT_TRUE(dummy); 41 | slot.set(false); 42 | slot.waitGet(dummy); 43 | EXPECT_FALSE(dummy); 44 | } 45 | 46 | --------------------------------------------------------------------------------