Add a memory card WorkerThread. This works simiilarly to the file

worker threads (though somewhat simpler).  Changes: MemoryCardDriverThreaded
is going away; all memory card drivers are implicitly threaded at a higher
level (there's no reason to not do so).  Memory card drivers can safely time
out; we'll recover sanely.  This needs a bit more work, but should be more
tolerant of long memory card recovery periods.
This commit is contained in:
Glenn Maynard
2005-02-05 19:00:33 +00:00
parent 93b70b137d
commit d468e15435
2 changed files with 349 additions and 138 deletions
+236 -22
View File
@@ -7,10 +7,12 @@
#include "RageLog.h" #include "RageLog.h"
#include "RageFileManager.h" #include "RageFileManager.h"
#include "RageFileDriver.h" #include "RageFileDriver.h"
#include "RageFileDriverTimeout.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "ProfileManager.h" #include "ProfileManager.h"
#include "Foreach.h" #include "Foreach.h"
#include "GameState.h" #include "GameState.h"
#include "RageUtil_WorkerThread.h"
MemoryCardManager* MEMCARDMAN = NULL; // global and accessable from anywhere in our program MemoryCardManager* MEMCARDMAN = NULL; // global and accessable from anywhere in our program
@@ -28,9 +30,214 @@ static const CString MEM_CARD_MOUNT_POINT_INTERNAL[NUM_PLAYERS] =
"@mc2int/", "@mc2int/",
}; };
MemoryCardManager::MemoryCardManager() /* Only access the memory card driver in a timeout-safe thread. */
class ThreadedMemoryCardWorker: public WorkerThread
{
public:
ThreadedMemoryCardWorker();
~ThreadedMemoryCardWorker();
enum MountThreadState
{
detect_and_mount,
detect_and_dont_mount,
paused
};
void SetMountThreadState( MountThreadState mts );
/* These functions may time out. */
bool Mount( const UsbStorageDevice *pDevice );
bool Unmount( const UsbStorageDevice *pDevice );
bool Flush( const UsbStorageDevice *pDevice );
void Reset();
/* This function will not time out. */
bool StorageDevicesChanged( vector<UsbStorageDevice> &aOut );
protected:
void HandleRequest( int iRequest );
void RequestTimedOut();
void DoHeartbeat();
private:
MemoryCardDriver *m_pDriver;
MountThreadState m_MountThreadState;
/* We make a copy of the device info we're working with, since the pointer
* we're given will become invalid if the operation times out and DoRequest
* returns. */
UsbStorageDevice m_RequestDevice;
bool m_bResult;
RageMutex UsbStorageDevicesMutex;
bool m_bUsbStorageDevicesChanged;
vector<UsbStorageDevice> m_aUsbStorageDevices;
enum
{
REQ_MOUNT,
REQ_UNMOUNT,
REQ_FLUSH,
REQ_RESET
};
};
bool ThreadedMemoryCardWorker::StorageDevicesChanged( vector<UsbStorageDevice> &aOut )
{
UsbStorageDevicesMutex.Lock();
if( !m_bUsbStorageDevicesChanged )
{
UsbStorageDevicesMutex.Unlock();
return false;
}
aOut = m_aUsbStorageDevices;
m_aUsbStorageDevices.clear();
m_bUsbStorageDevicesChanged = false;
UsbStorageDevicesMutex.Unlock();
return true;
}
ThreadedMemoryCardWorker::ThreadedMemoryCardWorker():
WorkerThread("ThreadedMemoryCardWorker"),
UsbStorageDevicesMutex("UsbStorageDevicesMutex")
{ {
m_pDriver = MakeMemoryCardDriver(); m_pDriver = MakeMemoryCardDriver();
m_MountThreadState = detect_and_mount;
SetHeartbeat( 0.1f );
StartThread();
}
ThreadedMemoryCardWorker::~ThreadedMemoryCardWorker()
{
StopThread();
delete m_pDriver;
}
void ThreadedMemoryCardWorker::SetMountThreadState( MountThreadState mts )
{
/* If "pause", stop calling updates in the heartbeat. In principle, we should
* also not return from this function until the current heartbeat, if running,
* finishes. However, since we can't guarantee that it'll exit within the timeout,
* there's no point: we have to return when we time out, and in that case the
* heartbeat will still be running. I don't know if the reasons for pausing
* really need us to wait, so don't. */
m_MountThreadState = mts;
}
void ThreadedMemoryCardWorker::HandleRequest( int iRequest )
{
switch( iRequest )
{
case REQ_MOUNT:
m_bResult = m_pDriver->Mount( &m_RequestDevice );
break;
case REQ_UNMOUNT:
m_pDriver->Unmount( &m_RequestDevice );
break;
case REQ_FLUSH:
m_pDriver->Flush( &m_RequestDevice );
break;
case REQ_RESET:
m_pDriver->Reset();
break;
}
}
void ThreadedMemoryCardWorker::RequestTimedOut()
{
}
void ThreadedMemoryCardWorker::DoHeartbeat()
{
if( m_MountThreadState == paused )
return;
/* If true, detect and mount. If false, only detect. */
bool bMount = (m_MountThreadState == detect_and_mount);
vector<UsbStorageDevice> aStorageDevices;
// LOG->Trace("update");
if( !m_pDriver->DoOneUpdate( bMount, aStorageDevices ) )
return;
UsbStorageDevicesMutex.Lock();
m_aUsbStorageDevices = aStorageDevices;
m_bUsbStorageDevicesChanged = true;
UsbStorageDevicesMutex.Unlock();
}
bool ThreadedMemoryCardWorker::Mount( const UsbStorageDevice *pDevice )
{
ASSERT( TimeoutEnabled() );
/* If we're currently in a timed-out state, fail. */
if( IsTimedOut() )
return false;
m_RequestDevice = *pDevice;
if( !DoRequest(REQ_MOUNT) )
return false;
return m_bResult;
}
bool ThreadedMemoryCardWorker::Unmount( const UsbStorageDevice *pDevice )
{
ASSERT( TimeoutEnabled() );
/* If we're currently in a timed-out state, fail. */
if( IsTimedOut() )
return false;
m_RequestDevice = *pDevice;
if( !DoRequest(REQ_UNMOUNT) )
return false;
return true;
}
bool ThreadedMemoryCardWorker::Flush( const UsbStorageDevice *pDevice )
{
ASSERT( TimeoutEnabled() );
/* If we're currently in a timed-out state, fail. */
if( IsTimedOut() )
return false;
m_RequestDevice = *pDevice;
if( !DoRequest(REQ_FLUSH) )
return false;
return true;
}
void ThreadedMemoryCardWorker::Reset()
{
ASSERT( TimeoutEnabled() );
/* If we're currently in a timed-out state, fail. */
if( IsTimedOut() )
return;
DoRequest( REQ_RESET );
}
static ThreadedMemoryCardWorker *g_pWorker = NULL;
MemoryCardManager::MemoryCardManager()
{
ASSERT( g_pWorker == NULL );
g_pWorker = new ThreadedMemoryCardWorker;
m_bCardsLocked = false; m_bCardsLocked = false;
FOREACH_PlayerNumber( p ) FOREACH_PlayerNumber( p )
{ {
@@ -47,7 +254,8 @@ MemoryCardManager::MemoryCardManager()
MemoryCardManager::~MemoryCardManager() MemoryCardManager::~MemoryCardManager()
{ {
delete m_pDriver; ASSERT( g_pWorker != NULL );
SAFE_DELETE(g_pWorker);
FOREACH_PlayerNumber( pn ) FOREACH_PlayerNumber( pn )
{ {
@@ -58,18 +266,18 @@ MemoryCardManager::~MemoryCardManager()
void MemoryCardManager::Update( float fDelta ) void MemoryCardManager::Update( float fDelta )
{ {
if( m_pDriver->StorageDevicesChanged() ) const vector<UsbStorageDevice> vOld = m_vStorageDevices; // copy
{ if( !g_pWorker->StorageDevicesChanged( m_vStorageDevices ) )
vector<UsbStorageDevice> vOld = m_vStorageDevices; // copy return;
m_pDriver->GetStorageDevices( m_vStorageDevices ); const vector<UsbStorageDevice> &vNew = m_vStorageDevices;
vector<UsbStorageDevice> &vNew = m_vStorageDevices;
vector<UsbStorageDevice> vConnects; // fill these in below vector<UsbStorageDevice> vConnects; // fill these in below
vector<UsbStorageDevice> vDisconnects; // fill these in below vector<UsbStorageDevice> vDisconnects; // fill these in below
// check for disconnects // check for disconnects
FOREACH( UsbStorageDevice, vOld, old ) FOREACH_CONST( UsbStorageDevice, vOld, old )
{ {
vector<UsbStorageDevice>::iterator iter = find( vNew.begin(), vNew.end(), *old ); vector<UsbStorageDevice>::const_iterator iter = find( vNew.begin(), vNew.end(), *old );
if( iter == vNew.end() ) // card no longer present if( iter == vNew.end() ) // card no longer present
{ {
LOG->Trace( "Disconnected bus %d port %d device %d path %s", old->iBus, old->iPort, old->iLevel, old->sOsMountDir.c_str() ); LOG->Trace( "Disconnected bus %d port %d device %d path %s", old->iBus, old->iPort, old->iLevel, old->sOsMountDir.c_str() );
@@ -78,9 +286,9 @@ void MemoryCardManager::Update( float fDelta )
} }
// check for connects // check for connects
FOREACH( UsbStorageDevice, vNew, newd ) FOREACH_CONST( UsbStorageDevice, vNew, newd )
{ {
vector<UsbStorageDevice>::iterator iter = find( vOld.begin(), vOld.end(), *newd ); vector<UsbStorageDevice>::const_iterator iter = find( vOld.begin(), vOld.end(), *newd );
if( iter == vOld.end() ) // card wasn't present last update if( iter == vOld.end() ) // card wasn't present last update
{ {
LOG->Trace( "Connected bus %d port %d device %d path %s", newd->iBus, newd->iPort, newd->iLevel, newd->sOsMountDir.c_str() ); LOG->Trace( "Connected bus %d port %d device %d path %s", newd->iBus, newd->iPort, newd->iLevel, newd->sOsMountDir.c_str() );
@@ -198,7 +406,6 @@ void MemoryCardManager::Update( float fDelta )
} }
SCREENMAN->RefreshCreditsMessages(); SCREENMAN->RefreshCreditsMessages();
}
} }
MemoryCardState MemoryCardManager::GetCardState( PlayerNumber pn ) MemoryCardState MemoryCardManager::GetCardState( PlayerNumber pn )
@@ -230,7 +437,7 @@ void MemoryCardManager::UnlockCards()
FOREACH_PlayerNumber( p ) FOREACH_PlayerNumber( p )
m_bTooLate[p] = false; m_bTooLate[p] = false;
m_pDriver->SetMountThreadState( MemoryCardDriver::detect_and_mount ); g_pWorker->SetMountThreadState( ThreadedMemoryCardWorker::detect_and_mount );
} }
/* Called just before reading or writing to the memory card. Should block. */ /* Called just before reading or writing to the memory card. Should block. */
@@ -251,7 +458,7 @@ void MemoryCardManager::MountCard( PlayerNumber pn )
this->PauseMountingThread(); this->PauseMountingThread();
} }
if( !m_pDriver->MountAndTestWrite(&m_Device[pn]) ) if( !g_pWorker->Mount( &m_Device[pn] ) )
{ {
if( bStartingMemoryCardAccess ) if( bStartingMemoryCardAccess )
this->UnPauseMountingThread(); this->UnPauseMountingThread();
@@ -291,7 +498,7 @@ void MemoryCardManager::UnmountCard( PlayerNumber pn )
return; return;
/* Leave our own filesystem drivers mounted. */ /* Leave our own filesystem drivers mounted. */
m_pDriver->Unmount( &m_Device[pn] ); g_pWorker->Unmount( &m_Device[pn] );
m_bMounted[pn] = false; m_bMounted[pn] = false;
@@ -313,10 +520,10 @@ void MemoryCardManager::FlushAndReset()
continue; // skip continue; // skip
if( (!d.bNeedsWriteTest && !d.bWriteTestSucceeded) || m_bTooLate[p] ) if( (!d.bNeedsWriteTest && !d.bWriteTestSucceeded) || m_bTooLate[p] )
continue; // skip continue; // skip
m_pDriver->Flush(&m_Device[p]); g_pWorker->Flush( &m_Device[p] );
} }
m_pDriver->ResetUsbStorage(); // forces cards to be re-detected g_pWorker->Reset(); // forces cards to be re-detected
} }
bool MemoryCardManager::PathIsMemCard( CString sDir ) const bool MemoryCardManager::PathIsMemCard( CString sDir ) const
@@ -339,15 +546,22 @@ CString MemoryCardManager::GetName( PlayerNumber pn ) const
void MemoryCardManager::PauseMountingThread() void MemoryCardManager::PauseMountingThread()
{ {
m_pDriver->SetMountThreadState( MemoryCardDriver::paused ); g_pWorker->SetMountThreadState( ThreadedMemoryCardWorker::paused );
/* Start the timeout period. */
g_pWorker->SetTimeout( 10 );
RageFileDriverTimeout::SetTimeout( 10 );
} }
void MemoryCardManager::UnPauseMountingThread() void MemoryCardManager::UnPauseMountingThread()
{ {
m_pDriver->SetMountThreadState( g_pWorker->SetMountThreadState(
m_bCardsLocked ? m_bCardsLocked ?
MemoryCardDriver::detect_and_dont_mount : ThreadedMemoryCardWorker::detect_and_dont_mount : ThreadedMemoryCardWorker::detect_and_mount );
MemoryCardDriver::detect_and_mount );
/* End the timeout period. */
g_pWorker->SetTimeout( -1 );
RageFileDriverTimeout::SetTimeout( -1 );
} }
bool IsAnyPlayerUsingMemoryCard() bool IsAnyPlayerUsingMemoryCard()
@@ -364,7 +578,7 @@ bool IsAnyPlayerUsingMemoryCard()
LuaFunction_NoArgs( IsAnyPlayerUsingMemoryCard, IsAnyPlayerUsingMemoryCard() ) LuaFunction_NoArgs( IsAnyPlayerUsingMemoryCard, IsAnyPlayerUsingMemoryCard() )
/* /*
* (c) 2003-2004 Chris Danford * (c) 2003-2005 Chris Danford, Glenn Maynard
* All rights reserved. * All rights reserved.
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
-3
View File
@@ -32,12 +32,9 @@ public:
CString GetName( PlayerNumber pn ) const; CString GetName( PlayerNumber pn ) const;
protected: protected:
void UpdateUnassignedCards(); // do our best to assign a Device to each player
void PauseMountingThread(); // call this before mouting, reading, or writing to memory card void PauseMountingThread(); // call this before mouting, reading, or writing to memory card
void UnPauseMountingThread(); // call this when done mouting, reading, or writing to memory card void UnPauseMountingThread(); // call this when done mouting, reading, or writing to memory card
MemoryCardDriver *m_pDriver;
vector<UsbStorageDevice> m_vStorageDevices; // all currently connected vector<UsbStorageDevice> m_vStorageDevices; // all currently connected
bool m_bCardsLocked; bool m_bCardsLocked;