ResourceManager.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. #ifndef GUL_RESOURCE_MANAGER_H
  2. #define GUL_RESOURCE_MANAGER_H
  3. #include "uri.h"
  4. #include <chrono>
  5. #include <typeindex>
  6. #include <mutex>
  7. #include <optional>
  8. #include <atomic>
  9. #include <any>
  10. #include <map>
  11. #include <unordered_map>
  12. // C++17 includes the <filesystem> library, but
  13. // unfortunately gcc7 does not have a finalized version of it
  14. // it is in the <experimental/filesystem lib
  15. // this section includes the proper header
  16. // depending on whether the header exists and
  17. // includes that. It also sets the
  18. // nfcbn::nf namespace
  19. #if __has_include(<filesystem>)
  20. #include <filesystem>
  21. namespace gul
  22. {
  23. namespace fs = std::filesystem;
  24. }
  25. #elif __has_include(<experimental/filesystem>)
  26. #include <experimental/filesystem>
  27. namespace gul
  28. {
  29. namespace fs = std::experimental::filesystem;
  30. }
  31. #else
  32. #error There is no <filesystem> or <experimental/filesystem>
  33. #endif
  34. namespace gul
  35. {
  36. template <typename TP>
  37. inline std::time_t to_time_t(TP tp)
  38. {
  39. using namespace std::chrono;
  40. auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now()
  41. + system_clock::now());
  42. return system_clock::to_time_t(sctp);
  43. }
  44. template<typename T>
  45. struct SingleResourceManager;
  46. template<typename T>
  47. struct Resource_t;
  48. template<typename T>
  49. using ResourceID = std::shared_ptr<Resource_t<T>>;
  50. template<typename T>
  51. using wResourceID = std::weak_ptr<Resource_t<T>>;
  52. template<typename T>
  53. struct SingleResourceManagerData
  54. {
  55. using resource_type = T;
  56. using resource_handle = ResourceID<resource_type>;
  57. using loader_function = std::function<T(gul::uri const &)>;
  58. using unloader_function = std::function<void(resource_handle)>;
  59. std::vector< std::function<bool(resource_handle)> > m_onLoadCallbacks;
  60. std::vector< std::function<bool(resource_handle)> > m_onUnloadCallbacks;
  61. loader_function m_loader;
  62. unloader_function m_unloader;
  63. std::unordered_map<std::string, ResourceID<T>> m_resources;
  64. std::mutex m_mutex;
  65. };
  66. template<typename T>
  67. struct Resource_t
  68. {
  69. using resource_type = T;
  70. Resource_t(gul::uri const & _u) : uri(_u)
  71. {
  72. }
  73. gul::uri const & getUri() const
  74. {
  75. return uri;
  76. }
  77. /**
  78. * @brief isLoaded
  79. * @return
  80. *
  81. * Returns true if the resource has been loaded and is
  82. * available through get();
  83. */
  84. bool isLoaded() const
  85. {
  86. return value.has_value();
  87. }
  88. /**
  89. * @brief emplace_resource
  90. * @param v
  91. *
  92. * Ssets the resource data
  93. */
  94. void emplace_resource(T && v)
  95. {
  96. auto L = getLockGuard();
  97. value = std::move(v);
  98. updateLoadTime();
  99. setIsLoading(false);
  100. m_dirty = false;
  101. }
  102. /**
  103. * @brief updateLoadTime
  104. * @param loadTime
  105. *
  106. * Updates the load time of the current resource
  107. */
  108. void updateLoadTime(std::chrono::system_clock::time_point t = std::chrono::system_clock::now())
  109. {
  110. m_loadTime = t;
  111. }
  112. /**
  113. * @brief updateAccessTime
  114. * @param t
  115. *
  116. * Updates the resource's access time.
  117. */
  118. void updateAccessTime(std::chrono::system_clock::time_point t = std::chrono::system_clock::now())
  119. {
  120. m_accessTime = t;
  121. }
  122. auto getAccessTime() const
  123. {
  124. return m_accessTime;
  125. }
  126. /**
  127. * @brief load
  128. * @return
  129. *
  130. * Loads the resource and returns true if the resource was loaded
  131. * and false if the resource has already been loaded.
  132. *
  133. * This function should be called on the main thread.
  134. *
  135. */
  136. bool load()
  137. {
  138. if(!value.has_value())
  139. {
  140. auto f = getBackgroundLoader();
  141. f(); // call it on the same thread
  142. return true;
  143. }
  144. return false;
  145. }
  146. /**
  147. * @brief isLoading
  148. * @return
  149. *
  150. * Returns if the resource is currently scheduled
  151. * or loading in the background.
  152. */
  153. bool isLoading() const
  154. {
  155. return m_isBackgroundLoading;
  156. }
  157. void setIsLoading(bool t)
  158. {
  159. m_isBackgroundLoading.store(t);
  160. }
  161. /**
  162. * @brief unload
  163. *
  164. * Unloads the resource and calls any callback functions
  165. */
  166. void unload()
  167. {
  168. if(isLoaded())
  169. {
  170. auto self = m_self.lock();
  171. auto it = std::remove_if(m_data->m_onUnloadCallbacks.begin(), m_data->m_onUnloadCallbacks.end(),[self = m_self.lock()](auto &V)
  172. {
  173. return V(self);
  174. });
  175. m_data->m_onUnloadCallbacks.erase(it, m_data->m_onUnloadCallbacks.end());
  176. value.reset();
  177. m_unloadLater = false;
  178. }
  179. }
  180. /**
  181. * @brief loadBackground
  182. *
  183. * gets a functional object which can be called on a different thread
  184. * do load the resource in the background.
  185. */
  186. auto getBackgroundLoader()
  187. {
  188. return
  189. [this]()
  190. {
  191. this->setIsLoading(true);
  192. auto v = (m_data->m_loader)(uri);
  193. emplace_resource( std::move(v) );
  194. this->setIsLoading(false);
  195. auto it = std::remove_if(m_data->m_onLoadCallbacks.begin(), m_data->m_onLoadCallbacks.end(),[self = m_self.lock()](auto &V)
  196. {
  197. return V(self);
  198. });
  199. m_data->m_onLoadCallbacks.erase(it, m_data->m_onLoadCallbacks.end());
  200. };
  201. }
  202. /**
  203. * @brief loadCopy
  204. *
  205. * Call the loader function and return a copy of the object
  206. * that was loaded. This does not modify the resource
  207. */
  208. auto loadCopy() const
  209. {
  210. return (m_data->m_loader)(uri);
  211. }
  212. std::lock_guard<std::mutex> getLockGuard()
  213. {
  214. return std::lock_guard<std::mutex>(m_data->m_mutex);
  215. }
  216. auto getLoadTime() const
  217. {
  218. return m_loadTime;
  219. }
  220. auto getLoadTime_time_t() const
  221. {
  222. return to_time_t(getLoadTime());
  223. }
  224. /**
  225. * @brief scheduleUnload
  226. *
  227. * Sets the flag to unload the resource at a later time
  228. *
  229. */
  230. void scheduleUnload()
  231. {
  232. m_unloadLater = true;
  233. }
  234. /**
  235. * @brief get
  236. * @return
  237. *
  238. * Returns a reference to the actual data.
  239. * If the data is not loaded. the loader will be called.
  240. *
  241. */
  242. T & get()
  243. {
  244. if(m_isBackgroundLoading)
  245. {
  246. throw std::runtime_error("Resource is currently loading in the background.");
  247. }
  248. if(!value.has_value())
  249. {
  250. value = (m_data->m_loader)(uri);
  251. }
  252. return *value;
  253. }
  254. std::any const & getUserData() const
  255. {
  256. return m_userData;
  257. }
  258. std::any & getUserData()
  259. {
  260. return m_userData;
  261. }
  262. //================================================================
  263. // User Variables: Variables which can be attached to the resource
  264. // for user-specific things
  265. //================================================================
  266. template<typename V>
  267. V& setUserVar(std::string const & x, V const & val)
  268. {
  269. auto & vv = m_userVars[x];
  270. vv = val;
  271. return std::any_cast<V&>(x);
  272. }
  273. template<typename V>
  274. V& setUserVar(std::string const & x, V && val)
  275. {
  276. auto & vv = m_userVars[x];
  277. vv = std::move(val);
  278. return std::any_cast<V&>(x);
  279. }
  280. template<typename V>
  281. std::any const & getUserVar(std::string const & x) const
  282. {
  283. return m_userVars.at(x);
  284. }
  285. template<typename V>
  286. std::any & getUserVar(std::string const & x)
  287. {
  288. return m_userVars[x];
  289. }
  290. template<typename V>
  291. V const & getUserVar(std::string const & x) const
  292. {
  293. return std::any_cast<V const&>(getUserVar(x));
  294. }
  295. template<typename V>
  296. V & getUserVar(std::string const & x)
  297. {
  298. return std::any_cast<V&>(getUserVar(x));
  299. }
  300. void eraseUserVar(std::string const & x)
  301. {
  302. m_userVars.erase(x);
  303. }
  304. std::unordered_map<std::string, std::any> const & getUserVars()
  305. {
  306. return m_userVars;
  307. }
  308. std::unordered_map<std::string, std::any> const & getUserVars() const
  309. {
  310. return m_userVars;
  311. }
  312. protected:
  313. wResourceID<resource_type> m_self;
  314. std::shared_ptr<SingleResourceManagerData<resource_type> > m_data;
  315. std::optional<T> value;
  316. gul::uri uri;
  317. std::chrono::system_clock::time_point m_loadTime;
  318. std::chrono::system_clock::time_point m_accessTime = std::chrono::system_clock::now(); // the last time this resource was accessed
  319. //std::vector< std::function<bool(void)> > m_onLoadCallbacks;
  320. std::unordered_map<std::string, std::any> m_userVars;
  321. bool m_unloadLater = false;
  322. bool m_dirty = true;
  323. std::atomic<bool> m_isBackgroundLoading = false;
  324. std::any m_userData;
  325. friend struct SingleResourceManager<T>;
  326. };
  327. /**
  328. * @brief The SingleResourceManager struct
  329. *
  330. * Manages a single resource type
  331. */
  332. template<typename T>
  333. struct SingleResourceManager
  334. {
  335. using resource_type = T;
  336. using resource_handle = ResourceID<resource_type>;
  337. SingleResourceManager()
  338. {
  339. m_data = std::make_shared<SingleResourceManagerData<resource_type> >();
  340. }
  341. /**
  342. * @brief findResource
  343. * @param uri
  344. * @return
  345. *
  346. * Finds a resource. Returns a the ResourceID.
  347. * This does not load the resource data. It simly
  348. * returns the handle
  349. */
  350. resource_handle findResource(gul::uri const & uri)
  351. {
  352. std::lock_guard<std::mutex> L(m_data->m_mutex);
  353. auto &r = m_data->m_resources[uri.toString()];
  354. if(!r)
  355. {
  356. r = std::make_shared< Resource_t<T> >(uri);
  357. r->m_data = m_data;
  358. r->m_self = r;
  359. }
  360. return r;
  361. }
  362. resource_handle find(gul::uri const & uri)
  363. {
  364. return findResource(uri);
  365. }
  366. /**
  367. * @brief get
  368. * @param u
  369. * @return
  370. *
  371. * Load a resource from a uri. Returns the reference to the
  372. * resource
  373. */
  374. resource_handle get( gul::uri const & u)
  375. {
  376. auto r = findResource(u);
  377. r->load();
  378. return r;
  379. }
  380. /**
  381. * @brief setLoader
  382. * @param C
  383. *
  384. * Sets the resource loader which can load the uri
  385. * from disk. The signature of the function should be
  386. *
  387. * T [](gul::uri const & _uri)
  388. * {
  389. * }
  390. */
  391. template<typename callable_t>
  392. void setLoader(callable_t && C)
  393. {
  394. m_data->m_loader = std::move(C);
  395. }
  396. void insertOnLoadCallback(std::function<bool( resource_handle )> && v)
  397. {
  398. m_data->m_onLoadCallbacks.push_back(v);
  399. }
  400. void insertOnUnloadCallback(std::function<bool( resource_handle )> && v)
  401. {
  402. m_data->m_onUnloadCallbacks.push_back(v);
  403. }
  404. /**
  405. * @brief processUnload
  406. *
  407. * Checks if any resources can be unloaded and unloads them.
  408. * This will call any onUnload callbacks.
  409. */
  410. void processUnload()
  411. {
  412. for(auto & [a,b] : m_data->m_resources)
  413. {
  414. (void)a;
  415. if(b->m_unloadLater)
  416. {
  417. b->unload();
  418. }
  419. }
  420. }
  421. template<typename callable_t>
  422. void forEach(callable_t && c)
  423. {
  424. for(auto & [a,b] : m_data->m_resources)
  425. {
  426. c(b);
  427. }
  428. }
  429. auto begin()
  430. {
  431. return m_data->m_resources.begin();
  432. }
  433. auto end()
  434. {
  435. return m_data->m_resources.end();
  436. }
  437. protected:
  438. std::shared_ptr<SingleResourceManagerData<resource_type> > m_data;
  439. };
  440. class ResourceManager
  441. {
  442. public:
  443. static auto getFileModifyTime_time_t(fs::path const & p)
  444. {
  445. fs::file_time_type file_time = last_write_time(p);
  446. return to_time_t(file_time);
  447. }
  448. /**
  449. * @brief get
  450. * @param _uri
  451. * @return
  452. *
  453. * Returns a specifc resource. The resource will be loaded if it hasn't already been
  454. */
  455. template<typename T>
  456. ResourceID<T> get(gul::uri const & _uri)
  457. {
  458. auto l = getSingleResourceManager<T>();
  459. return l->get(_uri);
  460. }
  461. /**
  462. * @brief findResource
  463. * @param u
  464. * @return
  465. *
  466. * Finds a specific resource. The resource may or may not be loaded.
  467. */
  468. template<typename T>
  469. typename SingleResourceManager<T>::resource_handle findResource(gul::uri const & u)
  470. {
  471. auto l = getSingleResourceManager<T>();
  472. return l->findResource(u);
  473. }
  474. /**
  475. * @brief setLoader
  476. * @param C
  477. *
  478. * Sets the resource loader for a particular resource.
  479. *
  480. * The functional should have the form:
  481. *
  482. * T (gul::uri const & _uri)
  483. */
  484. template<typename T, typename callable_t>
  485. void setLoader(callable_t && C)
  486. {
  487. auto l = getSingleResourceManager<T>();
  488. l->setLoader(C);
  489. }
  490. template<typename T>
  491. std::shared_ptr< SingleResourceManager<T> > getSingleResourceManager()
  492. {
  493. std::type_index i(typeid (T));
  494. auto & f = m_singleResources[i];
  495. if(!f)
  496. {
  497. f = std::make_shared< SingleResourceManager<T> >();
  498. m_singleResources[i] = std::static_pointer_cast< void >(f);
  499. return getSingleResourceManager<T>();
  500. }
  501. return std::static_pointer_cast< SingleResourceManager<T> >(f);
  502. }
  503. protected:
  504. std::map< std::type_index, std::shared_ptr<void> > m_singleResources;
  505. };
  506. }
  507. #endif