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:
parent
1b0012d231
commit
757aca19ef
@ -30,35 +30,31 @@
|
|||||||
// TODO: h_find - required for caching
|
// 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
|
// 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 = invalid handle value
|
||||||
// < 0 is an error code (we assume < 0 <==> MSB is set -
|
// < 0 is an error code (we assume < 0 <==> MSB is set -
|
||||||
// true for 1s and 2s complement and sign-magnitude systems)
|
// true for 1s and 2s complement and sign-magnitude systems)
|
||||||
@ -499,17 +495,21 @@ int h_reload(const char* fn)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
// now reload all affected handles
|
// 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++)
|
for(i = 0; i <= last_in_use; i++)
|
||||||
{
|
{
|
||||||
HDATA* hd = h_data(i);
|
HDATA* hd = h_data(i);
|
||||||
if(!hd)
|
if(!hd || hd->key != key)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int err = hd->type->reload(hd->user, hd->fn);
|
int err = hd->type->reload(hd->user, hd->fn);
|
||||||
// don't stop if an error is encountered - try to reload them all.
|
// don't stop if an error is encountered - try to reload them all.
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
|
{
|
||||||
|
Handle h = handle(i, hd->tag);
|
||||||
|
h_free(h, hd->type);
|
||||||
ret = err;
|
ret = err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -193,3 +193,156 @@ extern int res_cur_scope;
|
|||||||
|
|
||||||
|
|
||||||
#endif // #ifndef H_MGR_H__
|
#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).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +16,11 @@
|
|||||||
// Jan.Wassenberg@stud.uni-karlsruhe.de
|
// Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||||
// http://www.stud.uni-karlsruhe.de/~urkt/
|
// 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__
|
#ifndef __WPOSIX_H__
|
||||||
#define __WPOSIX_H__
|
#define __WPOSIX_H__
|
||||||
|
|
||||||
@ -307,8 +312,6 @@ extern int pthread_mutex_timedlock(pthread_mutex_t*, const struct timespec*);
|
|||||||
// <time.h>
|
// <time.h>
|
||||||
//
|
//
|
||||||
|
|
||||||
typedef long time_t;
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CLOCK_REALTIME
|
CLOCK_REALTIME
|
||||||
@ -329,8 +332,6 @@ struct timespec
|
|||||||
long tv_nsec;
|
long tv_nsec;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern time_t time(time_t*);
|
|
||||||
|
|
||||||
extern int gettimeofday(struct timeval* tv, void* tzp);
|
extern int gettimeofday(struct timeval* tv, void* tzp);
|
||||||
|
|
||||||
extern int nanosleep(const struct timespec* rqtp, struct timespec* rmtp);
|
extern int nanosleep(const struct timespec* rqtp, struct timespec* rmtp);
|
||||||
|
Loading…
Reference in New Issue
Block a user