diff --git a/Common/Database/DatabaseHelper.cs b/Common/Database/DatabaseHelper.cs index 235789e..cbaf4b0 100644 --- a/Common/Database/DatabaseHelper.cs +++ b/Common/Database/DatabaseHelper.cs @@ -14,7 +14,10 @@ public class DatabaseHelper public static readonly ConcurrentDictionary> UidInstanceMap = []; public static readonly List ToSaveUidList = []; 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 LoadAllData; @@ -80,16 +83,8 @@ public class DatabaseHelper Thread.Sleep(100); } - LastSaveTick = DateTime.UtcNow.Ticks; - - SaveThread = new Thread(async () => - { - while (true) { - CalcSaveDatabase(); - await Task.Delay(100); - } - }); - SaveThread.Start(); + _cts = new CancellationTokenSource(); + _saveTask = RunAutoSave(_cts.Token); LoadAllData = true; } @@ -252,6 +247,35 @@ public class DatabaseHelper 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 public static void CalcSaveDatabase() { @@ -261,6 +285,10 @@ public class DatabaseHelper public static void SaveDatabase() { + // ensure only one SaveDatabase() runnig + if (Interlocked.Exchange(ref _saving, 1) == 1) + return; + try { var prev = DateTime.Now; @@ -285,11 +313,18 @@ public class DatabaseHelper Math.Round(t, 2).ToString(CultureInfo.InvariantCulture))); ToSaveUidList.Clear(); + + // Thread.Sleep(5000); // for test if saving process taking too long } catch (Exception e) { logger.Error("An error occurred while saving the database", e); } + finally + { + // release lock + Volatile.Write(ref _saving, 0); + } LastSaveTick = DateTime.UtcNow.Ticks; } diff --git a/MikuSB/Program/MikuSB.cs b/MikuSB/Program/MikuSB.cs index edd0778..ff779d5 100644 --- a/MikuSB/Program/MikuSB.cs +++ b/MikuSB/Program/MikuSB.cs @@ -62,7 +62,7 @@ public class MikuSB await consoleTask; - ProcessExit(Volatile.Read(ref _exitCode)); + await ProcessExit(Volatile.Read(ref _exitCode)); } #region Exit @@ -96,11 +96,15 @@ public class MikuSB _cts.Cancel(); } - private static void ProcessExit(int exitCode) + private static async Task ProcessExit(int exitCode) { 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); }