From 2aa43ea93c529d5fc899143a0ceaa9fb80f69332 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Mon, 8 Nov 2004 22:13:56 +0000 Subject: [PATCH] allow concurrent file requests; we only need exclusive access for Mount/Unmount --- stepmania/src/RageFileManager.cpp | 198 ++++++++++++++++++++++-------- 1 file changed, 148 insertions(+), 50 deletions(-) diff --git a/stepmania/src/RageFileManager.cpp b/stepmania/src/RageFileManager.cpp index a6b114bfdf..015f17c22a 100644 --- a/stepmania/src/RageFileManager.cpp +++ b/stepmania/src/RageFileManager.cpp @@ -15,7 +15,7 @@ RageFileManager *FILEMAN = NULL; /* Lock this before touching any of these globals (except FILEMAN itself). */ -static RageMutex *g_Mutex; +static RageEvent *g_Mutex; CString InitialWorkingDirectory; CString DirOfExecutable; @@ -33,12 +33,67 @@ struct LoadedDriver RageFileDriver *driver; CString Type, Root, MountPoint; - LoadedDriver() { driver = NULL; } + int m_iRefs; + + LoadedDriver() { driver = NULL; m_iRefs = 0; } CString GetPath( const CString &path ); }; static vector g_Drivers; +void ReferenceAllDrivers( vector &aDriverList ) +{ + g_Mutex->Lock(); + aDriverList = g_Drivers; + for( unsigned i = 0; i < aDriverList.size(); ++i ) + ++aDriverList[i].m_iRefs; + g_Mutex->Unlock(); +} + +void UnreferenceAllDrivers( vector &aDriverList ) +{ + g_Mutex->Lock(); + for( unsigned i = 0; i < aDriverList.size(); ++i ) + --aDriverList[i].m_iRefs; + g_Mutex->Broadcast(); + g_Mutex->Unlock(); + + /* Clear the temporary list, to make it clear that the drivers may no longer be accessed. */ + aDriverList.clear(); +} + +/* Wait for the given driver to become unreferenced, and remove it from the list + * to get exclusive access to it. Returns false if the driver is no longer available + * (somebody else got it first). */ +bool GrabDriver( RageFileDriver *pDriver ) +{ + g_Mutex->Lock(); + + while(1) + { + unsigned i; + for( i = 0; i < g_Drivers.size(); ++i ) + if( g_Drivers[i].driver == pDriver ) + break; + + if( i == g_Drivers.size() ) + { + g_Mutex->Unlock(); + return false; + } + + if( g_Drivers[i].m_iRefs == 0 ) + { + g_Drivers.erase( g_Drivers.begin()+i ); + return true; + } + + /* The driver is in use. Wait for somebody to release a driver, and + * try again. */ + g_Mutex->Wait(); + } +} + // Mountpoints as directories cause a problem. If "Themes/default" is a mountpoint, and // doesn't exist anywhere else, then GetDirListing("Themes/*") must return "default". The // driver containing "Themes/default" won't do this; its world view begins at "BGAnimations" @@ -126,7 +181,7 @@ RageFileManager::RageFileManager( CString argv0 ) CHECKPOINT_M( argv0 ); ChangeToDirOfExecutable( argv0 ); - g_Mutex = new RageMutex("RageFileManager"); + g_Mutex = new RageEvent("RageFileManager"); g_Mountpoints = new RageFileDriverMountpoints; LoadedDriver ld; @@ -241,20 +296,21 @@ bool ilt( const CString &a, const CString &b ) { return a.CompareNoCase(b) < 0; bool ieq( const CString &a, const CString &b ) { return a.CompareNoCase(b) == 0; } void RageFileManager::GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo ) { - LockMut( *g_Mutex ); - NormalizePath( sPath ); - - for( unsigned i = 0; i < g_Drivers.size(); ++i ) + + vector aDriverList; + ReferenceAllDrivers( aDriverList ); + + for( unsigned i = 0; i < aDriverList.size(); ++i ) { - LoadedDriver &ld = g_Drivers[i]; + LoadedDriver &ld = aDriverList[i]; const CString p = ld.GetPath( sPath ); if( p.size() == 0 ) continue; const unsigned OldStart = AddTo.size(); - g_Drivers[i].driver->GetDirListing( p, AddTo, bOnlyDirs, bReturnPathToo ); + ld.driver->GetDirListing( p, AddTo, bOnlyDirs, bReturnPathToo ); /* If returning the path, prepend the mountpoint name to the files this driver returned. */ if( bReturnPathToo ) @@ -262,6 +318,8 @@ void RageFileManager::GetDirListing( CString sPath, CStringArray &AddTo, bool bO AddTo[j] = ld.MountPoint + AddTo[j]; } + UnreferenceAllDrivers( aDriverList ); + /* More than one driver might return the same file. Remove duplicates (case- * insensitively). */ sort( AddTo.begin(), AddTo.end(), ilt ); @@ -271,23 +329,26 @@ void RageFileManager::GetDirListing( CString sPath, CStringArray &AddTo, bool bO bool RageFileManager::Remove( CString sPath ) { - LockMut( *g_Mutex ); + vector aDriverList; + ReferenceAllDrivers( aDriverList ); NormalizePath( sPath ); /* Multiple drivers may have the same file. */ bool Deleted = false; - for( unsigned i = 0; i < g_Drivers.size(); ++i ) + for( unsigned i = 0; i < aDriverList.size(); ++i ) { - const CString p = g_Drivers[i].GetPath( sPath ); + const CString p = aDriverList[i].GetPath( sPath ); if( p.size() == 0 ) continue; - bool ret = g_Drivers[i].driver->Remove( p ); + bool ret = aDriverList[i].driver->Remove( p ); if( ret ) Deleted = true; } + UnreferenceAllDrivers( aDriverList ); + return Deleted; } @@ -307,8 +368,6 @@ void RageFileManager::CreateDir( CString sDir ) void RageFileManager::Mount( CString Type, CString Root, CString MountPoint ) { - LockMut( *g_Mutex ); - FixSlashesInPlace( Root ); FixSlashesInPlace( MountPoint ); @@ -342,23 +401,27 @@ void RageFileManager::Mount( CString Type, CString Root, CString MountPoint ) ld.Type = Type; ld.Root = Root; ld.MountPoint = MountPoint; - g_Drivers.push_back( ld ); + g_Mutex->Lock(); + g_Drivers.push_back( ld ); CHECKPOINT; g_Mountpoints->LoadFromDrivers( g_Drivers ); CHECKPOINT; + g_Mutex->Unlock(); } void RageFileManager::Unmount( CString Type, CString Root, CString MountPoint ) { - LockMut( *g_Mutex ); - FixSlashesInPlace( Root ); FixSlashesInPlace( MountPoint ); if( MountPoint.size() && MountPoint.Right(1) != "/" ) MountPoint += '/'; + /* Find all drivers we want to delete. Remove them from g_Drivers, and move them + * into aDriverListToUnmount. */ + vector aDriverListToUnmount; + g_Mutex->Lock(); for( unsigned i = 0; i < g_Drivers.size(); ++i ) { if( g_Drivers[i].Type.CompareNoCase( Type ) ) @@ -368,11 +431,30 @@ void RageFileManager::Unmount( CString Type, CString Root, CString MountPoint ) if( g_Drivers[i].MountPoint.CompareNoCase( MountPoint ) ) continue; - delete g_Drivers[i].driver; + ++g_Drivers[i].m_iRefs; + aDriverListToUnmount.push_back( g_Drivers[i] ); g_Drivers.erase( g_Drivers.begin()+i ); + --i; } g_Mountpoints->LoadFromDrivers( g_Drivers ); + + g_Mutex->Unlock(); + + /* Now we have a list of drivers to remove. */ + while( aDriverListToUnmount.size() ) + { + /* If the driver has more than one reference, somebody other than us is + * using it; wait for that operation to complete. Note that two Unmount() + * calls that want to remove the same mountpoint will deadlock here. */ + g_Mutex->Lock(); + while( aDriverListToUnmount[0].m_iRefs > 1 ) + g_Mutex->Wait(); + g_Mutex->Unlock(); + + delete aDriverListToUnmount[0].driver; + aDriverListToUnmount.erase( aDriverListToUnmount.begin() ); + } } bool RageFileManager::IsMounted( CString MountPoint ) @@ -390,21 +472,25 @@ bool RageFileManager::IsMounted( CString MountPoint ) * inserted). */ bool RageFileManager::MountpointIsReady( CString MountPoint ) { - LockMut( *g_Mutex ); + vector aDriverList; + ReferenceAllDrivers( aDriverList ); - for( unsigned i = 0; i < g_Drivers.size(); ++i ) + for( unsigned i = 0; i < aDriverList.size(); ++i ) { - if( g_Drivers[i].MountPoint.CompareNoCase( MountPoint ) ) + if( aDriverList[i].MountPoint.CompareNoCase( MountPoint ) ) continue; - return g_Drivers[i].driver->Ready(); + return aDriverList[i].driver->Ready(); } + UnreferenceAllDrivers( aDriverList ); return false; } void RageFileManager::GetLoadedDrivers( vector &Mounts ) { + LockMut( *g_Mutex ); + for( unsigned i = 0; i < g_Drivers.size(); ++i ) { DriverLocation l; @@ -417,10 +503,10 @@ void RageFileManager::GetLoadedDrivers( vector &Mounts ) void RageFileManager::FlushDirCache( CString sPath ) { - LockMut( *g_Mutex ); - NormalizePath( sPath ); + LockMut( *g_Mutex ); + for( unsigned i = 0; i < g_Drivers.size(); ++i ) { if( sPath.size() == 0 ) @@ -439,61 +525,67 @@ void RageFileManager::FlushDirCache( CString sPath ) RageFileManager::FileType RageFileManager::GetFileType( CString sPath ) { - LockMut( *g_Mutex ); - NormalizePath( sPath ); - for( unsigned i = 0; i < g_Drivers.size(); ++i ) + vector aDriverList; + ReferenceAllDrivers( aDriverList ); + + for( unsigned i = 0; i < aDriverList.size(); ++i ) { - const CString p = g_Drivers[i].GetPath( sPath ); + const CString p = aDriverList[i].GetPath( sPath ); if( p.size() == 0 ) continue; - FileType ret = g_Drivers[i].driver->GetFileType( p ); + FileType ret = aDriverList[i].driver->GetFileType( p ); if( ret != TYPE_NONE ) return ret; } + UnreferenceAllDrivers( aDriverList ); + return TYPE_NONE; } int RageFileManager::GetFileSizeInBytes( CString sPath ) { - LockMut( *g_Mutex ); - NormalizePath( sPath ); - for( unsigned i = 0; i < g_Drivers.size(); ++i ) + vector aDriverList; + ReferenceAllDrivers( aDriverList ); + + for( unsigned i = 0; i < aDriverList.size(); ++i ) { - const CString p = g_Drivers[i].GetPath( sPath ); + const CString p = aDriverList[i].GetPath( sPath ); if( p.size() == 0 ) continue; - int ret = g_Drivers[i].driver->GetFileSizeInBytes( p ); + int ret = aDriverList[i].driver->GetFileSizeInBytes( p ); if( ret != -1 ) return ret; } + UnreferenceAllDrivers( aDriverList ); return -1; } int RageFileManager::GetFileHash( CString sPath ) { - LockMut( *g_Mutex ); - NormalizePath( sPath ); - for( unsigned i = 0; i < g_Drivers.size(); ++i ) + vector aDriverList; + ReferenceAllDrivers( aDriverList ); + + for( unsigned i = 0; i < aDriverList.size(); ++i ) { - const CString p = g_Drivers[i].GetPath( sPath ); + const CString p = aDriverList[i].GetPath( sPath ); if( p.size() == 0 ) continue; - int ret = g_Drivers[i].driver->GetFileHash( p ); + int ret = aDriverList[i].driver->GetFileHash( p ); if( ret != -1 ) return ret; } + UnreferenceAllDrivers( aDriverList ); return -1; - } static bool SortBySecond( const pair &a, const pair &b ) @@ -548,8 +640,6 @@ static bool PathUsesSlowFlush( const CString &sPath ) /* Used only by RageFile: */ RageFileObj *RageFileManager::Open( CString sPath, int mode, RageFile &p, int &err ) { - LockMut( *g_Mutex ); - err = ENOENT; if( (mode & RageFile::WRITE) && PathUsesSlowFlush(sPath) ) @@ -562,9 +652,12 @@ RageFileObj *RageFileManager::Open( CString sPath, int mode, RageFile &p, int &e NormalizePath( sPath ); - for( unsigned i = 0; i < g_Drivers.size(); ++i ) + vector aDriverList; + ReferenceAllDrivers( aDriverList ); + + for( unsigned i = 0; i < aDriverList.size(); ++i ) { - LoadedDriver &ld = g_Drivers[i]; + LoadedDriver &ld = aDriverList[i]; const CString path = ld.GetPath( sPath ); if( path.size() == 0 ) continue; @@ -573,6 +666,7 @@ RageFileObj *RageFileManager::Open( CString sPath, int mode, RageFile &p, int &e if( ret ) { AddReference( ret, ld.driver ); + UnreferenceAllDrivers( aDriverList ); return ret; } @@ -581,6 +675,7 @@ RageFileObj *RageFileManager::Open( CString sPath, int mode, RageFile &p, int &e if( error != ENOENT ) err = error; } + UnreferenceAllDrivers( aDriverList ); return NULL; } @@ -603,8 +698,6 @@ RageFileObj *RageFileManager::CopyFileObj( const RageFileObj *cpy, RageFile &p ) RageFileObj *RageFileManager::OpenForWriting( CString sPath, int mode, RageFile &p, int &err ) { - LockMut( *g_Mutex ); - /* * The value for a driver to open a file is the number of directories and/or files * that would have to be created in order to write it, or 0 if the file already exists. @@ -626,10 +719,13 @@ RageFileObj *RageFileManager::OpenForWriting( CString sPath, int mode, RageFile */ NormalizePath( sPath ); + vector aDriverList; + ReferenceAllDrivers( aDriverList ); + vector< pair > Values; - for( unsigned i = 0; i < g_Drivers.size(); ++i ) + for( unsigned i = 0; i < aDriverList.size(); ++i ) { - LoadedDriver &ld = g_Drivers[i]; + LoadedDriver &ld = aDriverList[i]; const CString path = ld.GetPath( sPath ); if( path.size() == 0 ) continue; @@ -647,7 +743,7 @@ RageFileObj *RageFileManager::OpenForWriting( CString sPath, int mode, RageFile for( unsigned i = 0; i < Values.size(); ++i ) { const int driver = Values[i].first; - LoadedDriver &ld = g_Drivers[driver]; + LoadedDriver &ld = aDriverList[driver]; const CString path = ld.GetPath( sPath ); ASSERT( path.size() ); @@ -668,6 +764,8 @@ RageFileObj *RageFileManager::OpenForWriting( CString sPath, int mode, RageFile if( !err ) err = EEXIST; /* no driver could write */ + UnreferenceAllDrivers( aDriverList ); + return NULL; }