1
0
forked from 0ad/0ad

h_mgr dox update; fixed redefinition error in wposix.h when building against DLL CRT

This was SVN commit r177.
This commit is contained in:
janwas 2004-03-07 14:17:23 +00:00
parent 1b0012d231
commit 757aca19ef
3 changed files with 185 additions and 31 deletions

View File

@ -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,18 +495,22 @@ 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;
}

View File

@ -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).

View File

@ -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*);
// <time.h>
//
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);