Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions Seeker/Settings/SettingsActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
// If a parse is in flight, cancel it FIRST so it can't finish and clobber the cleared state by
// committing / persisting its (now-stale) cache. No-op when nothing is parsing.
SharedFileService.CancelOngoingParse();
UploadDirectoryManager.UploadDirectories.Clear();
UploadDirectoryManager.ClearDirectories();
UploadDirectoryManager.SaveToSharedPreferences(SeekerState.SharedPreferences);
SharedFileService.ClearFileCache();
}
Expand Down Expand Up @@ -644,7 +644,7 @@
{
System.Threading.ThreadPool.QueueUserWorkItem((object o) =>
{
UploadDirectoryManager.UploadDirectories.Remove(uploadDirEntry);
UploadDirectoryManager.RemoveDirectory(uploadDirEntry);

// remove purely in memory, no disk walk; on failure fall back to a full rescan like before
bool removed = SharedFileService.TryRemoveSharedFolderInMemory(uploadDirEntry, out var removalErr);
Expand Down Expand Up @@ -1092,7 +1092,6 @@
{
newlyAddedDirectory = new UploadDirectoryEntry(new UploadDirectoryInfo(newlyAddedUriIfApplicable.ToString(), !fromLegacyPicker, UploadDirToReplaceOnReselect.Info.IsLocked, UploadDirToReplaceOnReselect.Info.IsHidden, UploadDirToReplaceOnReselect.Info.DisplayNameOverride));
newlyAddedDirectory.UploadDirectory = fromLegacyPicker ? DocumentFile.FromFile(new Java.IO.File(newlyAddedUriIfApplicable.Path)) : DocumentFile.FromTreeUri(this, newlyAddedUriIfApplicable);
UploadDirectoryManager.UploadDirectories.Remove(UploadDirToReplaceOnReselect);
}
else
{
Expand All @@ -1102,15 +1101,22 @@



if (UploadDirectoryManager.UploadDirectories.Where(up => up.Info.UploadDataDirectoryUri == newlyAddedUriIfApplicable.ToString()).Count() != 0)
if (UploadDirectoryManager.UploadDirectories.Where(up => up.Info.UploadDataDirectoryUri == newlyAddedUriIfApplicable.ToString() && (!reselectCase || up != UploadDirToReplaceOnReselect)).Count() != 0)

Check warning on line 1104 in Seeker/Settings/SettingsActivity.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '.Any()' to test whether this 'IEnumerable<Seeker.UploadDirectoryEntry>' is empty or not.

See more on https://sonarcloud.io/project/issues?id=jackBonadies_SeekerAndroid&issues=AZ7sBSjQtm-bIu2D38QK&open=AZ7sBSjQtm-bIu2D38QK&pullRequest=99

Check warning on line 1104 in Seeker/Settings/SettingsActivity.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Drop 'Where' and move the condition into the 'Count'.

See more on https://sonarcloud.io/project/issues?id=jackBonadies_SeekerAndroid&issues=AZ7sBSjQtm-bIu2D38QL&open=AZ7sBSjQtm-bIu2D38QL&pullRequest=99
{
//error!!
SeekerApplication.Toaster.ShowToast(SeekerApplication.GetString(Resource.String.ErrorAlreadyAdded), ToastLength.Long);
return;
//throw new Exception("Directory is already added!");
}

UploadDirectoryManager.UploadDirectories.Add(newlyAddedDirectory);
if (reselectCase)
{
UploadDirectoryManager.ReplaceDirectory(UploadDirToReplaceOnReselect, newlyAddedDirectory);
}
else
{
UploadDirectoryManager.AddDirectory(newlyAddedDirectory);
}
}

UploadDirectoryManager.RecomputeDirectoryState();
Expand Down
103 changes: 87 additions & 16 deletions Seeker/Transfers/UploadDirectoryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@
if (!string.IsNullOrEmpty(legacyUploadDataDirectory))
{
var uploadDir = new UploadDirectoryEntry(new UploadDirectoryInfo(legacyUploadDataDirectory, fromTree, false, false, null));
UploadDirectories = new List<UploadDirectoryEntry>();
UploadDirectories.Add(uploadDir);
SetDirectories(new List<UploadDirectoryEntry> { uploadDir });

SaveToSharedPreferences(sharedPreferences);
var editor = sharedPreferences.Edit();
Expand All @@ -88,13 +87,13 @@
}
else
{
UploadDirectories = new List<UploadDirectoryEntry>();
SetDirectories(null);
}
}
else
{
var infos = SerializationHelper.DeserializeFromString<List<UploadDirectoryInfo>>(sharedDirInfo);
UploadDirectories = infos.Select(info => new UploadDirectoryEntry(info)).ToList();
SetDirectories(infos.Select(info => new UploadDirectoryEntry(info)));
}
}

Expand All @@ -112,7 +111,73 @@
}
}

public static List<UploadDirectoryEntry> UploadDirectories { get; private set; }
// Copy-on-write: UploadDirectories is shared across the UI thread and multiple ThreadPool
// background threads (folder add/remove, parse/rescan).
private static volatile List<UploadDirectoryEntry> _uploadDirectories = new List<UploadDirectoryEntry>();
private static readonly object _uploadDirectoriesWriteLock = new object();

public static List<UploadDirectoryEntry> UploadDirectories => _uploadDirectories;

