Skip to content

Commit fd8121a

Browse files
committed
thread safety and error handling of tasks
1 parent 3d0e792 commit fd8121a

File tree

5 files changed

+162
-113
lines changed

5 files changed

+162
-113
lines changed

GPUPrefSwitcher/AppEntrySaveHandler.cs

Lines changed: 124 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.Linq;
5+
using System.Threading;
56

67
namespace GPUPrefSwitcher
78
{
@@ -26,7 +27,13 @@ public List<AppEntry> CurrentAppEntries
2627

2728
public void RevertAppEntriesToPrevious()
2829
{
29-
currentAppEntries = DeepCopyAppEntries(prevAppEntries);
30+
try
31+
{
32+
currentAppEntries = DeepCopyAppEntries(prevAppEntries);
33+
} finally
34+
{
35+
semaphoreSlim.Wait();
36+
}
3037
}
3138
private static List<AppEntry> DeepCopyAppEntries(List<AppEntry> appEntries)
3239
{
@@ -36,6 +43,7 @@ private static List<AppEntry> DeepCopyAppEntries(List<AppEntry> appEntries)
3643
return newList;
3744
}
3845

46+
private SemaphoreSlim semaphoreSlim;
3947

4048
internal readonly PreferencesXML PreferencesXML;
4149

@@ -44,28 +52,37 @@ public AppEntrySaveHandler()
4452
PreferencesXML = new PreferencesXML();
4553
prevAppEntries = PreferencesXML.GetAppEntries();
4654
currentAppEntries = PreferencesXML.GetAppEntries();
55+
semaphoreSlim = new SemaphoreSlim(1);
4756
}
4857

4958
public void ChangeAppEntryByPath(string path, AppEntry updatedAppEntry)
5059
{
51-
int index = CurrentAppEntries.IndexOf(CurrentAppEntries.Single(x => x.AppPath == path));
52-
53-
/* //for-loop alternative, but the above should throw an error with an obvious enough meaning
54-
int index = -1;
55-
for (int i = 0; i < CurrentAppEntries.Count; i++)
60+
try
5661
{
57-
AppEntry appEntry = CurrentAppEntries[i];
58-
if (appEntry.AppPath == path)
62+
semaphoreSlim.Wait();
63+
64+
int index = CurrentAppEntries.IndexOf(CurrentAppEntries.Single(x => x.AppPath == path));
65+
66+
/* //for-loop alternative, but the above should throw an error with an obvious enough meaning
67+
int index = -1;
68+
for (int i = 0; i < CurrentAppEntries.Count; i++)
5969
{
60-
index = i; break;
70+
AppEntry appEntry = CurrentAppEntries[i];
71+
if (appEntry.AppPath == path)
72+
{
73+
index = i; break;
74+
}
6175
}
62-
}
6376
64-
if(index<0)
65-
throw new AppEntrySaverException($"UpdateAppEntry: No AppEntry with path {path} was found");
66-
*/
77+
if(index<0)
78+
throw new AppEntrySaverException($"UpdateAppEntry: No AppEntry with path {path} was found");
79+
*/
6780

68-
CurrentAppEntries[index] = updatedAppEntry;
81+
CurrentAppEntries[index] = updatedAppEntry;
82+
} finally
83+
{
84+
semaphoreSlim.Release();
85+
}
6986
}
7087

