Skip to content

Commit 2213474

Browse files
committed
fixed an extremely tricky bug involving a variable assignment that changes BOTH the element in the prevOptions and currentOptions (use a breakpoint and carefully look between FileSwapper.cs line 258 and 263); the solution was to implement proper deep copying
1 parent fd8121a commit 2213474

File tree

3 files changed

+140
-20
lines changed

3 files changed

+140
-20
lines changed

GPUPrefSwitcher/AppEntry.cs

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Text;
45
using System.Windows.Forms;
@@ -8,7 +9,7 @@ namespace GPUPrefSwitcher
89
/// <summary>
910
/// Represents an App Entry in the Registry along with the user's preferences.
1011
/// </summary>
11-
public struct AppEntry
12+
public struct AppEntry : ICloneable
1213
{
1314
public required string AppPath { get; init; }
1415

@@ -44,6 +45,7 @@ readonly get
4445
public required bool SeenInRegistry { get; init; }
4546
public override readonly bool Equals(object obj)
4647
{
48+
4749
return obj is AppEntry entry &&
4850
AppPath == entry.AppPath &&
4951
AppName == entry.AppName &&
@@ -55,35 +57,69 @@ public override readonly bool Equals(object obj)
5557
GPUPrefPluggedIn == entry.GPUPrefPluggedIn &&
5658
RunOnBatteryPath == entry.RunOnBatteryPath &&
5759
RunPluggedInPath == entry.RunPluggedInPath &&
58-
PendingAddToRegistry == entry.PendingAddToRegistry;
60+
PendingAddToRegistry == entry.PendingAddToRegistry &&
61+
SeenInRegistry == entry.SeenInRegistry &&
62+
SwapperStates.SequenceEqual(entry.SwapperStates);
63+
64+
65+
/*
66+
bool yes = obj is AppEntry entry &&
67+
AppPath == entry.AppPath &&
68+
AppName == entry.AppName &&
69+
//appName == entry.appName && //breaks for some reason; null comparison with empty string... let's just exclude this since we're not using it for now
70+
EnableSwitcher == entry.EnableSwitcher &&
71+
EnableFileSwapper == entry.EnableFileSwapper &&
72+
FileSwapperPaths.SequenceEqual(entry.FileSwapperPaths) &&
73+
GPUPrefOnBattery == entry.GPUPrefOnBattery &&
74+
GPUPrefPluggedIn == entry.GPUPrefPluggedIn &&
75+
RunOnBatteryPath == entry.RunOnBatteryPath &&
76+
RunPluggedInPath == entry.RunPluggedInPath &&
77+
PendingAddToRegistry == entry.PendingAddToRegistry &&
78+
SeenInRegistry == entry.SeenInRegistry &&
79+
SwapperStates.SequenceEqual(entry.SwapperStates);
80+
81+
Logger.inst.Log($"Are the same: {yes}: {this} versus {(AppEntry)obj}");
82+
83+
return yes;
84+
*/
5985
}
6086

6187
//Equals() is much faster
6288
public override readonly int GetHashCode()
6389
{
90+
6491
int hashCode = -985154422;
6592
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(AppPath);
66-
//hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(appName); //see above comment for appName
93+
//hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(appName);
6794
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(AppName);
6895
hashCode = hashCode * -1521134295 + EnableSwitcher.GetHashCode();
6996
hashCode = hashCode * -1521134295 + EnableFileSwapper.GetHashCode();
70-
hashCode = hashCode * -1521134295 + GetStringArrHash(FileSwapperPaths);
71-
hashCode = hashCode * -1521134295 + GetStringArrHash(FileSwapperPaths);
97+
hashCode = hashCode * -1521134295 + GetArrHash(FileSwapperPaths);
98+
hashCode = hashCode * -1521134295 + GetArrHash(FileSwapperPaths);
7299
hashCode = hashCode * -1521134295 + GPUPrefOnBattery.GetHashCode();
73100
hashCode = hashCode * -1521134295 + GPUPrefPluggedIn.GetHashCode();
74101
hashCode = hashCode * -1521134295 + RunOnBatteryPath.GetHashCode();
75102
hashCode = hashCode * -1521134295 + RunPluggedInPath.GetHashCode();
76103
hashCode = hashCode * -1521134295 + PendingAddToRegistry.GetHashCode();
104+
hashCode = hashCode * -1521134295 + SeenInRegistry.GetHashCode();
105+
hashCode = hashCode * -1521134295 + GetArrHash(from s in SwapperStates select s.ToString()); //a bit hacky but it should work
106+
77107
//TODO: need for AppName
78108
return hashCode;
79109
}
80110

81-
public static int GetStringArrHash(string[] strings)
111+
public static int GetArrHash(IEnumerable<object> objs)
82112
{
83113
int hash = -335392656;
84-
for (int i = 0; i < strings.Length; i++)
114+
/*
115+
for (int i = 0; i < objs.Length; i++)
116+
{
117+
hash = hash * -130699793 + objs[i].GetHashCode();
118+
}
119+
*/
120+
foreach(object obj in objs)
85121
{
86-
hash = hash * -130699793 + strings[i].GetHashCode();
122+
hash = hash * -130699793 + obj.GetHashCode();
87123
}
88124
return hash;
89125
}
@@ -93,6 +129,7 @@ public override string ToString()
93129
StringBuilder sb = new();
94130
sb.AppendLine($"AppEntry (enabled: {EnableSwitcher}; appname: {AppName}): {AppPath}");
95131
sb.AppendLine($"On Battery: {GPUPrefOnBattery}; Plugged in: On Battery: {GPUPrefPluggedIn}");
132+
sb.AppendLine($"Pending add: {PendingAddToRegistry}");
96133
sb.AppendLine($"File swapper (enabled: {EnableFileSwapper}):");
97134
for (int i = 0; i < FileSwapperPaths.Length; i++)
98135
{
@@ -101,6 +138,25 @@ public override string ToString()
101138
return sb.ToString();
102139
}
103140

141+
public object Clone()
142+
{
143+
return new AppEntry()
144+
{
145+
AppPath = AppPath,
146+
AppName = AppName,
147+
EnableSwitcher = EnableSwitcher,
148+
EnableFileSwapper = EnableFileSwapper,
149+
FileSwapperPaths = (from s in FileSwapperPaths select s).ToArray(),
150+
GPUPrefOnBattery = GPUPrefOnBattery,
151+
GPUPrefPluggedIn = GPUPrefPluggedIn,
152+
RunOnBatteryPath = RunOnBatteryPath,
153+
RunPluggedInPath = RunPluggedInPath,
154+
PendingAddToRegistry = PendingAddToRegistry,
155+
SeenInRegistry = SeenInRegistry,
156+
SwapperStates = (from s in SwapperStates select s).ToArray(),
157+
};
158+
}
159+
104160
public static bool operator ==(AppEntry left, AppEntry right)
105161
{
106162
return left.Equals(right);

GPUPrefSwitcher/AppEntrySaveHandler.cs

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ public class AppEntrySaveHandler
1515
/// !!! This should always contain deep copies !!!
1616
/// </summary>
1717
private List<AppEntry> prevAppEntries;
18+
/*
19+
private List<AppEntry> prevAppEntries
20+
{
21+
get
22+
{
23+
return prevAppEntries_;
24+
}
25+
set
26+
{
27+
Logger.inst.Log("Set PrevAppEntries");
28+
prevAppEntries_ = value;
29+
}
30+
}
31+
private List<AppEntry> prevAppEntries_;
32+
*/
1833

1934
public List<AppEntry> CurrentAppEntries
2035
{
@@ -29,17 +44,33 @@ public void RevertAppEntriesToPrevious()
2944
{
3045
try
3146
{
47+
semaphoreSlim.Wait();
3248
currentAppEntries = DeepCopyAppEntries(prevAppEntries);
3349
} finally
3450
{
35-
semaphoreSlim.Wait();
51+
semaphoreSlim.Release();
3652
}
3753
}
3854
private static List<AppEntry> DeepCopyAppEntries(List<AppEntry> appEntries)
3955
{
40-
AppEntry[] appEntryCopies = appEntries.ToArray();
56+
/*
57+
//AppEntry[] appEntryCopies = appEntries.ToArray();
58+
59+
var appEntryCopies = new AppEntry[appEntries.Count()];
60+
for(int i = 0; i < appEntryCopies.Length; i++)
61+
{
62+
appEntryCopies[i] = appEntries[i]; //struct copy
63+
}
64+
4165
List<AppEntry> newList = new();
4266
newList.AddRange(appEntryCopies);
67+
*/
68+
List<AppEntry> newList = new();
69+
foreach(AppEntry a in appEntries)
70+
{
71+
newList.Add((AppEntry)a.Clone());
72+
}
73+
4374
return newList;
4475
}
4576

@@ -49,10 +80,22 @@ private static List<AppEntry> DeepCopyAppEntries(List<AppEntry> appEntries)
4980

5081
public AppEntrySaveHandler()
5182
{
52-
PreferencesXML = new PreferencesXML();
53-
prevAppEntries = PreferencesXML.GetAppEntries();
54-
currentAppEntries = PreferencesXML.GetAppEntries();
55-
semaphoreSlim = new SemaphoreSlim(1);
83+
try
84+
{
85+
semaphoreSlim = new SemaphoreSlim(1);
86+
87+
semaphoreSlim.Wait();
88+
89+
PreferencesXML = new PreferencesXML();
90+
prevAppEntries = PreferencesXML.GetAppEntries();
91+
//currentAppEntries = PreferencesXML.GetAppEntries();
92+
currentAppEntries = DeepCopyAppEntries(prevAppEntries);
93+
94+
} finally { semaphoreSlim.Release(); }
95+
96+
97+
Logger.inst.Log(currentAppEntries.Single(x => x.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe").ToString());
98+
Logger.inst.Log(prevAppEntries.Single(x => x.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe").ToString());
5699
}
57100

58101
public void ChangeAppEntryByPath(string path, AppEntry updatedAppEntry)
@@ -61,7 +104,10 @@ public void ChangeAppEntryByPath(string path, AppEntry updatedAppEntry)
61104
{
62105
semaphoreSlim.Wait();
63106

107+
Logger.inst.Log($"prev: {CurrentAppEntries[CurrentAppEntries.IndexOf(CurrentAppEntries.Single(x => x.AppPath == path))]}");
108+
64109
int index = CurrentAppEntries.IndexOf(CurrentAppEntries.Single(x => x.AppPath == path));
110+
65111

66112
/* //for-loop alternative, but the above should throw an error with an obvious enough meaning
67113
int index = -1;
@@ -79,8 +125,11 @@ public void ChangeAppEntryByPath(string path, AppEntry updatedAppEntry)
79125
*/
80126

81127
CurrentAppEntries[index] = updatedAppEntry;
128+
129+
Logger.inst.Log($"new: {CurrentAppEntries[CurrentAppEntries.IndexOf(CurrentAppEntries.Single(x => x.AppPath == path))]}");
82130
} finally
83131
{
132+
Logger.inst.Log("ChangeAppEntryByPath released a semaphore.");
84133
semaphoreSlim.Release();
85134
}
86135
}
@@ -104,6 +153,8 @@ public void SaveAppEntryChanges()
104153
{
105154
semaphoreSlim.Wait();
106155

156+
//Logger.inst.Log(currentAppEntries.Single(x => x.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe").ToString());
157+
//Logger.inst.Log(prevAppEntries.Single(x => x.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe").ToString());
107158
List<AppEntry> differences = new();
108159
differences.AddRange(currentAppEntries.Where(entry => NotSameOrInPrevAppEntries(entry)));
109160

@@ -141,10 +192,21 @@ public void SaveAppEntryChanges()
141192

142193
prevAppEntries = DeepCopyAppEntries(currentAppEntries); //update the saved entries
143194

144-
Logger.inst.Log("Concluded saving");
195+
//Logger.inst.Log(currentAppEntries.Single(x => x.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe").ToString());
196+
//Logger.inst.Log(prevAppEntries.Single(x => x.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe").ToString());
197+
198+
Logger.inst.Log($"Concluded saving. Differences: {differences.Count} Added: {needToAdd.Count} Removed: {needToRemoveFromXML.Count}");
145199

146200
bool NotSameOrInPrevAppEntries(AppEntry appEntry)
147201
{
202+
/*
203+
if (appEntry.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe")
204+
{
205+
Logger.inst.Log(prevAppEntries.Single(x => x.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe").ToString());
206+
Logger.inst.Log(CurrentAppEntries.Single(x => x.AppPath == "F:\\SteamLibrary\\steamapps\\common\\Apex Legends\\r5apex.exe").ToString());
207+
Logger.inst.Log($"{appEntry} not in prev app entries: {!prevAppEntries.Contains(appEntry)}");
208+
}
209+
*/
148210
return !prevAppEntries.Contains(appEntry); //Contains uses Equals() which is implemented in AppEntry
149211
}
150212
} finally

GPUPrefSwitcher/Switcher.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,18 @@ public static class Switcher
4343
/// </summary>
4444
internal static void Start()
4545
{
46+
appOptions = new AppOptions();
47+
Logger.inst.EnableRealtimeStandardLogWrites = appOptions.CurrentOptions.EnableRealtimeLogging;
48+
Logger.inst.Log($"Initialized {nameof(AppOptions)}.");
49+
4650
switcherData = SwitcherData.Initialize();
4751
Logger.inst.Log($"Initialized {nameof(SwitcherData)}.");
4852

4953
appEntrySaveHandler = new AppEntrySaveHandler(); //exceptions get thrown from this too if there are problems with the XML (e.g. syntax)
50-
Logger.inst.Log($"Initialized {nameof(PreferencesXML)}.");
54+
Logger.inst.Log($"Initialized {nameof(AppEntrySaveHandler)}.");
5155

5256
//SystemEvents.PowerModeChanged += HandlePowerChangeEvent; **NOT the right event, this is for resume/suspend
53-
appOptions = new AppOptions();
54-
Logger.inst.Log($"Initialized {nameof(AppOptions)}.");
57+
5558

5659
prevPowerLineStatus = switcherData.CurrentSwitcherData.PrevPowerStatus_enum;
5760
spoofPowerStateEnabled = appOptions.CurrentOptions.SpoofPowerStateEnabled;
@@ -64,7 +67,6 @@ internal static void Start()
6467
switcherData.SaveToXML();
6568

6669
updateInterval = appOptions.CurrentOptions.UpdateInterval;
67-
Logger.inst.EnableRealtimeStandardLogWrites = appOptions.CurrentOptions.EnableRealtimeLogging;
6870

6971
File.Delete(CRASHED_FILE_PATH);//"successful" initialization yippee
7072

0 commit comments

Comments
 (0)