/// <summary>
/// Replace the whole directory list (used on restore). Snapshots the source so the caller
/// can't mutate it out from under readers afterwards.
/// </summary>
public static void SetDirectories(IEnumerable<UploadDirectoryEntry> entries)
{
lock (_uploadDirectoriesWriteLock)
{
_uploadDirectories = entries == null
? new List<UploadDirectoryEntry>()
: new List<UploadDirectoryEntry>(entries);
}
}

public static void AddDirectory(UploadDirectoryEntry entry)
{
lock (_uploadDirectoriesWriteLock)
{
var copy = new List<UploadDirectoryEntry>(_uploadDirectories);
copy.Add(entry);
_uploadDirectories = copy;
}
}

public static bool RemoveDirectory(UploadDirectoryEntry entry)
{
lock (_uploadDirectoriesWriteLock)
{
var copy = new List<UploadDirectoryEntry>(_uploadDirectories);
bool removed = copy.Remove(entry);
if (removed)
{
_uploadDirectories = copy;
}
return removed;
}
}

public static void ClearDirectories()
{
lock (_uploadDirectoriesWriteLock)
{
_uploadDirectories = new List<UploadDirectoryEntry>();
}
}

/// <summary>
/// Atomically remove <paramref name="oldEntry"/> and add <paramref name="newEntry"/> in a
/// single swap (the reselect case), so readers never observe an intermediate state.
/// </summary>
public static void ReplaceDirectory(UploadDirectoryEntry oldEntry, UploadDirectoryEntry newEntry)
{
lock (_uploadDirectoriesWriteLock)
{
var copy = new List<UploadDirectoryEntry>(_uploadDirectories);
copy.Remove(oldEntry);
copy.Add(newEntry);
_uploadDirectories = copy;
}
}

public static bool IsFromTree(string presentablePath)
{
Expand Down Expand Up @@ -247,9 +312,11 @@

private static void ResolveDocumentFilesAndErrorStates()
{
for (int i = 0; i < UploadDirectories.Count; i++)
// Snapshot once so a concurrent copy-on-write swap can't tear Count vs. [i].
var dirs = _uploadDirectories;
for (int i = 0; i < dirs.Count; i++)
{
UploadDirectoryEntry entry = UploadDirectories[i];
UploadDirectoryEntry entry = dirs[i];

Android.Net.Uri uploadDirUri = Android.Net.Uri.Parse(entry.Info.UploadDataDirectoryUri);
try
Expand Down Expand Up @@ -301,17 +368,19 @@

private static void RecomputeSubdirFlags()
{
for (int i = 0; i < UploadDirectories.Count; i++)
// Snapshot once so a concurrent copy-on-write swap can't tear Count vs. [i].
var dirs = _uploadDirectories;
for (int i = 0; i < dirs.Count; i++)
{
UploadDirectoryEntry entry = UploadDirectories[i];
UploadDirectoryEntry entry = dirs[i];
var ourUri = Android.Net.Uri.Parse(entry.Info.UploadDataDirectoryUri);

entry.IsSubdir = false;
for (int j = 0; j < UploadDirectories.Count; j++)
for (int j = 0; j < dirs.Count; j++)
{
if (i != j)
{
if (IsNestedUnder(ourUri, Android.Net.Uri.Parse(UploadDirectories[j].Info.UploadDataDirectoryUri)))
if (IsNestedUnder(ourUri, Android.Net.Uri.Parse(dirs[j].Info.UploadDataDirectoryUri)))

Check warning on line 383 in Seeker/Transfers/UploadDirectoryManager.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Merge this if statement with the enclosing one.

See more on https://sonarcloud.io/project/issues?id=jackBonadies_SeekerAndroid&issues=AZ7sBSl-tm-bIu2D38QM&open=AZ7sBSl-tm-bIu2D38QM&pullRequest=99
{
entry.IsSubdir = true;
}
Expand All @@ -324,9 +393,11 @@
{
PresentableNameLockedDirectories.Clear();
PresentableNameHiddenDirectories.Clear();
for (int i = 0; i < UploadDirectories.Count; i++)
// Snapshot once so a concurrent copy-on-write swap can't tear Count vs. [i].
var dirs = _uploadDirectories;
for (int i = 0; i < dirs.Count; i++)
{
UploadDirectoryEntry entry = UploadDirectories[i];
UploadDirectoryEntry entry = dirs[i];
if (!entry.Info.IsLocked && !entry.Info.IsHidden)
{
continue;
Expand All @@ -350,13 +421,13 @@

UploadDirectoryEntry ourTopLevelParent = null;

for (int j = 0; j < UploadDirectories.Count; j++)
for (int j = 0; j < dirs.Count; j++)
{
if (i != j)
{
if (!UploadDirectories[j].IsSubdir && IsNestedUnder(ourUri, Android.Net.Uri.Parse(UploadDirectories[j].Info.UploadDataDirectoryUri)))
if (!dirs[j].IsSubdir && IsNestedUnder(ourUri, Android.Net.Uri.Parse(dirs[j].Info.UploadDataDirectoryUri)))

Check warning on line 428 in Seeker/Transfers/UploadDirectoryManager.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Merge this if statement with the enclosing one.

See more on https://sonarcloud.io/project/issues?id=jackBonadies_SeekerAndroid&issues=AZ7sBSl-tm-bIu2D38QN&open=AZ7sBSl-tm-bIu2D38QN&pullRequest=99
{
ourTopLevelParent = UploadDirectories[j];
ourTopLevelParent = dirs[j];
break;
}
}
Expand Down
Loading