7188
/*
@@ -83,103 +100,118 @@ public void ChangeAppEntryByPath(string path, AppEntry updatedAppEntry)
83100
*/
84101
public void SaveAppEntryChanges()
85102
{
103+
try
104+
{
105+
semaphoreSlim.Wait();
86106

87-
List<AppEntry> differences = new();
88-
differences.AddRange(currentAppEntries.Where( entry => NotSameOrInPrevAppEntries(entry) ));
107+
List<AppEntry> differences = new();
108+
differences.AddRange(currentAppEntries.Where(entry => NotSameOrInPrevAppEntries(entry)));
89109

90-
List<string> existingAppPaths = new List<string>(from appEntry in PreferencesXML.GetAppEntries() select appEntry.AppPath);
91-
List<AppEntry> needToAdd = new();
92-
needToAdd.AddRange(currentAppEntries.Where( entry => !existingAppPaths.Contains(entry.AppPath) ));
110+
List<string> existingAppPaths = new List<string>(from appEntry in PreferencesXML.GetAppEntries() select appEntry.AppPath);
111+
List<AppEntry> needToAdd = new();
112+
needToAdd.AddRange(currentAppEntries.Where(entry => !existingAppPaths.Contains(entry.AppPath)));
93113

94-
//remove appentries whose *paths* exist in the prev but not the current
95-
List<AppEntry> needToRemoveFromXML = new();
96-
foreach( AppEntry entry in prevAppEntries )
97-
{
98-
if(!currentAppEntries.Exists(a => a.AppPath == entry.AppPath))
114+
//remove appentries whose *paths* exist in the prev but not the current
115+
List<AppEntry> needToRemoveFromXML = new();
116+
foreach (AppEntry entry in prevAppEntries)
99117
{
100-
needToRemoveFromXML.Add(entry);
118+
if (!currentAppEntries.Exists(a => a.AppPath == entry.AppPath))
119+
{
120+
needToRemoveFromXML.Add(entry);
121+
}
101122
}
102-
}
103123

104-
//new AppEntries should be added first so that ModifyAppEntry can actually find the AppEntry with the path
105-
foreach (AppEntry appEntry in needToAdd)
106-
{
107-
PreferencesXML.AddAppEntryAndSave(appEntry);
108-
}
124+
//new AppEntries should be added first so that ModifyAppEntry can actually find the AppEntry with the path
125+
foreach (AppEntry appEntry in needToAdd)
126+
{
127+
PreferencesXML.AddAppEntryAndSave(appEntry);
128+
}
109129

110-
foreach(AppEntry appEntry in differences)
111-
{
112-
PreferencesXML.ModifyAppEntryAndSave(appEntry.AppPath, appEntry);
113-
}
130+
foreach (AppEntry appEntry in differences)
131+
{
132+
PreferencesXML.ModifyAppEntryAndSave(appEntry.AppPath, appEntry);
133+
}
114134

115-
foreach(AppEntry appEntry in needToRemoveFromXML)
116-
{
117-
bool success = PreferencesXML.TryDeleteAppEntryAndSave(appEntry.AppPath);
118-
if(!success) //this would probably only ever fail if AppEntries were deleted externally whilst the GUI or Service was still running
119-
throw new XMLHelperException($"DeleteAppEntry: AppEntry with the specified path not found in data store: {appEntry.AppPath}");
120-
}
135+
foreach (AppEntry appEntry in needToRemoveFromXML)
136+
{
137+
bool success = PreferencesXML.TryDeleteAppEntryAndSave(appEntry.AppPath);
138+
if (!success) //this would probably only ever fail if AppEntries were deleted externally whilst the GUI or Service was still running
139+
throw new XMLHelperException($"DeleteAppEntry: AppEntry with the specified path not found in data store: {appEntry.AppPath}");
140+
}
121141

122-
prevAppEntries = DeepCopyAppEntries(currentAppEntries); //update the saved entries
142+
prevAppEntries = DeepCopyAppEntries(currentAppEntries); //update the saved entries
123143

124-
Logger.inst.Log("Concluded saving");
144+
Logger.inst.Log("Concluded saving");
125145

126-
bool NotSameOrInPrevAppEntries (AppEntry appEntry)
146+
bool NotSameOrInPrevAppEntries(AppEntry appEntry)
147+
{
148+
return !prevAppEntries.Contains(appEntry); //Contains uses Equals() which is implemented in AppEntry
149+
}
150+
} finally
127151
{
128-
return !prevAppEntries.Contains(appEntry); //Contains uses Equals() which is implemented in AppEntry
152+
semaphoreSlim.Release();
129153
}
130154
}
131155

