fix racing between AutoSave and process exit

This commit is contained in:
cs8425
2026-04-29 19:30:57 +08:00
parent 5f3fc0c746
commit 7e83d2ccd2
2 changed files with 54 additions and 15 deletions

View File

@@ -14,7 +14,10 @@ public class DatabaseHelper
public static readonly ConcurrentDictionary<int, List<BaseDatabaseDataHelper>> UidInstanceMap = []; public static readonly ConcurrentDictionary<int, List<BaseDatabaseDataHelper>> UidInstanceMap = [];
public static readonly List<int> ToSaveUidList = []; public static readonly List<int> ToSaveUidList = [];
public static long LastSaveTick = DateTime.UtcNow.Ticks; public static long LastSaveTick = DateTime.UtcNow.Ticks;
public static Thread? SaveThread;
private static int _saving = 0;
private static Task? _saveTask;
private static CancellationTokenSource? _cts;
public static bool LoadAccount; public static bool LoadAccount;
public static bool LoadAllData; public static bool LoadAllData;
@@ -80,16 +83,8 @@ public class DatabaseHelper
Thread.Sleep(100); Thread.Sleep(100);
} }
LastSaveTick = DateTime.UtcNow.Ticks; _cts = new CancellationTokenSource();
_saveTask = RunAutoSave(_cts.Token);
SaveThread = new Thread(async () =>
{
while (true) {
CalcSaveDatabase();
await Task.Delay(100);
}
});
SaveThread.Start();
LoadAllData = true; LoadAllData = true;
} }
@@ -252,6 +247,35 @@ public class DatabaseHelper
ToSaveUidList.RemoveAll(x => x == key); ToSaveUidList.RemoveAll(x => x == key);
} }
public static void Stop()
{
_cts?.Cancel();
}
public static async Task WaitAsync()
{
if (_saveTask != null)
await _saveTask;
}
private static async Task RunAutoSave(CancellationToken token)
{
LastSaveTick = DateTime.UtcNow.Ticks;
try
{
while (!token.IsCancellationRequested)
{
CalcSaveDatabase();
await Task.Delay(100, token);
}
}
catch (OperationCanceledException)
{
// exit normally
// Console.WriteLine($"RunAutoSave exit! - OperationCanceledException");
}
}
// Auto save per 5 min // Auto save per 5 min
public static void CalcSaveDatabase() public static void CalcSaveDatabase()
{ {
@@ -261,6 +285,10 @@ public class DatabaseHelper
public static void SaveDatabase() public static void SaveDatabase()
{ {
// ensure only one SaveDatabase() runnig
if (Interlocked.Exchange(ref _saving, 1) == 1)
return;
try try
{ {
var prev = DateTime.Now; var prev = DateTime.Now;
@@ -285,11 +313,18 @@ public class DatabaseHelper
Math.Round(t, 2).ToString(CultureInfo.InvariantCulture))); Math.Round(t, 2).ToString(CultureInfo.InvariantCulture)));
ToSaveUidList.Clear(); ToSaveUidList.Clear();
// Thread.Sleep(5000); // for test if saving process taking too long
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error("An error occurred while saving the database", e); logger.Error("An error occurred while saving the database", e);
} }
finally
{
// release lock
Volatile.Write(ref _saving, 0);
}
LastSaveTick = DateTime.UtcNow.Ticks; LastSaveTick = DateTime.UtcNow.Ticks;
} }

View File

@@ -62,7 +62,7 @@ public class MikuSB
await consoleTask; await consoleTask;
ProcessExit(Volatile.Read(ref _exitCode)); await ProcessExit(Volatile.Read(ref _exitCode));
} }
#region Exit #region Exit
@@ -96,11 +96,15 @@ public class MikuSB
_cts.Cancel(); _cts.Cancel();
} }
private static void ProcessExit(int exitCode) private static async Task ProcessExit(int exitCode)
{ {
SocketListener.Connections.Values.ToList().ForEach(x => x.Stop(true)); SocketListener.Connections.Values.ToList().ForEach(x => x.Stop(true));
DatabaseHelper.SaveThread?.Interrupt();
DatabaseHelper.SaveDatabase(); DatabaseHelper.Stop(); // notify stop
await DatabaseHelper.WaitAsync(); // wait AutoSave thread exit
DatabaseHelper.SaveDatabase(); // final flush
Environment.Exit(exitCode); Environment.Exit(exitCode);
} }