Cache.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. #define DELAY_REMOVE_STEP (1.0f/8) // number of steps per 'delay_remove' time to check for element removal
  5. ASSERT(OFFSET(Cache<Image>::Elm, data)==0); // '_data_offset' is not used because it's assumed to be always 0
  6. static SyncLock DelayRemoveLock;
  7. static UIntPtr DelayRemoveThreadID;
  8. static Flt DelayRemoveWaited;
  9. /******************************************************************************
  10. We must unlock 'D._lock' everywhere before we want to lock 'T._lock'
  11. This is to avoid deadlocks, when one thread locks first 'D._lock' and then 'T._lock'
  12. and another thread locks first 'T._lock' and then 'D._lock'
  13. For example:
  14. -background loading thread calls (Images._lock, D._lock.on)
  15. -inside 'Draw' during drawing we load an image (D._lock.on, Images._lock) -> deadlock
  16. Instead of it, during drawing it will be: D._lock.on, D._lock.off, Images.lock and unlocks...
  17. We have to do this even if we're not going to perform and 'D' operations:
  18. -background loading thread calls (Images._lock, D._lock.on)
  19. -during 'Draw' drawing (where D is already locked) we use 'find' (D._lock.on, Images._lock) -> deadlock
  20. Normally 'Desc.delay_remove' should be stored as 'Dbl', however we store it as 'Flt',
  21. to reduce memory usage (because it's stored for every cache element and it can be a lot of them).
  22. We can do this because we don't need high precision for this, as typically the delay times are set to several seconds,
  23. and the 'Flt' error after 1 month of application run-time is 0.25s and after 1 year is 2s (see "Timer.cpp" file).
  24. These errors are acceptable.
  25. When unloading elements in 'processDelayRemove' we store how much each element waited in the global 'DelayRemoveWaited'
  26. so its every child when unloading elements can use that time.
  27. TODO: However if that child element was loaded and unloaded for extra cache elm ptr handle just before 'processDelayRemove' was called,
  28. then it'll still going to be unloaded faster, because we store remove time only when the last handle is released.
  29. To avoid this we would probably have to store remove times in elements,
  30. but that would increase memory usage (all elements would need it, while now only those need it in the removal queue).
  31. /******************************************************************************/
  32. C _Cache::Desc& _Cache::lockedDesc(Int i)C {return elmDesc(*_order[i]);}
  33. CPtr _Cache::lockedData(Int i)C {return elmData(*_order[i]);}
  34. _Cache::_Cache(CChar8 *name, Int block_elms, Bool (*load)(Ptr data, C Str &file)) : _memx(block_elms)
  35. {
  36. _d_lock=0;
  37. _case_sensitive=false;
  38. _elms=0;
  39. /*_data_offset=*/_desc_offset=0;
  40. _mode=CACHE_EXIT;
  41. _debug_name=name;
  42. _order=null;
  43. _user =null;
  44. _load =load;
  45. _load_user=null;
  46. _can_be_removed=null;
  47. _delay_remove_counter=0; _delay_remove_time=0; _delay_remove_check=0;
  48. }
  49. void _Cache::clear ( ) {SyncUnlocker unlocker(D._lock); SyncLocker locker(_lock); _elms=0; _memx.clear(); _delay_remove.clear();} // here can't Free '_order' because it assumes that its size is '_memx.maxElms()'
  50. void _Cache::del ( ) {SyncUnlocker unlocker(D._lock); SyncLocker locker(_lock); _elms=0; _memx.del (); _delay_remove.del (); Free(_order);}
  51. Byte _Cache::mode (Byte mode ) {Byte old_mode=T._mode; T._mode=mode; return old_mode;}
  52. void _Cache::caseSensitive(Bool sensitive ) {T._case_sensitive=sensitive;}
  53. void _Cache::setLoadUser (Bool (*load_user)(Ptr data, C Str &file, Ptr user), Ptr user) {T._user=user; T._load=null; T._load_user=load_user;}
  54. void _Cache::delayRemove (Flt time )
  55. {
  56. Bool adjust_existing=true;
  57. MAX(time, 0);
  58. if( time!=_delay_remove_time)
  59. {
  60. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  61. SyncLocker locker( _lock);
  62. Flt delta=time-_delay_remove_time; // how much are we increasing the delay
  63. _delay_remove_time =time; // set new value
  64. _delay_remove_check+=delta*DELAY_REMOVE_STEP; // adjust check time
  65. if(adjust_existing)REPAO(_delay_remove).time+=delta; // adjust element removal times
  66. update();
  67. }
  68. }
  69. /******************************************************************************/
  70. void _Cache::lock()C
  71. {
  72. Byte d_locked=0;
  73. if(!_lock.owned()) // if we don't have the '_lock' yet
  74. {
  75. #if SYNC_UNLOCK_SINGLE
  76. if(D._lock.owned()){d_locked=true; D._lock.off();} // if we own the 'D._lock' then disable it and remember that we had it
  77. #else
  78. for(; D._lock.owned(); d_locked++)D._lock.off(); // if we own the 'D._lock' then disable it and remember that we had it
  79. #endif
  80. }
  81. _lock.on();
  82. if(d_locked)_d_lock=d_locked; // if we've disabled 'D._lock' then set how many times
  83. }
  84. void _Cache::unlock()C
  85. {
  86. Byte d_locked=_d_lock; // remember if we had 'D._lock' disabled
  87. _d_lock =0; // disable before releasing '_lock'
  88. _lock.off();
  89. if(!_lock.owned()) // if we no longer own '_lock' then we can restore 'D._lock' if we disabled it
  90. {
  91. #if SYNC_UNLOCK_SINGLE
  92. if(d_locked)D._lock.on();
  93. #else
  94. REP(d_locked)D._lock.on();
  95. #endif
  96. }else // if we still own '_lock' then we should keep the variable about 'D._lock'
  97. {
  98. _d_lock=d_locked;
  99. }
  100. }
  101. /******************************************************************************/
  102. _Cache::Elm* _Cache::findExact(CChar *file, Int &stop)
  103. {
  104. Int l=0, r=_elms; for(; l<r; )
  105. {
  106. Int mid =UInt(l+r)/2,
  107. compare=ComparePath(file, elmDesc(*_order[mid]).file(), _case_sensitive);
  108. if(!compare)
  109. {
  110. stop=mid;
  111. return _order[mid];
  112. }
  113. if(compare<0)r=mid;
  114. else l=mid+1;
  115. }
  116. stop=l;
  117. return null;
  118. }
  119. /******************************************************************************/
  120. _Cache::Elm* _Cache::findElm(CChar *file, CChar *path)
  121. {
  122. Int stop;
  123. Elm *elm;
  124. if(Is(path) && !FullPath(file)) // try using "path+file" (precisely "SkipStartPath(path, DataPath()) + file"), this needs to be done first, in case there's a "file.dat" and "folder/file.dat" loaded, but we're looking for "file.dat" with "path=folder", this is also needed when loading sub-assets for a file at its location when no 'DataPath' was specified, for example, loading a material for a mesh: "file=material", path="c:/project/data"
  125. {
  126. CChar *rel_path=_SkipStartPath(path, DataPath()); // skip the 'DataPath' here, because loading assets assumes to be done relative to 'DataPath' whenever possible, so if the assets were loaded from 'DataPath' then their path will not include the 'DataPath'
  127. if(Is( rel_path)) // if the path is empty then don't bother checking this, as it's the same as "findExact(file)" below
  128. {
  129. Char path_file[MAX_LONG_PATH]; MergePath(path_file, rel_path, file);
  130. if(elm=findExact(path_file, stop))return elm;
  131. }
  132. }
  133. if(elm=findExact(file , stop))return elm; // try using 'file'
  134. if(DataPath().is())if(CChar *after=_AfterPath(file, DataPath()))return findExact(after, stop); // try using 'file' without the 'DataPath' if it was specified
  135. return null;
  136. }
  137. /******************************************************************************/
  138. Int _Cache::findDelayRemove(Elm &elm)
  139. {
  140. REPA(_delay_remove)if(_delay_remove[i].elm==&elm)return i;
  141. return -1;
  142. }
  143. /******************************************************************************/
  144. void _Cache::addToOrder(Elm &elm)
  145. {
  146. Int stop; findExact(elmDesc(elm).file(), stop);
  147. MoveFastN(_order+stop+1, _order+stop, _elms-stop); // faster version of: for(Int i=_elms; i>stop; i--)_order[i]=_order[i-1];
  148. _order[stop]=&elm; _elms++;
  149. }
  150. void _Cache::removeFromOrder(Elm &elm)
  151. {
  152. Int stop; if(findExact(elmDesc(elm).file(), stop))
  153. {
  154. _elms--; MoveFastN(_order+stop, _order+stop+1, _elms-stop); // faster version of: for(Int i=stop; i<_elms; i++)_order[i]=_order[i+1];
  155. }
  156. }
  157. /******************************************************************************/
  158. Ptr _Cache::validElmData(Elm &elm, Bool counted)
  159. {
  160. Desc &desc=elmDesc(elm);
  161. if( !(desc.flag&CACHE_ELM_LOADING))
  162. {
  163. if(counted)desc.ptr_num++;else FlagEnable(desc.flag, CACHE_ELM_STD_PTR);
  164. return elmData(elm);
  165. }
  166. return null;
  167. }
  168. /******************************************************************************/
  169. Ptr _Cache::_find(CChar *file, CChar *path, Bool counted)
  170. {
  171. if(Is(file)) // valid file name
  172. {
  173. // lock
  174. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  175. SyncLocker locker( _lock);
  176. if(Elm *elm=findElm(file, path))return validElmData(*elm, counted);
  177. }
  178. return null;
  179. }
  180. /******************************************************************************/
  181. inline static CChar* HelperPath(CChar *file, CChar *path, Char (&dest)[MAX_LONG_PATH]=ConstCast(TempChar<MAX_LONG_PATH>()).c)
  182. {
  183. MergePath(dest, _SkipStartPath(path, DataPath()), file);
  184. return dest;
  185. }
  186. Ptr _Cache::_get(CChar *file, CChar *path, Bool counted)
  187. {
  188. if(Is(file)) // valid file name
  189. {
  190. SyncUnlocker unlocker(D._lock);
  191. SyncLocker locker( _lock);
  192. // find existing
  193. if(Elm *elm=findElm(file, path))return validElmData(*elm, counted);
  194. // always null
  195. if(_mode==CACHE_ALL_NULL)return null;
  196. // new element
  197. Int max_elms =_memx.maxElms(); Elm &elm=_memx.New(); Desc &desc=elmDesc(elm); Ptr data=elmData(elm); desc.ptr_num=(counted ? 1 : 0); desc.flag=(counted ? 0 : CACHE_ELM_STD_PTR);
  198. if( max_elms!=_memx.maxElms())Realloc(_order, _memx.maxElms(), max_elms);
  199. file=_SkipStartPath(file, DataPath());
  200. // always dummy
  201. if(_mode==CACHE_ALL_DUMMY)
  202. {
  203. dummy:
  204. desc.flag|=CACHE_ELM_DUMMY;
  205. if(Is(path)){desc.file=HelperPath(file, path); if(!FExist(desc.file) && FExist(file))desc.file=file;} // if a file does not exist at path, but does exist at absolute, then use absolute
  206. else desc.file=file; // use absolute when no path available
  207. addToOrder(elm);
  208. return data;
  209. }else
  210. if(_mode==CACHE_DUMMY_NULL) // dummy on exist, null on fail
  211. {
  212. Bool exist=false;
  213. if(Is(path)){desc.file=HelperPath(file, path); if(FExist(desc.file)) exist=true; }
  214. if( !exist ){ if(FExist( file)){desc.file=file; exist=true;}}
  215. if( exist )
  216. {
  217. desc.flag|=CACHE_ELM_DUMMY;
  218. addToOrder(elm);
  219. return data;
  220. }
  221. _memx.removeData(&elm);
  222. return null;
  223. }
  224. // load
  225. {
  226. desc.flag|=CACHE_ELM_LOADING;
  227. // in path
  228. if(Is(path))
  229. {
  230. desc.file=HelperPath(file, path); // set file before loading
  231. addToOrder(elm);
  232. // try to load
  233. if(_load ? _load(data, desc.file) : _load_user(data, desc.file, _user))
  234. {
  235. ok:
  236. FlagDisable(desc.flag, CACHE_ELM_LOADING);
  237. return data;
  238. }
  239. removeFromOrder(elm); // !! don't try to optimize by using the same 'stop' calculated from 'addToOrder' as it may have changed, because inside 'load' there could be other objects loaded !!
  240. }
  241. // absolute
  242. {
  243. desc.file=file; // set file before loading
  244. addToOrder(elm);
  245. // try to load
  246. if(_load ? _load(data, desc.file) : _load_user(data, desc.file, _user))goto ok;
  247. removeFromOrder(elm); // !! don't try to optimize by using the same 'stop' calculated from 'addToOrder' as it may have changed, because inside 'load' there could be other objects loaded !!
  248. }
  249. FlagDisable(desc.flag, CACHE_ELM_LOADING);
  250. }
  251. // dummy on fail
  252. if(_mode==CACHE_DUMMY)goto dummy;
  253. // null
  254. _memx.removeData(&elm);
  255. }
  256. return null;
  257. }
  258. /******************************************************************************/
  259. Ptr _Cache::_require(CChar *file, CChar *path, Bool counted)
  260. {
  261. if(Ptr data=_get(file, path, counted))return data;
  262. if(Is(file) && _mode==CACHE_EXIT)
  263. {
  264. Str error =MLT(S+"Can't load "+_debug_name+" \""+file+'"', PL,S+u"Nie można wczytać \"" +file+'"');
  265. if(Is(path))error+=MLT(S+"\nAdditional path \"" +path+'"', PL,S+u"\nDodatkowa ścieżka \""+path+'"');
  266. Exit(error);
  267. }
  268. return null;
  269. }
  270. /******************************************************************************/
  271. Ptr _Cache::_find (C UID &id, CChar *path, Bool counted) {return id.valid() ? _find (_EncodeFileName(id), path, counted) : null;}
  272. Ptr _Cache::_get (C UID &id, CChar *path, Bool counted) {return id.valid() ? _get (_EncodeFileName(id), path, counted) : null;}
  273. Ptr _Cache::_require(C UID &id, CChar *path, Bool counted) {return id.valid() ? _require(_EncodeFileName(id), path, counted) : null;}
  274. /******************************************************************************
  275. INLINE Int _Cache::absIndex(CPtr data)C // this function assumes that 'data' is not null
  276. {
  277. #if 1 // fast, this can work because '_data_offset' is now zero
  278. DEBUG_ASSERT(_data_offset==0, "Cache data offset should be zero but it isn't");
  279. return _memx.absIndex(dataElm(data));
  280. #else // safe
  281. #if WINDOWS
  282. __try {return _memx.absIndex(dataElm(data));} // use 'absIndex' only when exception control is possible
  283. __except(EXCEPTION_EXECUTE_HANDLER) {return -1;}
  284. #else
  285. return _memx._abs.index(data);
  286. #endif
  287. #endif
  288. }
  289. /******************************************************************************/
  290. Bool _Cache::_contains(CPtr data)C
  291. {
  292. if(C Elm *elm=dataElm(data))
  293. {
  294. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  295. SyncLocker locker( _lock);
  296. if(contains(elm))if(!(elmDesc(*elm).flag&CACHE_ELM_LOADING))return true;
  297. }
  298. return false;
  299. }
  300. Bool _Cache::_dummy(CPtr data)C
  301. {
  302. if(C Elm *elm=dataElm(data))
  303. {
  304. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  305. SyncLocker locker( _lock);
  306. if(contains(elm))if(elmDesc(*elm).flag&CACHE_ELM_DUMMY)return true;
  307. }
  308. return false;
  309. }
  310. void _Cache::_dummy(CPtr data, Bool dummy)
  311. {
  312. if(Elm *elm=dataElm(data))
  313. {
  314. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  315. SyncLocker locker( _lock);
  316. if(contains(elm))FlagSet(elmDesc(*elm).flag, CACHE_ELM_DUMMY, dummy);
  317. }
  318. }
  319. Int _Cache::_ptrCount(CPtr data)C
  320. {
  321. if(C Elm *elm=dataElm(data))
  322. {
  323. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  324. SyncLocker locker( _lock);
  325. if(contains(elm))return elmDesc(*elm).ptr_num;
  326. }
  327. return -1;
  328. }
  329. CChar* _Cache::_name(CPtr data, CChar *path)C
  330. {
  331. if(C Elm *elm=dataElm(data))
  332. {
  333. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  334. SyncLocker locker( _lock);
  335. if(contains(elm))
  336. {
  337. C Desc &desc=elmDesc(*elm);
  338. if(!(desc.flag&CACHE_ELM_LOADING)) // name may change while loading
  339. {
  340. if(Is(path))return _SkipStartPath(desc.file(), _SkipStartPath(path, DataPath())); // must be '_SkipStartPath' because we're returning CChar*
  341. return desc.file();
  342. }
  343. }
  344. }
  345. return null;
  346. }
  347. UID _Cache::_id(CPtr data)C
  348. {
  349. if(C Elm *elm=dataElm(data))
  350. {
  351. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  352. SyncLocker locker( _lock);
  353. if(contains(elm))return FileNameID(elmDesc(*elm).file()); // ID does not change while loading, so ignore CACHE_ELM_LOADING
  354. }
  355. return UIDZero;
  356. }
  357. /******************************************************************************/
  358. void _Cache::_removeData(CPtr data)
  359. {
  360. if(Elm *elm=dataElm(data))
  361. {
  362. SyncUnlocker unlocker(D._lock); // this must be used also since later 'D._lock' can be locked when deleting the resource
  363. SyncLocker locker( _lock);
  364. if(contains(elm))
  365. {
  366. Desc &desc=elmDesc(*elm);
  367. FlagDisable(desc.flag, CACHE_ELM_STD_PTR);
  368. if(!desc.ptr_num) // if there are no more pointers accessing this element
  369. {
  370. if(desc.flag&CACHE_ELM_DELAY_REMOVE)_delay_remove.remove(findDelayRemove(*elm)); // if was listed in the 'delay_remove' then remove it from it
  371. removeFromOrder(*elm);
  372. _memx.removeData( elm);
  373. }
  374. }
  375. }
  376. }
  377. /******************************************************************************/
  378. void _Cache::_incRef(CPtr data)
  379. {
  380. if(Elm *elm=dataElm(data))
  381. #if !SYNC_LOCK_SAFE // if 'SyncLock' is not safe then crash may occur when trying to lock, to prevent that, check if we have any elements (this means cache was already initialized)
  382. if(_elms)
  383. #endif
  384. {
  385. SyncUnlocker unlocker(D._lock); // must be used even though we're not using GPU
  386. SyncLocker locker( _lock);
  387. if(contains(elm))elmDesc(*elm).ptr_num++;
  388. }
  389. }
  390. void _Cache::_decRef(CPtr data)
  391. {
  392. if(Elm *elm=dataElm(data))
  393. #if !SYNC_LOCK_SAFE // if 'SyncLock' is not safe then crash may occur when trying to lock, to prevent that, check if we have any elements (this means cache was already initialized)
  394. if(_elms)
  395. #endif
  396. {
  397. SyncUnlocker unlocker(D._lock); // this must be used also since later 'D._lock' can be locked when deleting the resource
  398. SyncLocker locker( _lock);
  399. if(contains(elm))
  400. {
  401. Desc &desc=elmDesc(*elm); DEBUG_ASSERT(desc.ptr_num>0, "'_Cache.decRef' Decreasing 'ptr_num' when it's already zero");
  402. if(!--desc.ptr_num && !(desc.flag&CACHE_ELM_STD_PTR)) // if there are no more pointers accessing this element
  403. {
  404. Flt delay_remove_time=_delay_remove_time;
  405. if( delay_remove_time >0 && GetThreadId()==DelayRemoveThreadID)delay_remove_time-=DelayRemoveWaited; // if want to use delayed remove by time and we're unloading because of parent getting delay unloaded, then decrease the time which the parent already waited
  406. if( delay_remove_time >0 // if want to use delayed remove by time
  407. || _delay_remove_counter>0 // if want to use delayed remove temporarily
  408. || (_can_be_removed && !_can_be_removed(data))) // or it can't be removed right now
  409. {
  410. Flt time=Time.appTime()+delay_remove_time; // set removal time
  411. if(desc.flag&CACHE_ELM_DELAY_REMOVE) // if already listed
  412. {
  413. Int i=findDelayRemove(*elm); DEBUG_ASSERT(i>=0, "'_Cache.decRef' Element has CACHE_ELM_DELAY_REMOVE but isn't listed in '_delay_remove'");
  414. _delay_remove[i].time=time;
  415. }else // not yet listed
  416. {
  417. FlagEnable(desc.flag, CACHE_ELM_DELAY_REMOVE);
  418. DelayRemove &remove=_delay_remove.New();
  419. remove.elm =elm;
  420. remove.time=time;
  421. }
  422. }else // remove now
  423. {
  424. if(desc.flag&CACHE_ELM_DELAY_REMOVE)_delay_remove.remove(findDelayRemove(*elm)); // if was listed in the 'delay_remove' then remove it from it
  425. removeFromOrder(*elm);
  426. _memx.removeData( elm);
  427. }
  428. }
  429. }
  430. }
  431. }
  432. /******************************************************************************/
  433. void _Cache::processDelayRemove(Bool always)
  434. {
  435. if(_delay_remove.elms())
  436. {
  437. SyncUnlocker unlocker(D._lock); // this must be used also since later 'D._lock' can be locked when deleting the resource
  438. SyncLocker delay_locker(DelayRemoveLock); DelayRemoveThreadID=GetThreadId(); // support only one 'processDelayRemove' at a time, because we use only one global 'DelayRemoveWaited' base on 'DelayRemoveThreadID' for all caches/threads
  439. SyncLocker locker( _lock);
  440. _delay_remove_check=Time.appTime()+_delay_remove_time*DELAY_REMOVE_STEP; // perform next check at this time
  441. REPA(_delay_remove)
  442. {
  443. DelayRemove &remove=_delay_remove[i];
  444. if(always || Time.appTime()>=remove.time) // if always remove or enough time has passed (use >= so when having zero delay time then it will be processed immediately)
  445. {
  446. Elm &elm=*remove.elm; Desc &desc=elmDesc(elm); _delay_remove.remove(i); // access before removal and remove afterwards
  447. if(desc.ptr_num || (desc.flag&CACHE_ELM_STD_PTR)) // if there is something accessing this element now
  448. {
  449. FlagDisable(desc.flag, CACHE_ELM_DELAY_REMOVE); // keep the element but disable the 'CACHE_ELM_DELAY_REMOVE' flag since we've removed it from the '_delay_remove' container
  450. }else // nothing accessing this element
  451. {
  452. // remove element from cache
  453. DelayRemoveWaited=Max(0, Time.appTime()-remove.time+_delay_remove_time); // get how much time this element was waiting to be removed, set this before removing this element, so its children will be able to access it in the destructor
  454. removeFromOrder( elm);
  455. _memx.removeData(&elm);
  456. }
  457. }
  458. }
  459. DelayRemoveWaited =0; // clear back to zero before clearing 'DelayRemoveThreadID', in case some other thread has ID=0, and thus would use 'DelayRemoveWaited' from this thread
  460. DelayRemoveThreadID=0;
  461. }
  462. }
  463. void _Cache::delayRemoveInc() { AtomicInc(_delay_remove_counter);}
  464. void _Cache::delayRemoveDec() {if(AtomicDec(_delay_remove_counter)==1)update();}
  465. void _Cache::delayRemoveNow() { processDelayRemove(true );}
  466. void _Cache::update () {if(_delay_remove.elms() && _delay_remove_counter==0 && Time.appTime()>=_delay_remove_check)processDelayRemove(false);}
  467. /******************************************************************************/
  468. void _Cache::_lockedFrom(C _Cache &src)
  469. {
  470. del();
  471. _case_sensitive =src._case_sensitive;
  472. _mode =src._mode;
  473. _debug_name =src._debug_name;
  474. _delay_remove_time=src._delay_remove_time;
  475. //_delay_remove_check, _delay_remove_counter - leave them
  476. #if 0 // modify dest type to match src type (this is not needed)
  477. //_data_offset =src._data_offset;
  478. _desc_offset =src._desc_offset;
  479. _user =src._user;
  480. _load =src._load;
  481. _load_user =src._load_user;
  482. _can_be_removed=src._can_be_removed;
  483. _memx._reset(src._memx.elmSize(), src._memx.blockElms(), src._memx._new, src._memx._del);
  484. #endif
  485. _elms =src._elms;
  486. _memx.setNum(src._memx.elms()); // pre allocate memory for elements
  487. Alloc(_order, _memx.maxElms()); // initialize order exactly the same way as source
  488. REPA(src) // setup order exactly the same way as source
  489. {
  490. C Elm & src_elm = *src._order[i]; Int memx_index=src._memx.validIndex(&src_elm); DYNAMIC_ASSERT(InRange(memx_index, _memx), "Invalid element index in Cache.operator=(C Cache &src)"); // get valid index of i-th element in memx container
  491. Elm &dest_elm =_memx[memx_index]; _order[i]=&dest_elm; // set valid index of i-th element
  492. C Desc & src_desc=src.elmDesc( src_elm);
  493. Desc &dest_desc= elmDesc(dest_elm);
  494. dest_desc=src_desc; // copy desc
  495. dest_desc.ptr_num=0; // there are no pointers referencing elements created in dest, because if there were any pointers, then they reference only elements in the source, this must be cleared so reference counting will work correctly
  496. FlagDisable(dest_desc.flag, CACHE_ELM_STD_PTR); // these elements aren't specifically referenced, as only source elements are
  497. // don't set anything in '_delay_remove' because this is set based on elements that are no longer referenced, however in current situation all elements have 'ptr_num=0' and 'CACHE_ELM_STD_PTR' disabled, so all of them would have to be removed
  498. // TODO: what's the best way to handle adjusting CACHE_ELM_STD_PTR flag and setting '_delay_remove', because currently it's always disabled. Sample use when this cache copying happens is for Editor source, copying Project class objects, which has "Cache<WorldVer> world_vers;" member (this is not reference counted, but only accessed through CACHE_ELM_STD_PTR)
  499. }
  500. }
  501. /******************************************************************************/
  502. }
  503. /******************************************************************************/