132156
public bool AppEntriesHaveChangedFromLastSave()
133157
{
134-
if (prevAppEntries.SequenceEqual(CurrentAppEntries))
135-
{
136-
return false;
137-
}
138-
else
139-
{
140-
141-
var diffs = currentAppEntries.Except(prevAppEntries);
142-
return true;//move to end for debug info
143-
foreach (AppEntry e in diffs)
158+
try {
159+
160+
semaphoreSlim.Wait();
161+
162+
if (prevAppEntries.SequenceEqual(CurrentAppEntries))
144163
{
145-
146-
Debug.WriteLine("CURRENT:");
147-
Debug.WriteLine(e.ToString());
148-
149-
//WARNING: causes crash if Preferences.xml AppsList is empty
150-
var prev = prevAppEntries.Single(x => x.AppPath == e.AppPath);
151-
Debug.WriteLine("PREVIOUS:");
152-
Debug.WriteLine(prev.ToString());
153-
154-
Debug.WriteLine(e.GetHashCode());
155-
Debug.WriteLine(prev.GetHashCode());
156-
157-
Debug.WriteLine($"DIFFERENT?: {(e.Equals(prev)? "no" : "yes")}");
158-
159-
/*
160-
Debug.WriteLine(e.EnableFileSwapper.GetHashCode());
161-
Debug.WriteLine(prev.EnableFileSwapper.GetHashCode());
162-
*/
163-
164-
/*
165-
Debug.WriteLine(e.AppPath.GetHashCode());
166-
Debug.WriteLine(prev.AppPath.GetHashCode());
167-
*/
168-
169-
/*
170-
Debug.WriteLine(e.FileSwapperPaths.GetHashCode());
171-
Debug.WriteLine(prev.FileSwapperPaths.GetHashCode());
172-
Debug.WriteLine(e.SwapperStates.GetHashCode());
173-
Debug.WriteLine(prev.SwapperStates.GetHashCode());
174-
*/
175-
176-
/*
177-
Debug.WriteLine(e.getStringArrHash(e.FileSwapperPaths));
178-
Debug.WriteLine(prev.getStringArrHash(prev.FileSwapperPaths));
179-
Debug.WriteLine(e.getStringArrHash(e.SwapperStates));
180-
Debug.WriteLine(prev.getStringArrHash(prev.SwapperStates));
181-
*/
164+
return false;
182165
}
166+
else
167+
{
168+
169+
var diffs = currentAppEntries.Except(prevAppEntries);
170+
return true;//move to end for debug info
171+
foreach (AppEntry e in diffs)
172+
{
173+
174+
Debug.WriteLine("CURRENT:");
175+
Debug.WriteLine(e.ToString());
176+
177+
//WARNING: causes crash if Preferences.xml AppsList is empty
178+
var prev = prevAppEntries.Single(x => x.AppPath == e.AppPath);
179+
Debug.WriteLine("PREVIOUS:");
180+
Debug.WriteLine(prev.ToString());
181+
182+
Debug.WriteLine(e.GetHashCode());
183+
Debug.WriteLine(prev.GetHashCode());
184+
185+
Debug.WriteLine($"DIFFERENT?: {(e.Equals(prev) ? "no" : "yes")}");
186+
187+
/*
188+
Debug.WriteLine(e.EnableFileSwapper.GetHashCode());
189+
Debug.WriteLine(prev.EnableFileSwapper.GetHashCode());
190+
*/
191+
192+
/*
193+
Debug.WriteLine(e.AppPath.GetHashCode());
194+
Debug.WriteLine(prev.AppPath.GetHashCode());
195+
*/
196+
197+
/*
198+
Debug.WriteLine(e.FileSwapperPaths.GetHashCode());
199+
Debug.WriteLine(prev.FileSwapperPaths.GetHashCode());
200+
Debug.WriteLine(e.SwapperStates.GetHashCode());
201+
Debug.WriteLine(prev.SwapperStates.GetHashCode());
202+
*/
203+
204+
/*
205+
Debug.WriteLine(e.getStringArrHash(e.FileSwapperPaths));
206+
Debug.WriteLine(prev.getStringArrHash(prev.FileSwapperPaths));
207+
Debug.WriteLine(e.getStringArrHash(e.SwapperStates));
208+
Debug.WriteLine(prev.getStringArrHash(prev.SwapperStates));
209+
*/
210+
}
211+
}
212+
} finally
213+
{
214+
semaphoreSlim.Release();
183215
}
184216
}
185217

GPUPrefSwitcher/AppLogger.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public static AppLogger Initialize (string logFolderPath_Error, string logFileNa
9595
logger.outputFile_Error.WriteLine(); //prepend newlines, looks better
9696
logger.outputFile_Standard.WriteLine();
9797

98-
logger.ErrorLog("---<<<<< BEGIN ERROR LOG SESSION >>>>>--- \n(Note that newer logs go at the end of the file, you can press CTRL+END to skip there in most editors)\n").Wait();
98+
logger.ErrorLog("---<<<<< BEGIN ERROR LOG SESSION >>>>>--- \n(Note that newer logs go at the end of the file, you can press CTRL+END to skip there in most editors)\n");
9999
logger.ForceStandardLog("---<<<<< BEGIN STANDARD LOG SESSION >>>>>--- \n(Note that newer logs go at the end of the file, you can press CTRL+END to skip there in most editors)\n");
100100

101101
//AppDomain.CurrentDomain.FirstChanceException += (sender, e) => //FirstChanceException triggers upon ALL exceptions, even those caught; this is excessive, and they can still be observed from Event Viewer
@@ -105,8 +105,8 @@ public static AppLogger Initialize (string logFolderPath_Error, string logFileNa
105105
{
106106
logger.DumpStandardLogBufferToStandardLog().Wait();
107107
logger.DumpStandardLogBufferToErrorLog().Wait(); //do this before the app terminates
108-
logger.ErrorLog("<<<AN UNHANDLED ERROR HAS OCCURRED>>>").Wait();
109-
logger.ErrorLog(e.ExceptionObject.ToString()).Wait();
108+
logger.ErrorLog("<<<AN UNHANDLED ERROR HAS OCCURRED>>>");
109+
logger.ErrorLog(e.ExceptionObject.ToString());
110110
};
111111

112112
//useful for capturing fire-and-forget tasks that error out
@@ -115,8 +115,8 @@ public static AppLogger Initialize (string logFolderPath_Error, string logFileNa
115115
{
116116
logger.DumpStandardLogBufferToStandardLog().Wait();
117117
logger.DumpStandardLogBufferToErrorLog().Wait(); //do this before the app terminates
118-
logger.ErrorLog("<<<AN UNOBSERVED TASK EXCEPTION HAS OCCURRED>>>").Wait();
119-
logger.ErrorLog(e.Exception.ToString()).Wait();
118+
logger.ErrorLog("<<<AN UNOBSERVED TASK EXCEPTION HAS OCCURRED>>>");
119+
logger.ErrorLog(e.Exception.ToString());
120120

121121
};
122122

@@ -173,16 +173,15 @@ internal Task DumpStandardLogBufferToErrorLog()
173173
/// </summary>
174174
/// <param name="str"></param>
175175
/// <returns></returns>
176-
public Task ErrorLog(string str)
176+
public void ErrorLog(string str)
177177
{
178178

179179
errorLogCount += 1;
180180

181181
string toPrint = $"[(T:{TotalLogCount}, {errorLogCount}) {DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss")}]: {str}";
182182

183183
semaphoreSlim_Error.AvailableWaitHandle.WaitOne();//block if unavailable
184-
185-
return WriteAsyncInternal_Error(toPrint);
184+
_ = WriteAsyncInternal_Error(toPrint);
186185
}
187186

188187
public int GlobalLogLevel

GPUPrefSwitcher/FileSwapper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public string SettingsBankFolderPath
6464
public FileSwapper(AppEntry appEntry, AppEntrySaveHandler appEntrySaveHandler)
6565
{
6666
AppEntry = appEntry;
67-
appEntrySaveHandler = AppEntrySaveHandler;
67+
AppEntrySaveHandler = appEntrySaveHandler;
6868
}
6969

7070

@@ -77,7 +77,7 @@ public async Task InitiateFileSwaps(PowerLineStatus forPowerLineStatus, AppEntry
7777
if (!appEntryFolderExists)
7878
{
7979
Directory.CreateDirectory(SettingsBankFolderPath);
80-
File.Create(Path.Combine(SettingsBankFolderPath, $"{AppEntry.AppName} settings are in {SettingsBankFolderPath}"));
80+
File.Create(Path.Combine(SettingsBankFolderPath, $"{AppEntry.AppName} settings are in {SettingsBankFolderName}"));
8181
}
8282

8383
List<Task> fileSwapTasks = new();

GPUPrefSwitcher/PreferencesXML.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ public XmlNodeList GetElementsByTagName(string tagName)
6161

6262
public static readonly string XML_PREFERENCES_PATH = Program.SavedDataPath + "Preferences.xml";
6363
private XmlDocument xmlDocument = new();
64-
//private SemaphoreSlim semaphoreSlim = new(1);
6564

6665
internal PreferencesXML()
6766
{

0 commit comments

Comments
 (0)