implementation complete, hopefully :P

This was SVN commit r427.
This commit is contained in:
janwas 2004-06-08 15:22:40 +00:00
parent 825289242a
commit 53425fa4b5
2 changed files with 89 additions and 61 deletions

View File

@ -30,6 +30,8 @@
// no module init/shutdown necessary: all global data is allocated
// as part of a FAMConnection, which must be FAMClose-d by caller.
//
// that means every routine has a FAMConnection* parameter, but it's safer.
// rationale for polling:
@ -70,7 +72,7 @@
// to this struct, due to the pImpl idiom. this is heap-allocated.
struct Watch
{
const std::string dir_name;
std::string dir_name;
HANDLE hDir;
// user pointer from from FAMMonitorDirectory; passed to FAMEvent
@ -91,17 +93,11 @@ struct Watch
// we miss changes made to a directory.
// issue code uses sizeof(change_buf) to determine size.
FAMConnection* fc;
FAMRequest fr;
// these are returned in FAMEvent. could get them via FAMNextEvent's
// fc parameter and associating packets with FAMRequest,
// but storing them here is more convenient.
Watch(const std::string& _dir_name, HANDLE _hDir)
: dir_name(_dir_name), last_path("")
Watch()
: last_path("")
{
hDir = _hDir;
hDir = INVALID_HANDLE_VALUE;
last_action = 0;
last_ticks = 0;
@ -117,9 +113,15 @@ struct Watch
};
// list of all active watches to detect duplicates and
// for easier cleanup. only store pointer in container -
// they're not copy-equivalent (dtor would close hDir).
// list of all active watches. required, since we have to be able to
// cancel watches; also makes detecting duplicates possible.
//
// only store pointer in container - they're not copy-equivalent
// (dtor would close hDir).
//
// key is uint reqnum - that's what FAMCancelMonitor is passed.
// reqnums aren't reused to avoid problems with stale reqnums after
// cancelling; hence, map instead of vector and freelist.
typedef std::map<uint, Watch*> Watches;
typedef Watches::iterator WatchIt;
@ -152,13 +154,13 @@ struct AppState
uint last_reqnum;
AppState(const char* _app_name)
: app_name(_app_name)
AppState()
{
hIOCP = 0;
// not INVALID_HANDLE_VALUE! (CreateIoCompletionPort requirement)
last_reqnum = 0;
// provide a little protection against random reqnums passed in
last_reqnum = 1000;
}
~AppState()
@ -187,6 +189,33 @@ struct AppState
return -1;\
}
///////////////////////////////////////////////////////////////////////////////
static int alloc_watch(FAMConnection* const fc, const FAMRequest* const fr, Watch*& _w)
{
GET_APP_STATE(fc, state);
Watches& watches = state->watches;
SAFE_NEW(Watch, w);
watches[fr->reqnum] = w;
_w = w;
return 0;
}
static int free_watch(FAMConnection* const fc, const FAMRequest* const fr, Watch*& w)
{
GET_APP_STATE(fc, state);
Watches& watches = state->watches;
watches.erase(fr->reqnum);
w = 0;
return 0;
}
static int find_watch(FAMConnection* const fc, const FAMRequest* const fr, Watch*& w)
{
GET_APP_STATE(fc, state);
@ -200,24 +229,18 @@ static int find_watch(FAMConnection* const fc, const FAMRequest* const fr, Watch
#define GET_WATCH(fc, fr, watch_ptr_var)\
Watch* watch_ptr_var;\
CHECK_ERR(find_watch(fc, fr, watch_ptr_var);
CHECK_ERR(find_watch(fc, fr, watch_ptr_var))
///////////////////////////////////////////////////////////////////////////////
int FAMOpen2(FAMConnection* const fc, const char* const app_name)
{
try
{
fc->internal = new AppState(app_name);
}
catch(std::bad_alloc)
{
fc->internal = 0;
}
// either (VC6) new returned 0, or we caught bad_alloc => fail.
if(!fc->internal)
return -1;
SAFE_NEW(AppState, state);
state->app_name = app_name;
fc->internal = state;
return 0;
}
@ -233,7 +256,7 @@ int FAMClose(FAMConnection* const fc)
// HACK - see call site
static int get_packet(FAMConnection* vc);
static int get_packet(FAMConnection* fc);
int FAMMonitorDirectory(FAMConnection* const fc, const char* const _dir, FAMRequest* const fr, void* const user_data)
@ -276,17 +299,22 @@ int FAMMonitorDirectory(FAMConnection* const fc, const char* const _dir, FAMRequ
hIOCP = CreateIoCompletionPort(hDir, hIOCP, key, 0);
if(hIOCP == 0 || hIOCP == INVALID_HANDLE_VALUE)
{
fail:
CloseHandle(hDir);
return -1;
}
// create Watch and associate with FAM structs
Watch* const w = new Watch(dir, hDir);
watches[reqnum] = w;
w->fc = fc;
w->fr = *fr;
Watch* w;
if(alloc_watch(fc, fr, w) < 0)
goto fail;
w->dir_name = dir;
w->hDir = hDir;
w->user_data = user_data;
if(dir[dir.length()-1] != '\\')
w->dir_name += '\\';
// post a dummy kickoff packet; the IOCP polling code will "re"issue
// the corresponding watch. this keeps the ReadDirectoryChangesW call
// and directory <--> Watch association code in one place.
@ -302,28 +330,29 @@ int FAMMonitorDirectory(FAMConnection* const fc, const char* const _dir, FAMRequ
int FAMCancelMonitor(FAMConnection* const fc, FAMRequest* const fr)
{
GET_APP_STATE(fc, state);
// GET_WATCH(fc, fr, w);
GET_WATCH(fc, fr, w);
// contrary to dox, the RDC IOs do not "complete" - no packet received on the IOCP in test.
/// int a = CancelIo(w->hDir);
// contrary to dox, the RDC IOs do not issue a completion notification.
// no packet was received on the IOCP while or after cancelling in a test.
//
// if cancel somehow fails though, no matter - the Watch is freed, and
// its reqnum isn't reused; if we receive a packet, it's ignored.
CancelIo(w->hDir);
free_watch(fc, fr, w); // can't fail
return 0;
}
static int extract_events(Watch* const w)
static int extract_events(FAMConnection* fc, FAMRequest* fr, Watch* w)
{
FAMConnection* const fc = w->fc;
const FAMRequest& fr = w->fr;
GET_APP_STATE(fc, state);
Events& events = state->pending_events;
// will be modified for each event and added to events
FAMEvent event_template;
event_template.fc = fc;
event_template.fr = fr;
event_template.fr = *fr;
event_template.user = w->user_data;
// points to current FILE_NOTIFY_INFORMATION;
@ -374,19 +403,13 @@ static int extract_events(Watch* const w)
event.code = code;
//
// convert filename from Windows BSTR to portable C string
//
char* fn = event.filename;
const int num_chars = fni->FileNameLength/2;
for(int i = 0; i < num_chars; i++)
{
char c = (char)fni->FileName[i];
if(c == '\\')
c = '/';
*fn++ = c;
}
// build filename
// (prepend directory and convert from Windows BSTR)
strcpy(event.filename, w->dir_name.c_str());
char* fn = event.filename + w->dir_name.length();
// .. can't use wcstombs - FileName isn't 0-terminated
for(int i = 0; i < (int)fni->FileNameLength/2; i++)
*fn++ = (char)fni->FileName[i];
*fn = '\0';
@ -404,7 +427,7 @@ static int extract_events(Watch* const w)
// if a packet is pending, extract its events and re-issue its watch.
int get_packet(FAMConnection* fc)
static int get_packet(FAMConnection* fc)
{
GET_APP_STATE(fc, state);
const HANDLE hIOCP = state->hIOCP;
@ -418,14 +441,16 @@ int get_packet(FAMConnection* fc)
if(!got_packet) // no new packet - done
return 1;
const FAMRequest fr = { (uint)key };
Watch* w;
CHECK_ERR(find_watch(fc, &fr, w));
FAMRequest _fr = { (uint)key };
FAMRequest* const fr = &_fr;
// if other fields are added, their value is 0;
// find_watch only looks at reqnum anyway.
GET_WATCH(fc, fr, w);
// this is an actual packet, not just a kickoff for issuing the watch.
// extract the events and push them onto AppState's queue.
if(bytes_transferred != 0)
extract_events(w);
extract_events(fc, fr, w);
// (re-)issue change notification request.
// it's safe to reuse Watch.change_buf, because we copied out all events.

View File

@ -53,6 +53,9 @@ typedef struct
FAMEvent;
// these functions must be called from the same thread!
// (Win32 overlapped I/O limitation)
extern int FAMOpen2(FAMConnection*, const char* app_name);
extern int FAMClose(FAMConnection*);