Fix race condition in ContentManager (#884)

* Fix race condition in ContentManager

This fix a race condition happening since #791 when trying to load a
game via command line.

* Address gdk's comments

* Ensure to dispose the FileStream and not the IStorage
This commit is contained in:
Thog 2020-01-13 01:17:44 +01:00 committed by Ac_K
parent b8e3909d80
commit f0055482fd

View file

@ -28,6 +28,8 @@ namespace Ryujinx.HLE.FileSystem.Content
private Switch _device; private Switch _device;
private readonly object _lock = new object();
public ContentManager(Switch device) public ContentManager(Switch device)
{ {
_contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>(); _contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>();
@ -57,6 +59,8 @@ namespace Ryujinx.HLE.FileSystem.Content
} }
public void LoadEntries(bool ignoreMissingFonts = false) public void LoadEntries(bool ignoreMissingFonts = false)
{
lock (_lock)
{ {
_contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>(); _contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>();
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>(); _locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
@ -93,7 +97,7 @@ namespace Ryujinx.HLE.FileSystem.Content
{ {
string ncaName = new DirectoryInfo(directoryPath).Name.Replace(".nca", string.Empty); string ncaName = new DirectoryInfo(directoryPath).Name.Replace(".nca", string.Empty);
using (FileStream ncaFile = new FileStream(Directory.GetFiles(directoryPath)[0], FileMode.Open, FileAccess.Read)) using (FileStream ncaFile = File.OpenRead(Directory.GetFiles(directoryPath)[0]))
{ {
Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage()); Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage());
@ -156,13 +160,19 @@ namespace Ryujinx.HLE.FileSystem.Content
_device.System.Font.Initialize(this, ignoreMissingFonts); _device.System.Font.Initialize(this, ignoreMissingFonts);
} }
}
public void ClearEntry(long titleId, NcaContentType contentType, StorageId storageId) public void ClearEntry(long titleId, NcaContentType contentType, StorageId storageId)
{
lock (_lock)
{ {
RemoveLocationEntry(titleId, contentType, storageId); RemoveLocationEntry(titleId, contentType, storageId);
} }
}
public void RefreshEntries(StorageId storageId, int flag) public void RefreshEntries(StorageId storageId, int flag)
{
lock (_lock)
{ {
LinkedList<LocationEntry> locationList = _locationEntries[storageId]; LinkedList<LocationEntry> locationList = _locationEntries[storageId];
LinkedListNode<LocationEntry> locationEntry = locationList.First; LinkedListNode<LocationEntry> locationEntry = locationList.First;
@ -179,8 +189,11 @@ namespace Ryujinx.HLE.FileSystem.Content
locationEntry = nextLocationEntry; locationEntry = nextLocationEntry;
} }
} }
}
public bool HasNca(string ncaId, StorageId storageId) public bool HasNca(string ncaId, StorageId storageId)
{
lock (_lock)
{ {
if (_contentDictionary.ContainsValue(ncaId)) if (_contentDictionary.ContainsValue(ncaId))
{ {
@ -192,29 +205,38 @@ namespace Ryujinx.HLE.FileSystem.Content
return storage == storageId; return storage == storageId;
} }
}
return false; return false;
} }
public UInt128 GetInstalledNcaId(long titleId, NcaContentType contentType) public UInt128 GetInstalledNcaId(long titleId, NcaContentType contentType)
{
lock (_lock)
{ {
if (_contentDictionary.ContainsKey(((ulong)titleId, contentType))) if (_contentDictionary.ContainsKey(((ulong)titleId, contentType)))
{ {
return new UInt128(_contentDictionary[((ulong)titleId, contentType)]); return new UInt128(_contentDictionary[((ulong)titleId, contentType)]);
} }
}
return new UInt128(); return new UInt128();
} }
public StorageId GetInstalledStorage(long titleId, NcaContentType contentType, StorageId storageId) public StorageId GetInstalledStorage(long titleId, NcaContentType contentType, StorageId storageId)
{
lock (_lock)
{ {
LocationEntry locationEntry = GetLocation(titleId, contentType, storageId); LocationEntry locationEntry = GetLocation(titleId, contentType, storageId);
return locationEntry.ContentPath != null ? return locationEntry.ContentPath != null ?
LocationHelper.GetStorageId(locationEntry.ContentPath) : StorageId.None; LocationHelper.GetStorageId(locationEntry.ContentPath) : StorageId.None;
} }
}
public string GetInstalledContentPath(long titleId, StorageId storageId, NcaContentType contentType) public string GetInstalledContentPath(long titleId, StorageId storageId, NcaContentType contentType)
{
lock (_lock)
{ {
LocationEntry locationEntry = GetLocation(titleId, contentType, storageId); LocationEntry locationEntry = GetLocation(titleId, contentType, storageId);
@ -222,11 +244,14 @@ namespace Ryujinx.HLE.FileSystem.Content
{ {
return locationEntry.ContentPath; return locationEntry.ContentPath;
} }
}
return string.Empty; return string.Empty;
} }
public void RedirectLocation(LocationEntry newEntry, StorageId storageId) public void RedirectLocation(LocationEntry newEntry, StorageId storageId)
{
lock (_lock)
{ {
LocationEntry locationEntry = GetLocation(newEntry.TitleId, newEntry.ContentType, storageId); LocationEntry locationEntry = GetLocation(newEntry.TitleId, newEntry.ContentType, storageId);
@ -237,6 +262,7 @@ namespace Ryujinx.HLE.FileSystem.Content
AddLocationEntry(newEntry, storageId); AddLocationEntry(newEntry, storageId);
} }
}
private bool VerifyContentType(LocationEntry locationEntry, NcaContentType contentType) private bool VerifyContentType(LocationEntry locationEntry, NcaContentType contentType)
{ {
@ -827,6 +853,8 @@ namespace Ryujinx.HLE.FileSystem.Content
{ {
LoadEntries(true); LoadEntries(true);
lock (_lock)
{
var locationEnties = _locationEntries[StorageId.NandSystem]; var locationEnties = _locationEntries[StorageId.NandSystem];
foreach (var entry in locationEnties) foreach (var entry in locationEnties)
@ -835,9 +863,9 @@ namespace Ryujinx.HLE.FileSystem.Content
{ {
var path = _device.FileSystem.SwitchPathToSystemPath(entry.ContentPath); var path = _device.FileSystem.SwitchPathToSystemPath(entry.ContentPath);
using (IStorage ncaStorage = File.Open(path, FileMode.Open).AsStorage()) using (FileStream fileStream = File.OpenRead(path))
{ {
Nca nca = new Nca(_device.System.KeySet, ncaStorage); Nca nca = new Nca(_device.System.KeySet, fileStream.AsStorage());
if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data)
{ {
@ -852,6 +880,7 @@ namespace Ryujinx.HLE.FileSystem.Content
} }
} }
} }
}
return null; return null;
} }