fix race condition causing corruption if FlushDirCache() is called from

one thread while another thread is simultaneously filling in a FileSet:
you don't really own a FileSet if m_bFilled is false
This commit is contained in:
Glenn Maynard
2005-02-06 09:56:21 +00:00
parent cc1454dd46
commit 39e5c30706
+42 -9
View File
@@ -272,6 +272,9 @@ FileSet *FilenameDB::GetFileSet( CString dir, bool create )
m_Mutex.Lock(); m_Mutex.Lock();
while(1)
{
/* Look for the directory. */
map<CString, FileSet *>::iterator i = dirs.find( lower ); map<CString, FileSet *>::iterator i = dirs.find( lower );
if( !create ) if( !create )
{ {
@@ -280,15 +283,24 @@ FileSet *FilenameDB::GetFileSet( CString dir, bool create )
return i->second; return i->second;
} }
FileSet *ret; /* We're allowed to create. If the directory wasn't found, break out and
if( i != dirs.end() ) * create it. */
{ if( i == dirs.end() )
break;
/* This directory already exists. If it's still being filled in by another /* This directory already exists. If it's still being filled in by another
* thread, wait for it. */ * thread, wait for it. */
FileSet *pFileSet = i->second; FileSet *pFileSet = i->second;
while( !pFileSet->m_bFilled ) if( !pFileSet->m_bFilled )
{
m_Mutex.Wait(); m_Mutex.Wait();
/* Beware: when we unlock m_Mutex to wait for it to finish filling,
* we give up our claim to dirs, so i may be invalid. Start over
* and re-search. */
continue;
}
if( ExpireSeconds == -1 || pFileSet->age.PeekDeltaTime() < ExpireSeconds ) if( ExpireSeconds == -1 || pFileSet->age.PeekDeltaTime() < ExpireSeconds )
{ {
/* Found it, and it hasn't expired. */ /* Found it, and it hasn't expired. */
@@ -297,11 +309,12 @@ FileSet *FilenameDB::GetFileSet( CString dir, bool create )
/* It's expired. Delete the old entry. */ /* It's expired. Delete the old entry. */
this->DelFileSet( i ); this->DelFileSet( i );
break;
} }
/* Create the FileSet and insert it. Set it to !m_bFilled, so if other threads /* Create the FileSet and insert it. Set it to !m_bFilled, so if other threads
* happen to try to use this directory before we finish filling it, they'll wait. */ * happen to try to use this directory before we finish filling it, they'll wait. */
ret = new FileSet; FileSet *ret = new FileSet;
ret->m_bFilled = false; ret->m_bFilled = false;
dirs[lower] = ret; dirs[lower] = ret;
@@ -452,10 +465,30 @@ void FilenameDB::DelFile( const CString &sPath )
void FilenameDB::FlushDirCache() void FilenameDB::FlushDirCache()
{ {
LockMut(m_Mutex); while(1)
for( map<CString, FileSet *>::iterator i = dirs.begin(); i != dirs.end(); ++i ) {
delete i->second; m_Mutex.Lock();
dirs.clear(); if( dirs.empty() )
{
m_Mutex.Unlock();
break;
}
/* Grab the first entry. Take it out of the list while we hold the
* lock, to guarantee that we own it. */
FileSet *pFileSet = dirs.begin()->second;
dirs.erase( dirs.begin() );
/* If it's being filled, we don't really own it until it's finished being
* filled, so wait. */
while( !pFileSet->m_bFilled )
m_Mutex.Wait();
m_Mutex.Unlock();
delete pFileSet;
}
} }
const File *FilenameDB::GetFile( const CString &sPath ) const File *FilenameDB::GetFile( const CString &sPath )