waio: fix spurious WARN_ERR

vfs: integrate hotload code
fixed a few warnings as well.

This was SVN commit r2609.
This commit is contained in:
janwas 2005-08-13 17:09:57 +00:00
parent 6c4aab2e55
commit 0e20c08969
14 changed files with 238 additions and 233 deletions

View File

@ -1082,7 +1082,6 @@ debug_printf("file_io fd=%d size=%d ofs=%d\n", f->fd, data_size, data_ofs);
// only align if we allocate the buffer and in AIO mode
const bool do_align = temp;
const bool cache = temp;
//

View File

@ -244,39 +244,25 @@ enum VfsMountFlags
VFS_MOUNT_RECURSIVE = 2,
// all real directories mounted during this operation will be watched
// for changes (using hotload.h). this flag is provided to avoid
// watches in output-only directories, e.g. screenshots/
// (only causes unnecessary overhead).
// for changes. this flag is provided to avoid watches in output-only
// directories, e.g. screenshots/ (only causes unnecessary overhead).
VFS_MOUNT_WATCH = 4
};
// mount <p_real_dir> into the VFS at <V_mount_point>,
// mount <P_real_dir> into the VFS at <V_mount_point>,
// which is created if it does not yet exist.
// files in that directory override the previous VFS contents if
// <pri>(ority) is not lower.
// all archives in <p_real_dir> are also mounted, in alphabetical order.
// all archives in <P_real_dir> are also mounted, in alphabetical order.
//
// flags determines extra actions to perform; see VfsMountFlags.
//
// p_real_dir = "." or "./" isn't allowed - see implementation for rationale.
// P_real_dir = "." or "./" isn't allowed - see implementation for rationale.
extern int vfs_mount(const char* V_mount_point, const char* P_real_dir, int flags = 0, uint pri = 0);
// rebuild the VFS, i.e. re-mount everything. open files are not affected.
// necessary after loose files or directories change, so that the VFS
// "notices" the changes and updates file locations. res calls this after
// dir_watch reports changes; can also be called from the console after a
// rebuild command. there is no provision for updating single VFS dirs -
// it's not worth the trouble.
extern int vfs_rebuild(void);
// unmount a previously mounted item, and rebuild the VFS afterwards.
extern int vfs_unmount(const char* name);
// if <path> or its ancestors are mounted,
// return a VFS path that accesses it.
// used when receiving paths from external code.
extern int vfs_make_vfs_path(const char* path, char* vfs_path);
//
// directory entry
@ -427,4 +413,14 @@ extern int vfs_map(Handle hf, uint flags, void*& p, size_t& size);
// may be removed when no longer needed.
extern int vfs_unmap(Handle hf);
//
// hotloading
//
extern int vfs_reload(const char* fn);
// this must be called from the main thread? (wdir_watch problem)
extern int vfs_reload_changed_files(void);
#endif // #ifndef __VFS_H__

View File

