ResourceManager.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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. enum class eResourceState : uint8_t
  67. {
  68. NOT_LOADED,
  69. LOADING,
  70. LOADED
  71. };
  72. template<typename T>
  73. struct Resource_t
  74. {
  75. using resource_type = T;
  76. Resource_t(gul::uri const & _u) : uri(_u)
  77. {
  78. }
  79. gul::uri const & getUri() const
  80. {
  81. return uri;
  82. }
  83. /**
  84. * @brief isLoaded
  85. * @return
  86. *
  87. * Returns true if the resource has been loaded and is
  88. * available through get();
  89. */
  90. bool isLoaded() const
  91. {
  92. return getState() == eResourceState::LOADED && value.has_value();
  93. }
  94. /**
  95. * @brief emplace_resource
  96. * @param v
  97. *
  98. * Sets the resource data
  99. */
  100. void emplace_resource(T && v)
  101. {
  102. auto L = getLockGuard();
  103. value = std::move(v);
  104. updateLoadTime();
  105. setState(eResourceState::LOADED);
  106. m_dirty = false;
  107. auto it = std::remove_if(m_data->m_onLoadCallbacks.begin(), m_data->m_onLoadCallbacks.end(),[self = m_self.lock()](auto &V)
  108. {
  109. return V(self);
  110. });
  111. m_data->m_onLoadCallbacks.erase(it, m_data->m_onLoadCallbacks.end());
  112. }
  113. /**
  114. * @brief updateLoadTime
  115. * @param loadTime
  116. *
  117. * Updates the load time of the current resource
  118. */
  119. void updateLoadTime(std::chrono::system_clock::time_point t = std::chrono::system_clock::now())
  120. {
  121. m_loadTime = t;
  122. }
  123. /**
  124. * @brief updateAccessTime
  125. * @param t
  126. *
  127. * Updates the resource's access time.
  128. */
  129. void updateAccessTime(std::chrono::system_clock::time_point t = std::chrono::system_clock::now())
  130. {
  131. m_accessTime = t;
  132. }
  133. auto getAccessTime() const
  134. {
  135. return m_accessTime;
  136. }
  137. auto getLoadTime() const
  138. {
  139. return m_loadTime;
  140. }
  141. auto getLoadTime_time_t() const
  142. {
  143. return to_time_t(getLoadTime());
  144. }
  145. /**
  146. * @brief load
  147. * @return
  148. *
  149. * Loads the resource and returns true if the resource was loaded
  150. * and false if the resource has already been loaded.
  151. *
  152. * This function should be called on the main thread.
  153. *
  154. */
  155. bool load()
  156. {
  157. if(!value.has_value())
  158. {
  159. auto f = getBackgroundLoader();
  160. f(); // call it on the same thread
  161. return true;
  162. }
  163. return false;
  164. }
  165. void setState(eResourceState s)
  166. {
  167. m_state = s;
  168. }
  169. eResourceState getState() const
  170. {
  171. return m_state;
  172. }
  173. /**
  174. * @brief isLoading
  175. * @return
  176. *
  177. * Returns if the resource is currently scheduled
  178. * or loading in the background.
  179. */
  180. bool isLoading() const
  181. {
  182. return getState() == eResourceState::LOADING;
  183. }
  184. /**
  185. * @brief unload
  186. *
  187. * Unloads the resource and calls any callback functions
  188. */
  189. void unload()
  190. {
  191. if(isLoaded())
  192. {
  193. auto self = m_self.lock();
  194. auto it = std::remove_if(m_data->m_onUnloadCallbacks.begin(), m_data->m_onUnloadCallbacks.end(),[self = m_self.lock()](auto &V)
  195. {
  196. return V(self);
  197. });
  198. m_data->m_onUnloadCallbacks.erase(it, m_data->m_onUnloadCallbacks.end());
  199. value.reset();
  200. m_unloadLater = false;
  201. }
  202. }
  203. /**
  204. * @brief loadBackground
  205. *
  206. * gets a functional object which can be called on a different thread
  207. * do load the resource in the background.
  208. */
  209. auto getBackgroundLoader()
  210. {
  211. return
  212. [this]()
  213. {
  214. this->setState(eResourceState::LOADING);
  215. auto v = (m_data->m_loader)(uri);
  216. emplace_resource( std::move(v) );
  217. this->setState(eResourceState::LOADED);
  218. };
  219. }
  220. /**
  221. * @brief loadCopy
  222. *
  223. * Call the loader function and return a copy of the object
  224. * that was loaded. This does not modify the resource
  225. */
  226. auto loadCopy() const
  227. {
  228. return (m_data->m_loader)(uri);
  229. }
  230. std::lock_guard<std::mutex> getLockGuard()
  231. {
  232. return std::lock_guard<std::mutex>(m_data->m_mutex);
  233. }
  234. /**
  235. * @brief scheduleUnload
  236. *
  237. * Sets the flag to unload the resource at a later time
  238. *
  239. */
  240. void scheduleUnload()
  241. {
  242. m_unloadLater = true;
  243. }
  244. /**
  245. * @brief get
  246. * @return
  247. *
  248. * Returns a reference to the actual data.
  249. * If the data is not loaded. the loader will be called.
  250. *
  251. */
  252. T & get()
  253. {
  254. if(isLoaded())
  255. {
  256. return *value;
  257. }
  258. if(isLoading())
  259. {
  260. throw std::runtime_error("Resource is currently loading in the background: " + getUri().toString());
  261. }
  262. {
  263. auto b = getBackgroundLoader();
  264. b();
  265. return get();
  266. }
  267. }
  268. std::any const & getUserData() const
  269. {
  270. return m_userData;
  271. }
  272. std::any & getUserData()
  273. {
  274. return m_userData;
  275. }
  276. //================================================================
  277. // User Variables: Variables which can be attached to the resource
  278. // for user-specific things
  279. //================================================================
  280. // Get a reference to the map of variables
  281. std::unordered_map<std::string, std::any> & getUserVars()
  282. {
  283. return m_userVars;
  284. }
  285. std::unordered_map<std::string, std::any> const & getUserVars() const
  286. {
  287. return m_userVars;
  288. }
  289. template<typename V>
  290. V& setUserVar(std::string const & x, V const & val)
  291. {
  292. auto & vv = m_userVars[x];
  293. vv = val;
  294. return std::any_cast<V&>(x);
  295. }
  296. template<typename V>
  297. V& setUserVar(std::string const & x, V && val)
  298. {
  299. auto & vv = m_userVars[x];
  300. vv = std::move(val);
  301. return std::any_cast<V&>(x);
  302. }
  303. std::any const & getUserVar(std::string const & x) const
  304. {
  305. return m_userVars.at(x);
  306. }
  307. std::any & getUserVar(std::string const & x)
  308. {
  309. return m_userVars[x];
  310. }
  311. template<typename V>
  312. V const & getUserVar(std::string const & x) const
  313. {
  314. return std::any_cast<V const&>(getUserVar(x));
  315. }
  316. template<typename V>
  317. V & getUserVar(std::string const & x)
  318. {
  319. return std::any_cast<V&>(getUserVar(x));
  320. }
  321. void eraseUserVar(std::string const & x)
  322. {
  323. m_userVars.erase(x);
  324. }
  325. protected:
  326. wResourceID<resource_type> m_self;
  327. std::shared_ptr<SingleResourceManagerData<resource_type> > m_data;
  328. std::optional<T> value;
  329. gul::uri uri;
  330. std::chrono::system_clock::time_point m_loadTime;
  331. std::chrono::system_clock::time_point m_accessTime = std::chrono::system_clock::now(); // the last time this resource was accessed
  332. std::unordered_map<std::string, std::any> m_userVars;
  333. std::atomic<eResourceState> m_state = {};
  334. bool m_unloadLater = false;
  335. bool m_dirty = true;
  336. //std::atomic<bool> m_isBackgroundLoading = false;
  337. std::any m_userData;
  338. friend struct SingleResourceManager<T>;
  339. };
  340. /**
  341. * @brief The SingleResourceManager struct
  342. *
  343. * Manages a single resource type
  344. */
  345. template<typename T>
  346. struct SingleResourceManager
  347. {
  348. using resource_type = T;
  349. using resource_handle = ResourceID<resource_type>;
  350. SingleResourceManager()
  351. {
  352. m_data = std::make_shared<SingleResourceManagerData<resource_type> >();
  353. }
  354. /**
  355. * @brief findResource
  356. * @param uri
  357. * @return
  358. *
  359. * Finds a resource. Returns a the ResourceID.
  360. * This does not load the resource data. It simly
  361. * returns the handle
  362. */
  363. resource_handle findResource(gul::uri const & uri)
  364. {
  365. std::lock_guard<std::mutex> L(m_data->m_mutex);
  366. auto &r = m_data->m_resources[uri.toString()];
  367. if(!r)
  368. {
  369. r = std::make_shared< Resource_t<T> >(uri);
  370. r->m_data = m_data;
  371. r->m_self = r;
  372. }
  373. return r;
  374. }
  375. resource_handle find(gul::uri const & uri)
  376. {
  377. return findResource(uri);
  378. }
  379. /**
  380. * @brief get
  381. * @param u
  382. * @return
  383. *
  384. * Load a resource from a uri. Returns the reference to the
  385. * resource
  386. */
  387. resource_handle get( gul::uri const & u)
  388. {
  389. auto r = findResource(u);
  390. r->load();
  391. return r;
  392. }
  393. /**
  394. * @brief setLoader
  395. * @param C
  396. *
  397. * Sets the resource loader which can load the uri
  398. * from disk. The signature of the function should be
  399. *
  400. * T [](gul::uri const & _uri)
  401. * {
  402. * }
  403. */
  404. template<typename callable_t>
  405. void setLoader(callable_t && C)
  406. {
  407. m_data->m_loader = std::move(C);
  408. }
  409. /**
  410. * @brief insertOnLoadCallback
  411. * @param v
  412. *
  413. * Sets function which will be called when the resource is emplaced.
  414. * when id->emplace_resource( ) is called
  415. *
  416. * The callback function should return TRUE if the callback function
  417. * should be removed after it is called.
  418. */
  419. void insertOnLoadCallback(std::function<bool( resource_handle )> && v)
  420. {
  421. m_data->m_onLoadCallbacks.push_back(v);
  422. }
  423. /**
  424. * @brief insertOnUnloadCallback
  425. * @param v
  426. *
  427. * Sets a function to be called when a resource is to be unloaded.
  428. * The callback function will be called BEFORE the resource data is removed.
  429. * id->get() will still be able to be called.
  430. *
  431. * The callback function should return TURE if the callback function should
  432. * be removed after it is called.
  433. *
  434. */
  435. void insertOnUnloadCallback(std::function<bool( resource_handle )> && v)
  436. {
  437. m_data->m_onUnloadCallbacks.push_back(v);
  438. }
  439. /**
  440. * @brief processUnload
  441. *
  442. * Checks if any resources can be unloaded and unloads them.
  443. * This will call any onUnload callbacks.
  444. */
  445. void processUnload()
  446. {
  447. for(auto & [a,b] : m_data->m_resources)
  448. {
  449. (void)a;
  450. if(b->m_unloadLater)
  451. {
  452. b->unload();
  453. }
  454. }
  455. }
  456. template<typename callable_t>
  457. void forEach(callable_t && c)
  458. {
  459. for(auto & [a,b] : m_data->m_resources)
  460. {
  461. c(b);
  462. }
  463. }
  464. auto begin()
  465. {
  466. return m_data->m_resources.begin();
  467. }
  468. auto end()
  469. {
  470. return m_data->m_resources.end();
  471. }
  472. size_t count() const
  473. {
  474. return m_data->m_resources.size();
  475. }
  476. protected:
  477. std::shared_ptr<SingleResourceManagerData<resource_type> > m_data;
  478. };
  479. class ResourceManager
  480. {
  481. public:
  482. static auto getFileModifyTime_time_t(fs::path const & p)
  483. {
  484. fs::file_time_type file_time = last_write_time(p);
  485. return to_time_t(file_time);
  486. }
  487. /**
  488. * @brief get
  489. * @param _uri
  490. * @return
  491. *
  492. * Returns a specifc resource. The resource will be loaded if it hasn't already been
  493. */
  494. template<typename T>
  495. ResourceID<T> get(gul::uri const & _uri)
  496. {
  497. auto l = getSingleResourceManager<T>();
  498. return l->get(_uri);
  499. }
  500. /**
  501. * @brief findResource
  502. * @param u
  503. * @return
  504. *
  505. * Finds a specific resource. The resource may or may not be loaded.
  506. */
  507. template<typename T>
  508. typename SingleResourceManager<T>::resource_handle findResource(gul::uri const & u)
  509. {
  510. auto l = getSingleResourceManager<T>();
  511. return l->findResource(u);
  512. }
  513. /**
  514. * @brief setLoader
  515. * @param C
  516. *
  517. * Sets the resource loader for a particular resource.
  518. *
  519. * The functional should have the form:
  520. *
  521. * T (gul::uri const & _uri)
  522. */
  523. template<typename T, typename callable_t>
  524. void setLoader(callable_t && C)
  525. {
  526. auto l = getSingleResourceManager<T>();
  527. l->setLoader(C);
  528. }
  529. template<typename T>
  530. std::shared_ptr< SingleResourceManager<T> > getSingleResourceManager()
  531. {
  532. std::type_index i(typeid (T));
  533. auto & f = m_singleResources[i];
  534. if(!f)
  535. {
  536. f = std::make_shared< SingleResourceManager<T> >();
  537. m_singleResources[i] = std::static_pointer_cast< void >(f);
  538. return getSingleResourceManager<T>();
  539. }
  540. return std::static_pointer_cast< SingleResourceManager<T> >(f);
  541. }
  542. protected:
  543. std::map< std::type_index, std::shared_ptr<void> > m_singleResources;
  544. };
  545. }
  546. #endif