diff --git a/source/lib/res/h_mgr.cpp b/source/lib/res/h_mgr.cpp index 4f768363dc..28bc254d76 100755 --- a/source/lib/res/h_mgr.cpp +++ b/source/lib/res/h_mgr.cpp @@ -30,35 +30,31 @@ // TODO: h_find - required for caching + + + + + +// rationale +// +// why fixed size control blocks, instead of just allocating dynamically? +// it is expected that resources be created and freed often. this way is +// much nicer to the memory manager. defining control blocks larger than +// the alloted space is caught by h_alloc (made possible by the vtbl builder +// storing control block size). it is also efficient to have all CBs in an +// more or less contiguous array (see below). +// +// why a manager, instead of a simple pool allocator? +// we need a central list of resources for freeing at exit, checking if a +// resource has already been loaded (for caching), and when reloading. +// may as well keep them in an array, rather than add a list and index. + + + // // handle // -// handles are an indirection layer between client code and resources. -// they allow an important check not possible with a direct pointer: -// guaranteeing the handle references a given resource /instance/. -// -// problem: code C1 allocates a resource, and receives a pointer p to its -// control block. C1 passes p on to C2, and later frees it. -// now other code allocates a resource, and happens to reuse the free slot -// pointed to by p (also possible if simply allocating from the heap). -// when C2 accesses p, the pointer is valid, but we cannot tell that -// it is referring to a resource that had already been freed. big trouble. -// -// solution: each allocation receives a unique tag (a global counter that -// is large enough to never overflow). Handles include this tag, as well -// as a reference (array index) to the control block, which isn't directly -// accessible. when dereferencing the handle, we check if the handle's tag -// matches the copy stored in the control block. this protects against stale -// handle reuse, double-free, and accidentally referencing other resources. -// -// type: each handle has an associated type. these must be checked to prevent -// using textures as sounds, for example. with the manual vtbl scheme, -// this type is actually a pointer to the resource object's vtbl, and is -// set up via H_TYPE_DEFINE. see header for rationale. this means that -// types are private to the module that declared the handle; knowledge -// of the type ensures the caller actually declared, and owns the resource. - // 0 = invalid handle value // < 0 is an error code (we assume < 0 <==> MSB is set - // true for 1s and 2s complement and sign-magnitude systems) @@ -499,17 +495,21 @@ int h_reload(const char* fn) int ret = 0; // now reload all affected handles - // TODO: if too slow + // TODO: what if too slow to iterate through all handles? for(i = 0; i <= last_in_use; i++) { HDATA* hd = h_data(i); - if(!hd) + if(!hd || hd->key != key) continue; int err = hd->type->reload(hd->user, hd->fn); // don't stop if an error is encountered - try to reload them all. if(err < 0) + { + Handle h = handle(i, hd->tag); + h_free(h, hd->type); ret = err; + } } return ret; diff --git a/source/lib/res/h_mgr.h b/source/lib/res/h_mgr.h index 81afa603f7..22843fc850 100755 --- a/source/lib/res/h_mgr.h +++ b/source/lib/res/h_mgr.h @@ -193,3 +193,156 @@ extern int res_cur_scope; #endif // #ifndef H_MGR_H__ + + + + +// introduction +// ------------ +// +// a resource is an instance of a specific type of game data (e.g. texture), +// described by a control block (example fields: format, pointer to tex data). +// +// this module allocates storage for the control blocks, which are accessed +// via handle. it also provides support for transparently reloading resources +// from disk (allows in-game editing of data), and caches resource data. +// finally, it frees all resources at exit, preventing leaks. +// +// +// handles +// ------- +// +// handles are an indirection layer between client code and resources +// (represented by their control blocks, which contains/points to its data). +// they allow an important check not possible with a direct pointer: +// guaranteeing the handle references a given resource /instance/. +// +// problem: code C1 allocates a resource, and receives a pointer p to its +// control block. C1 passes p on to C2, and later frees it. +// now other code allocates a resource, and happens to reuse the free slot +// pointed to by p (also possible if simply allocating from the heap). +// when C2 accesses p, the pointer is valid, but we cannot tell that +// it is referring to a resource that had already been freed. big trouble. +// +// solution: each allocation receives a unique tag (a global counter that +// is large enough to never overflow). Handles include this tag, as well +// as a reference (array index) to the control block, which isn't directly +// accessible. when dereferencing the handle, we check if the handle's tag +// matches the copy stored in the control block. this protects against stale +// handle reuse, double-free, and accidentally referencing other resources. +// +// type: each handle has an associated type. these must be checked to prevent +// using textures as sounds, for example. with the manual vtbl scheme, +// this type is actually a pointer to the resource object's vtbl, and is +// set up via H_TYPE_DEFINE. this means that types are private to the module +// that declared the handle; knowledge of the type ensures the caller +// actually declared, and owns the resource. + + +// guide to defining and using resources +// ------------------------------------- +// +// 1) choose a name for the resource, used to represent all resources +// of this type. we will call ours Res1; all occurences of it below +// must be replaced with the actual name (exact spelling). +// why? the vtbl builder defines its functions as e.g. Res1_reload; +// your actual definition must match. +// +// 2) declare its control block: +// struct Res1 +// { +// void* data1; // data loaded from file +// int flags; // set when resource is created +// }; +// +// 3) build its vtbl: +// H_TYPE_DEFINE(Res1) +// +// this defines the symbol H_Res1, which is used whenever the handle +// manager needs its type. it is only accessible to this module +// (file scope). note that it is actually a pointer to the vtbl. +// this must come before uses of H_Res1, and after the CB definition; +// there are no restrictions WRT functions, because the macro +// forward-declares what it needs. +// +// 4) implement all 'virtual' functions from the resource interface. +// note that inheritance isn't really possible with this approach - +// all functions must be defined, even if not needed. +// +// -- +// +// init: +// one-time init of the control block. called from h_alloc. +// precondition: control block is initialized to 0. +// +// static void Type_init(Res1* r, va_list args) +// { +// r->flags = va_arg(args, int); +// } +// +// if the caller of h_alloc passed additional args, they are available +// in args. if init references more args than were passed, big trouble. +// however, this is a bug in your code, and cannot be triggered +// maliciously. only your code knows the resource type, and it is the +// only call site of h_alloc. +// there is no provision for indicating failure. if one-time init fails +// (rare, but one example might be failure to allocate memory that is +// for the lifetime of the resource, instead of in reload), it will +// have to set the control block state such that reload will fail. +// +// -- +// +// reload: +// does all initialization of the resource that requires its source file. +// called after init; also after dtor every time the file is reloaded. +// +// static int Type_reload(Res1* r, const char* filename); +// { +// // somehow load stuff from filename, and store it in r->data1. +// return 0; +// } +// +// reload must abort if the control block data indicates the resource +// has already been loaded! example: if texture's reload is called first, +// it loads itself from file (triggering file.reload); afterwards, +// file.reload will be called again. we can't avoid this, because the +// handle manager doesn't know anything about dependencies +// (here, texture -> file). +// return value: 0 if successful (includes 'already loaded'), +// negative error code otherwise. if this fails, the resource is freed +// (=> dtor is called!). +// +// -- +// +// dtor: +// frees all data allocated by init and reload. called after h_free, +// or at exit. control block is zeroed afterwards. +// +// static void Type_dtor (Res1* r); +// { +// // free memory r->data1 +// } +// +// again no provision for reporting errors - there's no one to act on it +// if called at exit. you can assert or log the error, though. +// +// 5) provide your layer on top of the handle manager: +// Handle res1_load(const char* filename, int my_flags) +// { +// return h_alloc(H_Res1, filename, 0, /* additional param passed to init -> */ my_flags); +// } +// +// int res1_free(Handle& h) +// { +// return h_free(h, H_Res1); +// } +// +// the h parameter is zeroed by h_free. +// +// (this layer allows a res_load interface on top of all the loaders, +// and is necessary because your module is the only one that knows H_Res1). +// +// 6) done. the resource will be freed at exit (if not done already). + + + diff --git a/source/lib/sysdep/win/wposix.h b/source/lib/sysdep/win/wposix.h index 3810c34464..8f66ee29f9 100755 --- a/source/lib/sysdep/win/wposix.h +++ b/source/lib/sysdep/win/wposix.h @@ -16,6 +16,11 @@ // Jan.Wassenberg@stud.uni-karlsruhe.de // http://www.stud.uni-karlsruhe.de/~urkt/ +// note: try to avoid redefining CRT functions - if building against the +// DLL CRT, the declarations will be incompatible. adding _CRTIMP to the decl +// is a last resort (e.g. if the regular CRT headers would conflict). + + #ifndef __WPOSIX_H__ #define __WPOSIX_H__ @@ -307,8 +312,6 @@ extern int pthread_mutex_timedlock(pthread_mutex_t*, const struct timespec*); // // -typedef long time_t; - typedef enum { CLOCK_REALTIME @@ -329,8 +332,6 @@ struct timespec long tv_nsec; }; -extern time_t time(time_t*); - extern int gettimeofday(struct timeval* tv, void* tzp); extern int nanosleep(const struct timespec* rqtp, struct timespec* rmtp);