@ -1,10 +1,11 @@
#include "precompiled.h"
#include "../res.h"
#include "vfs_mount.h"
#include "vfs_path.h"
#include "vfs_tree.h"
#include "zip.h"
#include "../res.h"
#include "sysdep/dir_watch.h"
struct Stats
{
@ -396,12 +397,14 @@ static int add_ent(TDir* td, DirEnt* ent, const char* P_parent_path, const Mount
}
// note: full path is needed for the dir watch.
static int populate_dir(TDir* td, const char* P_path, const Mount* m,
DirQueue* dir_queue, Archives* archives, int flags)
{
int err;
CHECK_ERR(tree_attach_real_dir(td, P_path, flags, m));
RealDir* rd = tree_get_real_dir(td);
WARN_ERR(mount_attach_real_dir(rd, P_path, m, flags));
DirIterator d;
RETURN_ERR(dir_open(P_path, &d));
@ -620,7 +623,7 @@ int vfs_mount(const char* V_mount_point, const char* P_real_path, int flags, uin
// dir_watch reports changes; can also be called from the console after a
// rebuild command. there is no provision for updating single VFS dirs -
// it's not worth the trouble.
int vfs_rebuild()
static int rebuild()
{
tree_clear();
tree_init();
@ -645,7 +648,7 @@ int vfs_unmount(const char* P_name)
// trim list and actually remove 'invalidated' entries.
mounts.erase(last, end);
return vfs_rebuild();
return rebuild();
}
@ -656,7 +659,7 @@ int vfs_unmount(const char* P_name)
// if <path> or its ancestors are mounted,
// return a VFS path that accesses it.
// used when receiving paths from external code.
int vfs_make_vfs_path(const char* P_path, char* V_path)
static int make_vfs_path(const char* P_path, char* V_path)
{
for(MountIt it = mounts.begin(); it != mounts.end(); ++it)
{
@ -691,14 +694,166 @@ void mount_shutdown()
int mount_populate(TDir* td, const Mount* m)
int mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int flags)
{
// more than one real dir mounted into VFS dir
// (=> can't create files for writing here)
if(rd->m)
rd->m = (const Mount*)-1;
else
rd->m = m;
#ifndef NO_DIR_WATCH
if(flags & VFS_MOUNT_WATCH)
{
char N_path[PATH_MAX];
CHECK_ERR(file_make_full_native_path(P_path, N_path));
CHECK_ERR(dir_add_watch(N_path, &rd->watch));
}
#endif
return 0;
}
void mount_detach_real_dir(RealDir* rd)
{
rd->m = 0;
#ifndef NO_DIR_WATCH
if(rd->watch) // avoid dir_cancel_watch complaining
WARN_ERR(dir_cancel_watch(rd->watch));
rd->watch = 0;
#endif
}
int mount_populate(TDir* td, RealDir* rd)
{
UNUSED2(td);
UNUSED2(rd);
return 0;
}
//-----------------------------------------------------------------------------
// hotloading
//-----------------------------------------------------------------------------
// called by vfs_reload and vfs_reload_changed_files (which will already
// have rebuilt the VFS - doing so more than once a frame is unnecessary).
static int reload_without_rebuild(const char* fn)
{
// invalidate this file's cached blocks to make sure its contents are
// loaded anew.
CHECK_ERR(file_invalidate_cache(fn));
CHECK_ERR(h_reload(fn));
return 0;
}
// called via console command.
int vfs_reload(const char* fn)
{
// if <fn> currently maps to an archive, the VFS must switch
// over to using the loose file (that was presumably changed).
CHECK_ERR(rebuild());
return reload_without_rebuild(fn);
}
// get directory change notifications, and reload all affected files.
// must be called regularly (e.g. once a frame). this is much simpler
// than asynchronous notifications: everything would need to be thread-safe.
int vfs_reload_changed_files()
{
// array of reloads requested this frame (see 'do we really need to
// reload' below). go through gyrations to avoid heap allocs.
const size_t MAX_RELOADS_PER_FRAME = 12;
typedef char Path[VFS_MAX_PATH];
typedef Path PathList[MAX_RELOADS_PER_FRAME];
PathList pending_reloads;
uint num_pending = 0;
// process only as many notifications as we have room for; the others
// will be handled next frame. it's not imagineable that they'll pile up.
while(num_pending < MAX_RELOADS_PER_FRAME)
{
// get next notification
char N_path[PATH_MAX];
int ret = dir_get_changed_file(N_path);
CHECK_ERR(ret); // error? (doesn't cover 'none available')
if(ret != 0) // none available; done.
break;
// convert to VFS path
char P_path[PATH_MAX];
CHECK_ERR(file_make_full_portable_path(N_path, P_path));
char* V_path = pending_reloads[num_pending];
CHECK_ERR(make_vfs_path(P_path, V_path));
// do we really need to reload? try to avoid the considerable cost of
// rebuilding VFS and scanning all Handles.
//
// note: be careful to avoid 'race conditions' depending on the
// timeframe in which notifications reach us.
// example: editor deletes a.tga; we are notified; reload is
// triggered but fails since the file isn't found; further
// notifications (e.g. renamed a.tmp to a.tga) come within x [ms] and
// are ignored due to a time limit.
// therefore, we can only check for multiple reload requests a frame;
// to that purpose, an array is built and duplicates ignored.
const char* ext = strrchr(V_path, '.');
// .. directory change notification; ignore because we get
// per-file notifications anyway. (note: assume no extension =>
// it's a directory). this also protects the strcmp calls below.
if(!ext)
continue;
// .. compiled XML files the engine writes out by the hundreds;
// skipping them is a big performance gain.
if(!strcmp(ext, ".xmb"))
continue;
// .. temp files, usually created when an editor saves a file
// (delete, create temp, rename temp); no need to reload those.
if(!strcmp(ext, ".tmp"))
continue;
// .. more than one notification for a file; only reload once.
// note: this doesn't suffer from the 'reloaded too early'
// problem described above; if there's more than one
// request in the array, the file has since been written.
for(uint i = 0; i < num_pending; i++)
if(!strcmp(pending_reloads[i], V_path))
continue;
// path has already been written to pending_reloads,
// so just mark it valid.
num_pending++;
}
// rebuild VFS, in case a file that has been changed is currently
// mounted from an archive (reloading would just grab the unchanged
// version in the archive). the rebuild sees differing mtimes and
// always choses the loose file version. only do this once
// (instead of per reload request) because it's slow (> 1s)!
if(num_pending != 0)
CHECK_ERR(rebuild());
// now actually reload all files in the array we built
for(uint i = 0; i < num_pending; i++)
CHECK_ERR(reload_without_rebuild(pending_reloads[i]));
return 0;
}
//-----------------------------------------------------------------------------

View File

@ -77,9 +77,6 @@ extern int x_io_close(XIo* xio);
struct TDir;
extern int mount_populate(TDir* td, const Mount* m);
//
// accessor routines that obviate the need to access Mount fields directly:
@ -90,4 +87,29 @@ extern bool mount_should_replace(const Mount* m_old, const Mount* m_new,
extern char mount_get_type(const Mount* m);
// stored by vfs_tree in TDir
struct RealDir
{
// if exactly one real directory is mounted into this virtual dir,
// this points to its location. used to add files to VFS when writing.
//
// the Mount is actually in the mount info and is invalid when
// that's unmounted, but the VFS would then be rebuilt anyway.
//
// = 0 if no real dir mounted here; = -1 if more than one.
const Mount* m;
#ifndef NO_DIR_WATCH
intptr_t watch;
#endif
};
extern int mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int flags);
extern void mount_detach_real_dir(RealDir* rd);
struct TDir;
extern int mount_populate(TDir* td, RealDir* rd);
#endif // #ifndef VFS_MOUNT_H__

View File

@ -7,11 +7,9 @@
#include <vector>
#include <algorithm>
#include "lib.h"
#include "../res.h"
#include "vfs_path.h"
#include "vfs_tree.h"
#include "../hotload.h" // see NO_DIR_WATCH
// we add/cancel directory watches from the VFS mount code for convenience -
@ -355,20 +353,10 @@ enum TDirFlags
// must be declared before TNode
struct TDir
{
// if exactly one real directory is mounted into this virtual dir,
// this points to its location. used to add files to VFS when writing.
//
// the Mount is actually in the mount info and is invalid when
// that's unmounted, but the VFS would then be rebuilt anyway.
//
// = 0 if no real dir mounted here; = -1 if more than one.
const Mount* m;
int flags; // enum TDirFlags
#ifndef NO_DIR_WATCH
intptr_t watch;
#endif
RealDir rd;
DynHashTbl children;
void init();
@ -435,7 +423,7 @@ static inline Key GetKey(const T t)
void TDir::init()
{
m = 0;
rd.m = 0;
children.init();
}
@ -486,24 +474,6 @@ done:
return 0;
}
// note: full path is needed for the dir watch.
int TDir::attach_real_dir(const char* P_path, int flags, const Mount* new_m)
{
// more than one real dir mounted into VFS dir
// (=> can't create files for writing here)
if(m)
m = (Mount*)-1;
else
m = new_m;
#ifndef NO_DIR_WATCH
if(flags & VFS_MOUNT_WATCH)
CHECK_ERR(res_watch_dir(P_path, &watch));
#endif
return 0;
}
int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
{
// cleared on failure / if returning root dir node (= "")
@ -560,7 +530,7 @@ int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
// make sure it has been populated with loose files/directories.
if(!(td->flags & TD_POPULATED))
{
WARN_ERR(mount_populate(td, td->m));
WARN_ERR(mount_populate(td, &td->rd));
td->flags |= TD_POPULATED;
}
@ -577,7 +547,7 @@ int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
// so we special-case its init.
if(type == N_FILE)
{
node->u.file.m = td->m;
node->u.file.m = td->rd.m;
}
}
else
@ -623,10 +593,10 @@ void TDir::clearR()
// wipe out this directory
children.clear();
#ifndef NO_DIR_WATCH
res_cancel_watch(watch);
watch = 0;
#endif
// the watch is restored when this directory is repopulated; we must
// remove it in case the real directory backing this one was deleted.
mount_detach_real_dir(&rd);
}
void TDir::displayR(int indent_level)
@ -716,11 +686,11 @@ void vfs_display()
int tree_add_file(TDir* dir, const char* name, const Mount* m,
int tree_add_file(TDir* td, const char* name, const Mount* m,
off_t size, time_t mtime)
{
TNode* node;
RETURN_ERR(dir->add(name, N_FILE, &node));
RETURN_ERR(td->add(name, N_FILE, &node));
TFile* tf = &node->u.file;
// assume they're the same if size and last-modified time match.
@ -737,33 +707,29 @@ int tree_add_file(TDir* dir, const char* name, const Mount* m,
}
int tree_add_dir(TDir* dir, const char* name, TDir** ptd)
int tree_add_dir(TDir* td, const char* name, TDir** ptd)
{
TNode* node;
RETURN_ERR(dir->add(name, N_DIR, &node));
RETURN_ERR(td->add(name, N_DIR, &node));
*ptd = &node->u.dir;
return 0;
}
int tree_attach_real_dir(TDir* dir, const char* path, int flags, const Mount* m)
{
return dir->attach_real_dir(path, flags, m);
}
int tree_lookup_dir(const char* path, TDir** pdir, uint flags, char* exact_path)
int tree_lookup_dir(const char* path, TDir** ptd, uint flags, char* exact_path)
{
// TDir::lookup would return a file node
if(path[0] != '\0' && path[strlen(path)-1] != '/')
return -1;
TDir* dir = (flags & LF_START_DIR)? *pdir : tree_root_dir;
TDir* td = (flags & LF_START_DIR)? *ptd : tree_root_dir;
TNode* node;
CHECK_ERR(dir->lookup(path, flags, &node, exact_path));
CHECK_ERR(td->lookup(path, flags, &node, exact_path));
// directories should exist, so warn if this fails
*pdir = &node->u.dir;
*ptd = &node->u.dir;
return 0;
}
@ -866,7 +832,7 @@ int tree_dir_close(TreeDirIterator* UNUSED(d))
//-----------------------------------------------------------------------------
// get/set for TFile
// get/set
const Mount* tree_get_mount(const TFile* tf)
{
@ -892,3 +858,9 @@ int tree_stat(const TFile* tf, struct stat* s)
return 0;
}
RealDir* tree_get_real_dir(TDir* td)
{
return &td->rd;
}

View File

@ -42,7 +42,6 @@ extern int tree_add_file(TDir* td, const char* fn, const Mount* m,
off_t size, time_t mtime);
extern int tree_add_dir(TDir* dir, const char* name, TDir** ptd);
extern int tree_attach_real_dir(TDir* dir, const char* path, int flags, const Mount* m);
enum TreeLookupFlags
{
@ -104,6 +103,10 @@ extern const Mount* tree_get_mount(const TFile* tf);
extern void tree_update_file(TFile* tf, off_t size, time_t mtime);
struct RealDir;
extern RealDir* tree_get_real_dir(TDir* td);
// for use in vfs_mount
extern void tree_lock();
extern void tree_unlock();

View File

@ -1,13 +0,0 @@
extern int res_reload(const char* fn);
// the following functions must be called from the same thread!
// (wdir_watch limitation)
extern int res_watch_dir(const char* path, intptr_t* watch);
extern int res_cancel_watch(const intptr_t watch);
extern int res_reload_changed_files(void);

View File

@ -1,133 +0,0 @@
#include "precompiled.h"
#include "res.h"
#include "file/file.h" // file_make_native_path, file_invalidate_cache
#include "timer.h"
#include "hotload.h" // we implement that interface
#include "sysdep/dir_watch.h"
#include <string.h>
// called by res_reload and res_reload_changed_files (which will already
// have rebuilt the VFS - doing so more than once a frame is unnecessary).
static int reload_without_rebuild(const char* fn)
{
// invalidate this file's cached blocks to make sure its contents are
// loaded anew.
CHECK_ERR(file_invalidate_cache(fn));
CHECK_ERR(h_reload(fn));
return 0;
}
// called via console command.
int res_reload(const char* fn)
{
// if <fn> currently maps to an archive, the VFS must switch
// over to using the loose file (that was presumably changed).
CHECK_ERR(vfs_rebuild());
return reload_without_rebuild(fn);
}
int res_watch_dir(const char* path, intptr_t* watch)
{
char n_path[PATH_MAX];
CHECK_ERR(file_make_full_native_path(path, n_path));
return dir_add_watch(n_path, watch);
}
int res_cancel_watch(const intptr_t watch)
{
return dir_cancel_watch(watch);
}
// get directory change notifications, and reload all affected files.
// must be called regularly (e.g. once a frame). this is much simpler
// than asynchronous notifications: everything would need to be thread-safe.
int res_reload_changed_files()
{
// array of reloads requested this frame (see 'do we really need to
// reload' below). go through gyrations to avoid heap allocs.
const size_t MAX_RELOADS_PER_FRAME = 12;
typedef char Path[VFS_MAX_PATH];
typedef Path PathList[MAX_RELOADS_PER_FRAME];
PathList pending_reloads;
uint num_pending = 0;
// process only as many notifications as we have room for; the others
// will be handled next frame. it's not imagineable that they'll pile up.
while(num_pending < MAX_RELOADS_PER_FRAME)
{
// get next notification
char n_path[PATH_MAX];
int ret = dir_get_changed_file(n_path);
CHECK_ERR(ret); // error? (doesn't cover 'none available')
if(ret != 0) // none available; done.
break;
// convert to VFS path
char p_path[PATH_MAX];
CHECK_ERR(file_make_full_portable_path(n_path, p_path));
char* vfs_path = pending_reloads[num_pending];
CHECK_ERR(vfs_make_vfs_path(p_path, vfs_path));
// do we really need to reload? try to avoid the considerable cost of
// rebuilding VFS and scanning all Handles.
//
// note: be careful to avoid 'race conditions' depending on the
// timeframe in which notifications reach us.
// example: editor deletes a.tga; we are notified; reload is
// triggered but fails since the file isn't found; further
// notifications (e.g. renamed a.tmp to a.tga) come within x [ms] and
// are ignored due to a time limit.
// therefore, we can only check for multiple reload requests a frame;
// to that purpose, an array is built and duplicates ignored.
const char* ext = strrchr(vfs_path, '.');
// .. directory change notification; ignore because we get
// per-file notifications anyway. (note: assume no extension =>
// it's a directory). this also protects the strcmp calls below.
if(!ext)
continue;
// .. compiled XML files the engine writes out by the hundreds;
// skipping them is a big performance gain.
if(!strcmp(ext, ".xmb"))
continue;
// .. temp files, usually created when an editor saves a file
// (delete, create temp, rename temp); no need to reload those.
if(!strcmp(ext, ".tmp"))
continue;
// .. more than one notification for a file; only reload once.
// note: this doesn't suffer from the 'reloaded too early'
// problem described above; if there's more than one
// request in the array, the file has since been written.
for(uint i = 0; i < num_pending; i++)
if(!strcmp(pending_reloads[i], vfs_path))
continue;
// path has already been written to pending_reloads,
// so just mark it valid.
num_pending++;
}
// rebuild VFS, in case a file that has been changed is currently
// mounted from an archive (reloading would just grab the unchanged
// version in the archive). the rebuild sees differing mtimes and
// always choses the loose file version. only do this once
// (instead of per reload request) because it's slow (> 1s)!
if(num_pending != 0)
CHECK_ERR(vfs_rebuild());
// now actually reload all files in the array we built
for(uint i = 0; i < num_pending; i++)
CHECK_ERR(reload_without_rebuild(pending_reloads[i]));
return 0;
}

View File

@ -233,8 +233,10 @@ fail:
int aio_close(int fd)
{
// early out for files that were never re-opened for AIO.
// since there is no way for wposix close to know this, we mustn't
// return an error (which would cause it to WARN_ERR).
if(!aio_h_is_set(fd))
return -1;
return 0;
HANDLE h = aio_h_get(fd);
// out of bounds or already closed

View File

@ -280,6 +280,7 @@ static void* prof_thread_func(void* UNUSED(data))
debug_warn("wpcu prof_thread_func: sem_timedwait failed");
uintptr_t pc = get_target_pc();
UNUSED2(pc);
// ADD TO LIST
}

View File

@ -249,7 +249,7 @@ func_and_arg;
// bridge calling conventions required by _beginthreadex and POSIX.
static unsigned __stdcall thread_start(void* param)
static unsigned __stdcall thread_start(void* UNUSED(param))
{
void*(*func)(void*) = func_and_arg.func;
void* arg = func_and_arg.arg;

View File

@ -18,7 +18,6 @@
#include "lib/timer.h"
#include "lib/input.h"
#include "lib/res/res.h"
#include "lib/res/hotload.h" // xxx
#include "lib/res/sound/snd.h"
#include "lib/res/graphics/tex.h"
#include "lib/res/graphics/cursor.h"
@ -1377,7 +1376,7 @@ static void Frame()
PROFILE_START( "reload changed files" );
MICROLOG(L"reload files");
res_reload_changed_files();
vfs_reload_changed_files();
PROFILE_END( "reload changed files" );
oglCheck();

View File

@ -312,7 +312,6 @@ void CModelRData::RenderModels(u32 streamflags,u32 flags)
#if 1
// submit batches for each model to the vertex buffer
for (i=0;i<m_Models.size();++i) {
u32 mflags=m_Models[i]->GetFlags(); // TODO2
if (!flags || (m_Models[i]->GetFlags()&flags)) {
CModelRData* modeldata=(CModelRData*) m_Models[i]->GetRenderData();
modeldata->SubmitBatches();

View File

@ -41,11 +41,14 @@
#define LOG_CATEGORY "graphics"
/*
// jw: unused
static bool saveTGA(const char* filename,int width,int height,int bpp,unsigned char* data)
{
int err = tex_write(filename, width, height, bpp, TEX_BGR, data);
return (err == 0);
}
*/
///////////////////////////////////////////////////////////////////////////////////
// CRenderer destructor