Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
h_mgr.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2013 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * handle manager for resources.
25  */
26 
27 #include "precompiled.h"
28 #include "h_mgr.h"
29 
30 #include <boost/unordered_map.hpp>
31 
32 #include <limits.h> // CHAR_BIT
33 #include <string.h>
34 #include <stdlib.h>
35 #include <new> // std::bad_alloc
36 
37 #include "lib/fnv_hash.h"
39 #include "lib/allocators/pool.h"
40 #include "lib/module_init.h"
42 
43 
44 namespace ERR {
45 static const Status H_IDX_INVALID = -120000; // totally invalid
46 static const Status H_IDX_UNUSED = -120001; // beyond current cap
47 static const Status H_TAG_MISMATCH = -120003;
48 static const Status H_TYPE_MISMATCH = -120004;
49 static const Status H_ALREADY_FREED = -120005;
50 }
52  { ERR::H_IDX_INVALID, L"Handle index completely out of bounds" },
53  { ERR::H_IDX_UNUSED, L"Handle index exceeds high-water mark" },
54  { ERR::H_TAG_MISMATCH, L"Handle tag mismatch (stale reference?)" },
55  { ERR::H_TYPE_MISMATCH, L"Handle type mismatch" },
56  { ERR::H_ALREADY_FREED, L"Handle already freed" }
57 };
58 STATUS_ADD_DEFINITIONS(hStatusDefinitions);
59 
60 
61 
62 // rationale
63 //
64 // why fixed size control blocks, instead of just allocating dynamically?
65 // it is expected that resources be created and freed often. this way is
66 // much nicer to the memory manager. defining control blocks larger than
67 // the allotted space is caught by h_alloc (made possible by the vtbl builder
68 // storing control block size). it is also efficient to have all CBs in an
69 // more or less contiguous array (see below).
70 //
71 // why a manager, instead of a simple pool allocator?
72 // we need a central list of resources for freeing at exit, checking if a
73 // resource has already been loaded (for caching), and when reloading.
74 // may as well keep them in an array, rather than add a list and index.
75 
76 
77 
78 //
79 // handle
80 //
81 
82 // 0 = invalid handle value
83 // < 0 is an error code (we assume < 0 <==> MSB is set -
84 // true for 1s and 2s complement and sign-magnitude systems)
85 
86 // fields:
87 // (shift value = # bits between LSB and field LSB.
88 // may be larger than the field type - only shift Handle vars!)
89 
90 // - index (0-based) of control block in our array.
91 // (field width determines maximum currently open handles)
92 #define IDX_BITS 16
93 static const u64 IDX_MASK = (1l << IDX_BITS) - 1;
94 
95 // - tag (1-based) ensures the handle references a certain resource instance.
96 // (field width determines maximum unambiguous resource allocs)
97 typedef i64 Tag;
98 #define TAG_BITS 48
99 static const u64 TAG_MASK = 0xFFFFFFFF; // safer than (1 << 32) - 1
100 
101 // make sure both fields fit within a Handle variable
102 cassert(IDX_BITS + TAG_BITS <= sizeof(Handle)*CHAR_BIT);
103 
104 
105 // return the handle's index field (always non-negative).
106 // no error checking!
107 static inline size_t h_idx(const Handle h)
108 {
109  return (size_t)(h & IDX_MASK) - 1;
110 }
111 
112 // return the handle's tag field.
113 // no error checking!
114 static inline Tag h_tag(Handle h)
115 {
116  return h >> IDX_BITS;
117 }
118 
119 // build a handle from index and tag.
120 // can't fail.
121 static inline Handle handle(size_t idx, u64 tag)
122 {
123  const size_t idxPlusOne = idx+1;
124  ENSURE(idxPlusOne <= IDX_MASK);
125  ENSURE((tag & IDX_MASK) == 0);
126  Handle h = tag | idxPlusOne;
127  ENSURE(h > 0);
128  return h;
129 }
130 
131 
132 //
133 // internal per-resource-instance data
134 //
135 
136 // chosen so that all current resource structs are covered.
137 static const size_t HDATA_USER_SIZE = 100;
138 
139 
140 struct HDATA
141 {
142  // we only need the tag, because it is trivial to compute
143  // &HDATA from idx and vice versa. storing the entire handle
144  // avoids needing to extract the tag field.
145  Handle h; // NB: will be overwritten by pool_free
146 
147  uintptr_t key;
148 
149  intptr_t refs;
150 
151  // smaller bit fields combined into 1
152  // .. if set, do not actually release the resource (i.e. call dtor)
153  // when the handle is h_free-d, regardless of the refcount.
154  // set by h_alloc; reset on exit and by housekeeping.
156  // .. HACK: prevent adding to h_find lookup index if flags & RES_UNIQUE
157  // (because those handles might have several instances open,
158  // which the index can't currently handle)
159  u32 unique : 1;
161 
163 
164  // for statistics
165  size_t num_derefs;
166 
167  // storing PIVFS here is not a good idea since this often isn't
168  // `freed' due to caching (and there is no dtor), so
169  // the VFS reference count would never reach zero.
171 
173 };
174 
175 
176 // max data array entries. compared to last_in_use => signed.
177 static const ssize_t hdata_cap = (1ul << IDX_BITS)/4;
178 
179 // pool of fixed-size elements allows O(1) alloc and free;
180 // there is a simple mapping between HDATA address and index.
181 static Pool hpool;
182 
183 
184 // error checking strategy:
185 // all handles passed in go through h_data(Handle, Type)
186 
187 
188 // get a (possibly new) array entry.
189 //
190 // fails if idx is out of bounds.
192 {
193  // don't check if idx is beyond the current high-water mark, because
194  // we might be allocating a new entry. subsequent tag checks protect
195  // against using unallocated entries.
196  if(size_t(idx) >= size_t(hdata_cap)) // also detects negative idx
198 
199  hd = (HDATA*)(hpool.da.base + idx*hpool.el_size);
200  hd->num_derefs++;
201  return INFO::OK;
202 }
203 
205 {
206  if(!pool_contains(&hpool, hd))
208  return (uintptr_t(hd) - uintptr_t(hpool.da.base))/hpool.el_size;
209 }
210 
211 
212 // get HDATA for the given handle.
213 // only uses (and checks) the index field.
214 // used by h_force_close (which must work regardless of tag).
215 static inline Status h_data_no_tag(const Handle h, HDATA*& hd)
216 {
217  ssize_t idx = (ssize_t)h_idx(h);
219  // need to verify it's in range - h_data_from_idx can only verify that
220  // it's < maximum allowable index.
221  if(uintptr_t(hd) > uintptr_t(hpool.da.base)+hpool.da.pos)
223  return INFO::OK;
224 }
225 
226 
227 static bool ignoreDoubleFree = false;
228 
229 // get HDATA for the given handle.
230 // also verifies the tag field.
231 // used by functions callable for any handle type, e.g. h_filename.
232 static inline Status h_data_tag(Handle h, HDATA*& hd)
233 {
235 
236  if(hd->key == 0) // HDATA was wiped out and hd->h overwritten by pool_free
237  {
238  if(ignoreDoubleFree)
239  return ERR::H_ALREADY_FREED; // NOWARN (see ignoreDoubleFree)
240  else
242  }
243 
244  if(h != hd->h)
246 
247  return INFO::OK;
248 }
249 
250 
251 // get HDATA for the given handle.
252 // also verifies the type.
253 // used by most functions accessing handle data.
254 static Status h_data_tag_type(const Handle h, const H_Type type, HDATA*& hd)
255 {
257 
258  // h_alloc makes sure type isn't 0, so no need to check that here.
259  if(hd->type != type)
260  {
261  debug_printf(L"h_mgr: expected type %ls, got %ls\n", hd->type->name, type->name);
263  }
264 
265  return INFO::OK;
266 }
267 
268 
269 //-----------------------------------------------------------------------------
270 // lookup data structure
271 //-----------------------------------------------------------------------------
272 
273 // speed up h_find (called every h_alloc)
274 // multimap, because we want to add handles of differing type but same key
275 // (e.g. a VFile and Tex object for the same underlying filename hash key)
276 //
277 // store index because it's smaller and Handle can easily be reconstructed
278 //
279 //
280 // note: there may be several RES_UNIQUE handles of the same type and key
281 // (e.g. sound files - several instances of a sound definition file).
282 // that wasn't foreseen here, so we'll just refrain from adding to the index.
283 // that means they won't be found via h_find - no biggie.
284 
285 typedef boost::unordered_multimap<uintptr_t, ssize_t> Key2Idx;
286 typedef Key2Idx::iterator It;
288 
290 
291 static Handle key_find(uintptr_t key, H_Type type, KeyRemoveFlag remove_option = KEY_NOREMOVE)
292 {
293  Key2Idx* key2idx = key2idx_wrapper.get();
294  if(!key2idx)
296 
297  // initial return value: "not found at all, or it's of the
298  // wrong type". the latter happens when called by h_alloc to
299  // check if e.g. a Tex object already exists; at that time,
300  // only the corresponding VFile exists.
301  Handle ret = -1;
302 
303  std::pair<It, It> range = key2idx->equal_range(key);
304  for(It it = range.first; it != range.second; ++it)
305  {
306  ssize_t idx = it->second;
307  HDATA* hd;
308  if(h_data_from_idx(idx, hd) != INFO::OK)
309  continue;
310  if(hd->type != type || hd->key != key)
311  continue;
312 
313  // found a match
314  if(remove_option == KEY_REMOVE)
315  key2idx->erase(it);
316  ret = hd->h;
317  break;
318  }
319 
320  key2idx_wrapper.lock();
321  return ret;
322 }
323 
324 
325 static void key_add(uintptr_t key, Handle h)
326 {
327  Key2Idx* key2idx = key2idx_wrapper.get();
328  if(!key2idx)
329  return;
330 
331  const ssize_t idx = h_idx(h);
332  // note: MSDN documentation of stdext::hash_multimap is incorrect;
333  // there is no overload of insert() that returns pair<iterator, bool>.
334  (void)key2idx->insert(std::make_pair(key, idx));
335 
336  key2idx_wrapper.lock();
337 }
338 
339 
340 static void key_remove(uintptr_t key, H_Type type)
341 {
342  Handle ret = key_find(key, type, KEY_REMOVE);
343  ENSURE(ret > 0);
344 }
345 
346 
347 //----------------------------------------------------------------------------
348 // h_alloc
349 //----------------------------------------------------------------------------
350 
351 static void warn_if_invalid(HDATA* hd)
352 {
353 #ifndef NDEBUG
354  H_VTbl* vtbl = hd->type;
355 
356  // validate HDATA
357  // currently nothing to do; <type> is checked by h_alloc and
358  // the others have no invariants we could check.
359 
360  // have the resource validate its user_data
361  Status err = vtbl->validate(hd->user);
362  ENSURE(err == INFO::OK);
363 
364  // make sure empty space in control block isn't touched
365  // .. but only if we're not storing a filename there
366  const u8* start = hd->user + vtbl->user_size;
367  const u8* end = hd->user + HDATA_USER_SIZE;
368  for(const u8* p = start; p < end; p++)
369  ENSURE(*p == 0); // else: handle user data was overrun!
370 #else
371  UNUSED2(hd);
372 #endif
373 }
374 
375 
377 {
378  if(!type)
380  if(type->user_size > HDATA_USER_SIZE)
382  if(type->name == 0)
384 
385  return INFO::OK;
386 }
387 
388 
389 static Tag gen_tag()
390 {
391  static Tag tag;
392  tag += (1ull << IDX_BITS);
393  // it's not easy to detect overflow, because compilers
394  // are allowed to assume it'll never happen. however,
395  // pow(2, 64-IDX_BITS) is "enough" anyway.
396  return tag;
397 }
398 
399 
400 static Handle reuse_existing_handle(uintptr_t key, H_Type type, size_t flags)
401 {
402  if(flags & RES_NO_CACHE)
403  return 0;
404 
405  // object of specified key and type doesn't exist yet
406  Handle h = h_find(type, key);
407  if(h <= 0)
408  return 0;
409 
410  HDATA* hd;
411  RETURN_STATUS_IF_ERR(h_data_tag_type(h, type, hd)); // h_find means this won't fail
412 
413  hd->refs += 1;
414 
415  // we are reactivating a closed but cached handle.
416  // need to generate a new tag so that copies of the
417  // previous handle can no longer access the resource.
418  // (we don't need to reset the tag in h_free, because
419  // use before this fails due to refs > 0 check in h_user_data).
420  if(hd->refs == 1)
421  {
422  const Tag tag = gen_tag();
423  h = handle(h_idx(h), tag); // can't fail
424  hd->h = h;
425  }
426 
427  return h;
428 }
429 
430 
431 static Status call_init_and_reload(Handle h, H_Type type, HDATA* hd, const PIVFS& vfs, const VfsPath& pathname, va_list* init_args)
432 {
433  Status err = INFO::OK;
434  H_VTbl* vtbl = type; // exact same thing but for clarity
435 
436  // init
437  if(vtbl->init)
438  vtbl->init(hd->user, *init_args);
439 
440  // reload
441  if(vtbl->reload)
442  {
443  // catch exception to simplify reload funcs - let them use new()
444  try
445  {
446  err = vtbl->reload(hd->user, vfs, pathname, h);
447  if(err == INFO::OK)
448  warn_if_invalid(hd);
449  }
450  catch(std::bad_alloc&)
451  {
452  err = ERR::NO_MEM;
453  }
454  }
455 
456  return err;
457 }
458 
459 
460 static Handle alloc_new_handle(H_Type type, const PIVFS& vfs, const VfsPath& pathname, uintptr_t key, size_t flags, va_list* init_args)
461 {
462  HDATA* hd = (HDATA*)pool_alloc(&hpool, 0);
463  if(!hd)
465  new(&hd->pathname) VfsPath;
466 
467  ssize_t idx = h_idx_from_data(hd);
469 
470  // (don't want to do this before the add-reference exit,
471  // so as not to waste tags for often allocated handles.)
472  const Tag tag = gen_tag();
473  Handle h = handle(idx, tag); // can't fail.
474 
475  hd->h = h;
476  hd->key = key;
477  hd->type = type;
478  hd->refs = 1;
479  if(!(flags & RES_NO_CACHE))
480  hd->keep_open = 1;
481  if(flags & RES_DISALLOW_RELOAD)
482  hd->disallow_reload = 1;
483  hd->unique = (flags & RES_UNIQUE) != 0;
484  hd->pathname = pathname;
485 
486  if(key && !hd->unique)
487  key_add(key, h);
488 
489  Status err = call_init_and_reload(h, type, hd, vfs, pathname, init_args);
490  if(err < 0)
491  goto fail;
492 
493  return h;
494 
495 fail:
496  // reload failed; free the handle
497  hd->keep_open = 0; // disallow caching (since contents are invalid)
498  (void)h_free(h, type); // (h_free already does WARN_IF_ERR)
499 
500  // note: since some uses will always fail (e.g. loading sounds if
501  // g_Quickstart), do not complain here.
502  return (Handle)err;
503 }
504 
505 
507 // (the same class is defined in vfs.cpp, but it is easier to
508 // just duplicate it to avoid having to specify the mutex.
509 // such a class exists in ps/ThreadUtil.h, but we can't
510 // take a dependency on that module here.)
512 {
515 };
516 
517 
518 // any further params are passed to type's init routine
519 Handle h_alloc(H_Type type, const PIVFS& vfs, const VfsPath& pathname, size_t flags, ...)
520 {
521  H_ScopedLock s;
522 
524 
525  const uintptr_t key = fnv_hash(pathname.string().c_str(), pathname.string().length()*sizeof(pathname.string()[0]));
526 
527  // see if we can reuse an existing handle
528  Handle h = reuse_existing_handle(key, type, flags);
530  // .. successfully reused the handle; refcount increased
531  if(h > 0)
532  return h;
533  // .. need to allocate a new one:
534  va_list args;
535  va_start(args, flags);
536  h = alloc_new_handle(type, vfs, pathname, key, flags, &args);
537  va_end(args);
538  return h; // alloc_new_handle already does WARN_RETURN_STATUS_IF_ERR
539 }
540 
541 
542 //-----------------------------------------------------------------------------
543 
544 static void h_free_hd(HDATA* hd)
545 {
546  if(hd->refs > 0)
547  hd->refs--;
548 
549  // still references open or caching requests it stays - do not release.
550  if(hd->refs > 0 || hd->keep_open)
551  return;
552 
553  // actually release the resource (call dtor, free control block).
554 
555  // h_alloc makes sure type != 0; if we get here, it still is
556  H_VTbl* vtbl = hd->type;
557 
558  // call its destructor
559  // note: H_TYPE_DEFINE currently always defines a dtor, but play it safe
560  if(vtbl->dtor)
561  vtbl->dtor(hd->user);
562 
563  if(hd->key && !hd->unique)
564  key_remove(hd->key, hd->type);
565 
566 #ifndef NDEBUG
567  // to_string is slow for some handles, so avoid calling it if unnecessary
568  if(debug_filter_allows(L"H_MGR|"))
569  {
570  wchar_t buf[H_STRING_LEN];
571  if(vtbl->to_string(hd->user, buf) < 0)
572  wcscpy_s(buf, ARRAY_SIZE(buf), L"(error)");
573  debug_printf(L"H_MGR| free %ls %ls accesses=%lu %ls\n", hd->type->name, hd->pathname.string().c_str(), (unsigned long)hd->num_derefs, buf);
574  }
575 #endif
576 
577  hd->pathname.~VfsPath(); // FIXME: ugly hack, but necessary to reclaim memory
578  memset(hd, 0, sizeof(*hd));
579  pool_free(&hpool, hd);
580 }
581 
582 
584 {
585  H_ScopedLock s;
586 
587  // 0-initialized or an error code; don't complain because this
588  // happens often and is harmless.
589  if(h <= 0)
590  return INFO::OK;
591 
592  // wipe out the handle to prevent reuse but keep a copy for below.
593  const Handle h_copy = h;
594  h = 0;
595 
596  HDATA* hd;
597  RETURN_STATUS_IF_ERR(h_data_tag_type(h_copy, type, hd));
598 
599  h_free_hd(hd);
600  return INFO::OK;
601 }
602 
603 
604 //----------------------------------------------------------------------------
605 // remaining API
606 
607 void* h_user_data(const Handle h, const H_Type type)
608 {
609  HDATA* hd;
610  if(h_data_tag_type(h, type, hd) != INFO::OK)
611  return 0;
612 
613  if(!hd->refs)
614  {
615  // note: resetting the tag is not enough (user might pass in its value)
616  DEBUG_WARN_ERR(ERR::LOGIC); // no references to resource (it's cached, but someone is accessing it directly)
617  return 0;
618  }
619 
620  warn_if_invalid(hd);
621  return hd->user;
622 }
623 
624 
626 {
627  // don't require type check: should be usable for any handle,
628  // even if the caller doesn't know its type.
629  HDATA* hd;
630  if(h_data_tag(h, hd) != INFO::OK)
631  return VfsPath();
632  return hd->pathname;
633 }
634 
635 
636 // TODO: what if iterating through all handles is too slow?
637 Status h_reload(const PIVFS& vfs, const VfsPath& pathname)
638 {
639  H_ScopedLock s;
640 
641  const u32 key = fnv_hash(pathname.string().c_str(), pathname.string().length()*sizeof(pathname.string()[0]));
642 
643  // destroy (note: not free!) all handles backed by this file.
644  // do this before reloading any of them, because we don't specify reload
645  // order (the parent resource may be reloaded first, and load the child,
646  // whose original data would leak).
647  for(HDATA* hd = (HDATA*)hpool.da.base; hd < (HDATA*)(hpool.da.base + hpool.da.pos); hd = (HDATA*)(uintptr_t(hd)+hpool.el_size))
648  {
649  if(hd->key == 0 || hd->key != key || hd->disallow_reload)
650  continue;
651  hd->type->dtor(hd->user);
652  }
653 
654  Status ret = INFO::OK;
655 
656  // now reload all affected handles
657  size_t i = 0;
658  for(HDATA* hd = (HDATA*)hpool.da.base; hd < (HDATA*)(hpool.da.base + hpool.da.pos); hd = (HDATA*)(uintptr_t(hd)+hpool.el_size), i++)
659  {
660  if(hd->key == 0 || hd->key != key || hd->disallow_reload)
661  continue;
662 
663  Status err = hd->type->reload(hd->user, vfs, hd->pathname, hd->h);
664  // don't stop if an error is encountered - try to reload them all.
665  if(err < 0)
666  {
667  h_free(hd->h, hd->type);
668  if(ret == 0) // don't overwrite first error
669  ret = err;
670  }
671  else
672  warn_if_invalid(hd);
673  }
674 
675  return ret;
676 }
677 
678 
679 Handle h_find(H_Type type, uintptr_t key)
680 {
681  H_ScopedLock s;
682  return key_find(key, type);
683 }
684 
685 
686 
687 // force the resource to be freed immediately, even if cached.
688 // tag is not checked - this allows the first Handle returned
689 // (whose tag will change after being 'freed', but remaining in memory)
690 // to later close the object.
691 // this is used when reinitializing the sound engine -
692 // at that point, all (cached) OpenAL resources must be freed.
694 {
695  H_ScopedLock s;
696 
697  // require valid index; ignore tag; type checked below.
698  HDATA* hd;
700  if(hd->type != type)
702  hd->keep_open = 0;
703  hd->refs = 0;
704  h_free_hd(hd);
705  return INFO::OK;
706 }
707 
708 
709 // increment Handle <h>'s reference count.
710 // only meant to be used for objects that free a Handle in their dtor,
711 // so that they are copy-equivalent and can be stored in a STL container.
712 // do not use this to implement refcounting on top of the Handle scheme,
713 // e.g. loading a Handle once and then passing it around. instead, have each
714 // user load the resource; refcounting is done under the hood.
716 {
717  HDATA* hd;
718  if(h_data_tag(h, hd) != INFO::OK)
719  return;
720 
721  ENSURE(hd->refs); // if there are no refs, how did the caller manage to keep a Handle?!
722  hd->refs++;
723 }
724 
725 
726 // retrieve the internal reference count or a negative error code.
727 // background: since h_alloc has no way of indicating whether it
728 // allocated a new handle or reused an existing one, counting references
729 // within resource control blocks is impossible. since that is sometimes
730 // necessary (always wrapping objects in Handles is excessive), we
731 // provide access to the internal reference count.
732 intptr_t h_get_refcnt(Handle h)
733 {
734  HDATA* hd;
736 
737  ENSURE(hd->refs); // if there are no refs, how did the caller manage to keep a Handle?!
738  return hd->refs;
739 }
740 
741 
743 
744 static Status Init()
745 {
746  // lock must be recursive (e.g. h_alloc calls h_find)
747  pthread_mutexattr_t attr;
748  int err;
749  err = pthread_mutexattr_init(&attr);
750  ENSURE(err == 0);
752  ENSURE(err == 0);
753  err = pthread_mutex_init(&h_mutex, &attr);
754  ENSURE(err == 0);
755  err = pthread_mutexattr_destroy(&attr);
756  ENSURE(err == 0);
757 
758  RETURN_STATUS_IF_ERR(pool_create(&hpool, hdata_cap*sizeof(HDATA), sizeof(HDATA)));
759  return INFO::OK;
760 }
761 
762 static void Shutdown()
763 {
764  debug_printf(L"H_MGR| shutdown. any handle frees after this are leaks!\n");
765  // objects that store handles to other objects are destroyed before their
766  // children, so the subsequent forced destruction of the child here will
767  // raise a double-free warning unless we ignore it. (#860, #915, #920)
768  ignoreDoubleFree = true;
769 
770  H_ScopedLock s;
771 
772  // forcibly close all open handles
773  for(HDATA* hd = (HDATA*)hpool.da.base; hd < (HDATA*)(hpool.da.base + hpool.da.pos); hd = (HDATA*)(uintptr_t(hd)+hpool.el_size))
774  {
775  // it's already been freed; don't free again so that this
776  // doesn't look like an error.
777  if(hd->key == 0)
778  continue;
779 
780  // disable caching; we need to release the resource now.
781  hd->keep_open = 0;
782  hd->refs = 0;
783 
784  h_free_hd(hd);
785  }
786 
787  pool_destroy(&hpool);
788 }
789 
790 void h_mgr_free_type(const H_Type type)
791 {
792  ignoreDoubleFree = true;
793 
794  H_ScopedLock s;
795 
796  // forcibly close all open handles of the specified type
797  for(HDATA* hd = (HDATA*)hpool.da.base; hd < (HDATA*)(hpool.da.base + hpool.da.pos); hd = (HDATA*)(uintptr_t(hd)+hpool.el_size))
798  {
799  // free if not previously freed and only free the proper type
800  if (hd->key == 0 || hd->type != type)
801  continue;
802 
803  // disable caching; we need to release the resource now.
804  hd->keep_open = 0;
805  hd->refs = 0;
806 
807  h_free_hd(hd);
808  }
809 }
810 
812 {
813  ModuleInit(&initState, Init);
814 }
815 
817 {
818  ModuleShutdown(&initState, Shutdown);
819 }
static OverrunProtector< Key2Idx > key2idx_wrapper
Definition: h_mgr.cpp:287
Status h_free(Handle &h, H_Type type)
Definition: h_mgr.cpp:583
#define u8
Definition: types.h:39
const Status LOGIC
Definition: status.h:409
void * pthread_mutex_t
Definition: wpthread.h:82
static void key_remove(uintptr_t key, H_Type type)
Definition: h_mgr.cpp:340
OverrunProtector wraps an arbitrary object in isolated page(s) and can detect inadvertent writes to i...
Status(* to_string)(const void *user, wchar_t *buf)
Definition: h_mgr.h:300
Status h_force_free(Handle h, H_Type type)
Definition: h_mgr.cpp:693
Path VfsPath
VFS path of the form &quot;(dir/)*file?&quot;.
Definition: vfs_path.h:40
const Status OK
Definition: status.h:386
static Status h_data_tag(Handle h, HDATA *&hd)
Definition: h_mgr.cpp:232
static const Status H_TAG_MISMATCH
Definition: h_mgr.cpp:47
void pool_free(Pool *p, void *el)
Make a fixed-size element available for reuse in the given Pool.
Definition: pool.cpp:146
boost::unordered_multimap< uintptr_t, ssize_t > Key2Idx
Definition: h_mgr.cpp:285
static Status Init()
Definition: h_mgr.cpp:744
shared_ptr< IVFS > PIVFS
Definition: vfs.h:226
void h_mgr_init()
Definition: h_mgr.cpp:811
size_t num_derefs
Definition: h_mgr.cpp:165
static void Shutdown()
Definition: h_mgr.cpp:762
void * pool_alloc(Pool *p, size_t size)
Dole out memory from the pool.
Definition: pool.cpp:120
static size_t h_idx(const Handle h)
Definition: h_mgr.cpp:107
static void warn_if_invalid(HDATA *hd)
Definition: h_mgr.cpp:351
static ssize_t h_idx_from_data(HDATA *hd)
Definition: h_mgr.cpp:204
Definition: h_mgr.cpp:140
~H_ScopedLock()
Definition: h_mgr.cpp:514
static ModuleInitState initState
Definition: h_mgr.cpp:742
Definition: h_mgr.h:294
static const u64 IDX_MASK
Definition: h_mgr.cpp:93
size_t user_size
Definition: h_mgr.h:301
VfsPath h_filename(const Handle h)
Definition: h_mgr.cpp:625
Handle h_alloc(H_Type type, const PIVFS &vfs, const VfsPath &pathname, size_t flags,...)
Definition: h_mgr.cpp:519
static Status type_validate(H_Type type)
Definition: h_mgr.cpp:376
u8 user[HDATA_USER_SIZE]
Definition: h_mgr.cpp:172
Status pool_create(Pool *p, size_t max_size, size_t el_size)
Ready Pool for use.
Definition: pool.cpp:86
#define ARRAY_SIZE(name)
int wcscpy_s(wchar_t *dst, size_t max_dst_chars, const wchar_t *src)
void lock() const
intptr_t h_get_refcnt(Handle h)
Definition: h_mgr.cpp:732
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
intptr_t ModuleInitState
initialization state of a module (class, source file, etc.) must be initialized to zero (e...
Definition: module_init.h:35
static Status h_data_from_idx(ssize_t idx, HDATA *&hd)
Definition: h_mgr.cpp:191
static pthread_mutex_t h_mutex
Definition: h_mgr.cpp:506
void * pthread_mutexattr_t
Definition: wpthread.h:75
static Handle alloc_new_handle(H_Type type, const PIVFS &vfs, const VfsPath &pathname, uintptr_t key, size_t flags, va_list *init_args)
Definition: h_mgr.cpp:460
static bool ignoreDoubleFree
Definition: h_mgr.cpp:227
u8 * base
Definition: dynarray.h:41
const Status LIMIT
Definition: status.h:428
const Status INVALID_POINTER
Definition: status.h:420
Definition: path.h:75
bool debug_filter_allows(const wchar_t *text)
indicate if the given text would be printed.
Definition: debug.cpp:119
static Handle key_find(uintptr_t key, H_Type type, KeyRemoveFlag remove_option=KEY_NOREMOVE)
Definition: h_mgr.cpp:291
const String & string() const
Definition: path.h:123
static Handle reuse_existing_handle(uintptr_t key, H_Type type, size_t flags)
Definition: h_mgr.cpp:400
static void key_add(uintptr_t key, Handle h)
Definition: h_mgr.cpp:325
pthread_key_t key
Definition: wpthread.cpp:140
Handle h
Definition: h_mgr.cpp:145
int pthread_mutex_lock(pthread_mutex_t *m)
Definition: wpthread.cpp:329
Status h_reload(const PIVFS &vfs, const VfsPath &pathname)
Definition: h_mgr.cpp:637
i64 Tag
Definition: h_mgr.cpp:97
VfsPath pathname
Definition: h_mgr.cpp:170
const Status INVALID_PARAM
Definition: status.h:423
void h_mgr_shutdown()
Definition: h_mgr.cpp:816
Status(* reload)(void *user, const PIVFS &vfs, const VfsPath &pathname, Handle)
Definition: h_mgr.h:297
Handle h_find(H_Type type, uintptr_t key)
Definition: h_mgr.cpp:679
u32 unique
Definition: h_mgr.cpp:159
i64 Status
Error handling system.
Definition: status.h:171
i64 Handle
`handle&#39; representing a reference to a resource (sound, texture, etc.)
Definition: handle.h:41
u32 disallow_reload
Definition: h_mgr.cpp:160
void h_mgr_free_type(const H_Type type)
Definition: h_mgr.cpp:790
void(* dtor)(void *user)
Definition: h_mgr.h:298
int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *)
Definition: wpthread.cpp:323
allocator design parameters:
Definition: pool.h:114
KeyRemoveFlag
Definition: h_mgr.cpp:289
size_t pos
Definition: dynarray.h:46
static Status h_data_no_tag(const Handle h, HDATA *&hd)
Definition: h_mgr.cpp:215
#define STATUS_ADD_DEFINITIONS(definitions)
add a module&#39;s array of StatusDefinition to the list.
Definition: status.h:216
static const Status H_IDX_INVALID
Definition: h_mgr.cpp:45
intptr_t ssize_t
Definition: wposix_types.h:82
static const ssize_t hdata_cap
Definition: h_mgr.cpp:177
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
u32 fnv_hash(const void *buf, size_t len)
rationale: this algorithm was chosen because it delivers &#39;good&#39; results for string data and is relati...
Definition: fnv_hash.cpp:33
void(* init)(void *user, va_list)
Definition: h_mgr.h:296
uintptr_t key
Definition: h_mgr.cpp:147
DynArray da
Definition: pool.h:116
static const StatusDefinition hStatusDefinitions[]
Definition: h_mgr.cpp:51
static const Status H_IDX_UNUSED
Definition: h_mgr.cpp:46
const wchar_t * name
Definition: h_mgr.h:302
#define u64
Definition: types.h:42
Status ModuleShutdown(volatile ModuleInitState *initState, void(*shutdown)())
calls a user-defined shutdown function if initState is &quot;initialized&quot;.
Definition: module_init.cpp:65
#define i64
Definition: types.h:37
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
Definition: wpthread.cpp:289
#define u32
Definition: types.h:41
size_t el_size
size of elements.
Definition: pool.h:122
static Status call_init_and_reload(Handle h, H_Type type, HDATA *hd, const PIVFS &vfs, const VfsPath &pathname, va_list *init_args)
Definition: h_mgr.cpp:431
H_Type type
Definition: h_mgr.cpp:162
static Pool hpool
Definition: h_mgr.cpp:181
int pthread_mutex_unlock(pthread_mutex_t *m)
Definition: wpthread.cpp:347
void * h_user_data(const Handle h, const H_Type type)
Definition: h_mgr.cpp:607
#define WARN_RETURN(status)
Definition: status.h:255
Key2Idx::iterator It
Definition: h_mgr.cpp:286
static const size_t HDATA_USER_SIZE
Definition: h_mgr.cpp:137
static Handle handle(size_t idx, u64 tag)
Definition: h_mgr.cpp:121
Status pool_destroy(Pool *p)
free all memory (address space + physical) that constitutes the given Pool.
Definition: pool.cpp:98
#define TAG_BITS
Definition: h_mgr.cpp:98
static void h_free_hd(HDATA *hd)
Definition: h_mgr.cpp:544
static const u64 TAG_MASK
Definition: h_mgr.cpp:99
Status(* validate)(const void *user)
Definition: h_mgr.h:299
const size_t H_STRING_LEN
Definition: h_mgr.h:371
static Tag gen_tag()
Definition: h_mgr.cpp:389
u32 keep_open
Definition: h_mgr.cpp:155
static const Status H_TYPE_MISMATCH
Definition: h_mgr.cpp:48
#define cassert(expr)
Compile-time assertion.
static Tag h_tag(Handle h)
Definition: h_mgr.cpp:114
const Status NO_MEM
Definition: status.h:430
static Status h_data_tag_type(const Handle h, const H_Type type, HDATA *&hd)
Definition: h_mgr.cpp:254
static const Status H_ALREADY_FREED
Definition: h_mgr.cpp:49
Status ModuleInit(volatile ModuleInitState *initState, Status(*init)())
calls a user-defined init function if initState is zero.
Definition: module_init.cpp:40
H_ScopedLock()
Definition: h_mgr.cpp:513
#define IDX_BITS
Definition: h_mgr.cpp:92
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
Definition: wpthread.cpp:278
intptr_t refs
Definition: h_mgr.cpp:149
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276
bool pool_contains(const Pool *p, void *el)
indicate whether a pointer was allocated from the given pool.
Definition: pool.cpp:108
int pthread_mutexattr_init(pthread_mutexattr_t *attr)
Definition: wpthread.cpp:273
void h_add_ref(Handle h)
Definition: h_mgr.cpp:715