1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3
4 #include "galaxy/GalaxyCache.h"
5
6 #include "Game.h"
7 #include "Pi.h"
8 #include "galaxy/Galaxy.h"
9 #include "galaxy/GalaxyGenerator.h"
10 #include "galaxy/Sector.h"
11 #include "galaxy/StarSystem.h"
12 #include "utils.h"
13 #include <utility>
14
15 //#define DEBUG_CACHE
16
17 //virtual
18
19 template <typename T, typename CompareT>
~GalaxyObjectCache()20 GalaxyObjectCache<T, CompareT>::~GalaxyObjectCache()
21 {
22 for (Slave *s : m_slaves)
23 s->MasterDeleted();
24 assert(m_attic.empty()); // otherwise the objects will deregister at a cache that no longer exists
25 }
26
27 template <typename T, typename CompareT>
AddToCache(std::vector<RefCountedPtr<T>> & objects)28 void GalaxyObjectCache<T, CompareT>::AddToCache(std::vector<RefCountedPtr<T>> &objects)
29 {
30 PROFILE_SCOPED()
31 for (auto it = objects.begin(), itEnd = objects.end(); it != itEnd; ++it) {
32 auto inserted = m_attic.insert(std::make_pair(it->Get()->GetPath(), it->Get()));
33 if (!inserted.second) {
34 it->Reset(inserted.first->second);
35 } else {
36 (*it)->SetCache(this);
37 }
38 }
39 }
40
41 template <typename T, typename CompareT>
GetIfCached(const SystemPath & path)42 RefCountedPtr<T> GalaxyObjectCache<T, CompareT>::GetIfCached(const SystemPath &path)
43 {
44 RefCountedPtr<T> s;
45 typename AtticMap::iterator i = m_attic.find(path);
46 if (i != m_attic.end()) {
47 s.Reset(i->second);
48 }
49
50 return s;
51 }
52
53 template <typename T, typename CompareT>
GetCached(const SystemPath & path)54 RefCountedPtr<T> GalaxyObjectCache<T, CompareT>::GetCached(const SystemPath &path)
55 {
56 RefCountedPtr<T> s = this->GetIfCached(path);
57 if (!s) {
58 ++m_cacheMisses;
59 s = m_galaxy->GetGenerator()->Generate<T, GalaxyObjectCache<T, CompareT>>(RefCountedPtr<Galaxy>(m_galaxy), path, this);
60 m_attic.insert(std::make_pair(path, s.Get()));
61 } else {
62 ++m_cacheHits;
63 }
64 return s;
65 }
66
67 template <typename T, typename CompareT>
HasCached(const SystemPath & path) const68 bool GalaxyObjectCache<T, CompareT>::HasCached(const SystemPath &path) const
69 {
70 PROFILE_SCOPED()
71
72 return (m_attic.find(path) != m_attic.end());
73 }
74
75 template <typename T, typename CompareT>
RemoveFromAttic(const SystemPath & path)76 void GalaxyObjectCache<T, CompareT>::RemoveFromAttic(const SystemPath &path)
77 {
78 m_attic.erase(path);
79 }
80
81 template <typename T, typename CompareT>
ClearCache()82 void GalaxyObjectCache<T, CompareT>::ClearCache()
83 {
84 for (auto it = m_slaves.begin(), itEnd = m_slaves.end(); it != itEnd; ++it)
85 (*it)->ClearCache();
86 }
87
88 template <typename T, typename CompareT>
OutputCacheStatistics(bool reset)89 void GalaxyObjectCache<T, CompareT>::OutputCacheStatistics(bool reset)
90 {
91 Output("%s: misses: %llu, slave hits: %llu, master hits: %llu\n", CACHE_NAME.c_str(), m_cacheMisses, m_cacheHitsSlave, m_cacheHits);
92 if (reset)
93 m_cacheMisses = m_cacheHitsSlave = m_cacheHits = 0;
94 }
95
96 template <typename T, typename CompareT>
NewSlaveCache()97 RefCountedPtr<typename GalaxyObjectCache<T, CompareT>::Slave> GalaxyObjectCache<T, CompareT>::NewSlaveCache()
98 {
99 return RefCountedPtr<Slave>(new Slave(this, RefCountedPtr<Galaxy>(m_galaxy), Pi::GetAsyncJobQueue()));
100 }
101
102 template <typename T, typename CompareT>
Slave(GalaxyObjectCache<T,CompareT> * master,RefCountedPtr<Galaxy> galaxy,JobQueue * jobQueue)103 GalaxyObjectCache<T, CompareT>::Slave::Slave(GalaxyObjectCache<T, CompareT> *master, RefCountedPtr<Galaxy> galaxy, JobQueue *jobQueue) :
104 m_master(master),
105 m_galaxy(galaxy),
106 m_jobs(Pi::GetAsyncJobQueue())
107 {
108 m_master->m_slaves.insert(this);
109 }
110
111 template <typename T, typename CompareT>
MasterDeleted()112 void GalaxyObjectCache<T, CompareT>::Slave::MasterDeleted()
113 {
114 m_master = nullptr;
115 }
116
117 template <typename T, typename CompareT>
GetIfCached(const SystemPath & path)118 RefCountedPtr<T> GalaxyObjectCache<T, CompareT>::Slave::GetIfCached(const SystemPath &path)
119 {
120 typename CacheMap::iterator i = m_cache.find(path);
121 if (i != m_cache.end())
122 return (*i).second;
123 return RefCountedPtr<T>();
124 }
125
126 template <typename T, typename CompareT>
GetCached(const SystemPath & path)127 RefCountedPtr<T> GalaxyObjectCache<T, CompareT>::Slave::GetCached(const SystemPath &path)
128 {
129 PROFILE_SCOPED()
130
131 typename CacheMap::iterator i = m_cache.find(path);
132 if (i != m_cache.end()) {
133 if (m_master)
134 ++m_master->m_cacheHitsSlave;
135 return (*i).second;
136 }
137
138 if (m_master) {
139 auto inserted = m_cache.insert(std::make_pair(path, m_master->GetCached(path)));
140 return inserted.first->second;
141 } else {
142 return RefCountedPtr<T>();
143 }
144 }
145
146 template <typename T, typename CompareT>
Erase(const SystemPath & path)147 void GalaxyObjectCache<T, CompareT>::Slave::Erase(const SystemPath &path) { m_cache.erase(path); }
148
149 template <typename T, typename CompareT>
Erase(const typename CacheMap::const_iterator & it)150 void GalaxyObjectCache<T, CompareT>::Slave::Erase(const typename CacheMap::const_iterator &it) { m_cache.erase(it); }
151
152 template <typename T, typename CompareT>
ClearCache()153 void GalaxyObjectCache<T, CompareT>::Slave::ClearCache() { m_cache.clear(); }
154
155 template <typename T, typename CompareT>
~Slave()156 GalaxyObjectCache<T, CompareT>::Slave::~Slave()
157 {
158 #ifdef DEBUG_CACHE
159 unsigned unique = 0;
160 for (auto it = m_cache.begin(); it != m_cache.end(); ++it)
161 if (it->second->GetRefCount() == 1)
162 unique++;
163 Output("%s: Discarding slave cache with " SIZET_FMT " entries (%u to be removed)\n", CACHE_NAME.c_str(), m_cache.size(), unique);
164 #endif
165 if (m_master)
166 m_master->m_slaves.erase(this);
167 }
168
169 template <typename T, typename CompareT>
AddToCache(std::vector<RefCountedPtr<T>> & objects)170 void GalaxyObjectCache<T, CompareT>::Slave::AddToCache(std::vector<RefCountedPtr<T>> &objects)
171 {
172 if (m_master) {
173 m_master->AddToCache(objects); // This modifies the vector to the sectors already in the master cache
174 for (auto it = objects.begin(), itEnd = objects.end(); it != itEnd; ++it) {
175 m_cache.insert(std::make_pair(it->Get()->GetPath(), *it));
176 }
177 }
178 }
179
180 template <typename T, typename CompareT>
FillCache(const typename GalaxyObjectCache<T,CompareT>::PathVector & paths,typename GalaxyObjectCache<T,CompareT>::CacheFilledCallback callback)181 void GalaxyObjectCache<T, CompareT>::Slave::FillCache(const typename GalaxyObjectCache<T, CompareT>::PathVector &paths,
182 typename GalaxyObjectCache<T, CompareT>::CacheFilledCallback callback)
183 {
184 // allocate some space for what we're about to chunk up
185 std::vector<std::unique_ptr<PathVector>> vec_paths;
186 vec_paths.reserve(paths.size() / CACHE_JOB_SIZE + 1);
187 std::unique_ptr<PathVector> current_paths;
188 #ifdef DEBUG_CACHE
189 size_t alreadyCached = m_cache.size();
190 unsigned masterCached = 0;
191 unsigned toBeCreated = 0;
192 #endif
193
194 // chop the paths into groups of CACHE_JOB_SIZE
195 for (auto it = paths.begin(), itEnd = paths.end(); it != itEnd; ++it) {
196 RefCountedPtr<T> s = m_master->GetIfCached(*it);
197 if (s) {
198 m_cache[*it] = s;
199 #ifdef DEBUG_CACHE
200 ++masterCached;
201 #endif
202 } else {
203 if (!current_paths) {
204 current_paths.reset(new PathVector);
205 current_paths->reserve(CACHE_JOB_SIZE);
206 }
207 current_paths->push_back(*it);
208 if (current_paths->size() >= CACHE_JOB_SIZE) {
209 vec_paths.push_back(std::move(current_paths));
210 }
211 #ifdef DEBUG_CACHE
212 ++toBeCreated;
213 #endif
214 }
215 }
216
217 // catch the last loop in case it's got some entries (could be less than the spread width)
218 if (current_paths) {
219 vec_paths.push_back(std::move(current_paths));
220 }
221
222 #ifdef DEBUG_CACHE
223 Output("%s: FillCache: " SIZET_FMT " cached, %u in master cache, %u to be created, will use " SIZET_FMT " jobs\n", CACHE_NAME.c_str(),
224 alreadyCached, masterCached, toBeCreated, vec_paths.size());
225 #endif
226
227 if (vec_paths.empty()) {
228 if (callback)
229 callback();
230 } else {
231 // now add the batched jobs
232 for (auto it = vec_paths.begin(), itEnd = vec_paths.end(); it != itEnd; ++it)
233 m_jobs.Order(new GalaxyObjectCache<T, CompareT>::CacheJob(std::move(*it), this, m_galaxy, callback));
234 }
235 }
236
237 template <typename T, typename CompareT>
CacheJob(std::unique_ptr<std::vector<SystemPath>> path,typename GalaxyObjectCache<T,CompareT>::Slave * slaveCache,RefCountedPtr<Galaxy> galaxy,typename GalaxyObjectCache<T,CompareT>::CacheFilledCallback callback)238 GalaxyObjectCache<T, CompareT>::CacheJob::CacheJob(std::unique_ptr<std::vector<SystemPath>> path,
239 typename GalaxyObjectCache<T, CompareT>::Slave *slaveCache, RefCountedPtr<Galaxy> galaxy,
240 typename GalaxyObjectCache<T, CompareT>::CacheFilledCallback callback) :
241 Job(),
242 m_paths(std::move(path)),
243 m_slaveCache(slaveCache),
244 m_galaxy(galaxy),
245 m_galaxyGenerator(galaxy->GetGenerator()),
246 m_callback(callback)
247 {
248 m_objects.reserve(m_paths->size());
249 }
250
251 //virtual
252 template <typename T, typename CompareT>
OnRun()253 void GalaxyObjectCache<T, CompareT>::CacheJob::OnRun() // RUNS IN ANOTHER THREAD!! MUST BE THREAD SAFE!
254 {
255 PROFILE_SCOPED()
256 for (auto it = m_paths->begin(), itEnd = m_paths->end(); it != itEnd; ++it)
257 m_objects.push_back(m_galaxyGenerator->Generate<T, GalaxyObjectCache<T, CompareT>>(m_galaxy, *it, nullptr));
258 }
259
260 //virtual
261 template <typename T, typename CompareT>
OnFinish()262 void GalaxyObjectCache<T, CompareT>::CacheJob::OnFinish() // runs in primary thread of the context
263 {
264 PROFILE_SCOPED()
265 m_slaveCache->AddToCache(m_objects);
266 if (m_slaveCache->m_jobs.IsEmpty() && m_callback)
267 m_callback();
268 }
269
270 /****** SectorCache ******/
271
272 template <>
273 const std::string GalaxyObjectCache<Sector, SystemPath::LessSectorOnly>::CACHE_NAME("SectorCache");
274
275 template class GalaxyObjectCache<Sector, SystemPath::LessSectorOnly>;
276
277 /****** StarSystemCache ******/
278
279 template <>
Slave(GalaxyObjectCache<StarSystem,SystemPath::LessSystemOnly> * master,RefCountedPtr<Galaxy> galaxy,JobQueue * jobQueue)280 GalaxyObjectCache<StarSystem, SystemPath::LessSystemOnly>::Slave::Slave(GalaxyObjectCache<StarSystem, SystemPath::LessSystemOnly> *master, RefCountedPtr<Galaxy> galaxy, JobQueue *jobQueue) :
281 m_master(master),
282 m_galaxy(galaxy),
283 m_jobs(Pi::GetSyncJobQueue())
284 {
285 m_master->m_slaves.insert(this);
286 }
287
288 template <>
289 const std::string GalaxyObjectCache<StarSystem, SystemPath::LessSystemOnly>::CACHE_NAME("StarSystemCache");
290
291 template class GalaxyObjectCache<StarSystem, SystemPath::LessSystemOnly>;
292