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
+47 -14
View File
@@ -272,23 +272,35 @@ FileSet *FilenameDB::GetFileSet( CString dir, bool create )
m_Mutex.Lock(); m_Mutex.Lock();
map<CString, FileSet *>::iterator i = dirs.find( lower ); while(1)
if( !create )
{ {
/* Look for the directory. */
map<CString, FileSet *>::iterator i = dirs.find( lower );
if( !create )
{
if( i == dirs.end() )
return NULL;
return i->second;
}
/* We're allowed to create. If the directory wasn't found, break out and
* create it. */
if( i == dirs.end() ) if( i == dirs.end() )
return NULL; break;
return i->second;
}
FileSet *ret;
if( i != dirs.end() )
{
/* 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 )