From 8d863f1d5f484bf636a0780f5e69d944b1810ae2 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Fri, 4 Mar 2016 21:44:54 +0800 Subject: [PATCH] fix Availability Statistics --- .../Controller/Service/AvailabilityStatistics.cs | 116 ++++++++++++++++----- shadowsocks-csharp/Program.cs | 9 ++ 2 files changed, 98 insertions(+), 27 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 43bc4c49..a43c4780 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -141,15 +141,14 @@ namespace Shadowsocks.Controller private void Run(object _) { UpdateRecords(); - Save(); Reset(); - FilterRawStatistics(); } private void UpdateRecords() { var records = new Dictionary(); - + UpdateRecordsState state = new UpdateRecordsState(); + state.counter = _controller.GetCurrentConfiguration().configs.Count; foreach (var server in _controller.GetCurrentConfiguration().configs) { var id = server.Identifier(); @@ -169,44 +168,76 @@ namespace Shadowsocks.Controller { MyPing ping = new MyPing(server, Repeat); ping.Completed += ping_Completed; - ping.Start(record); + ping.Start(new PingState { state = state, record = record }); + } + else if (!record.IsEmptyData()) + { + AppendRecord(id, record); } } - foreach (var kv in records.Where(kv => !kv.Value.IsEmptyData())) + if (!Config.Ping) { - AppendRecord(kv.Key, kv.Value); + Save(); + FilterRawStatistics(); } } private void ping_Completed(object sender, MyPing.CompletedEventArgs e) { + PingState pingState = (PingState)e.UserState; + UpdateRecordsState state = pingState.state; Server server = e.Server; - StatisticsRecord record = (StatisticsRecord)e.UserState; + StatisticsRecord record = pingState.record; record.SetResponse(e.RoundtripTime); + if (!record.IsEmptyData()) + { + AppendRecord(server.Identifier(), record); + } Logging.Debug($"Ping {server.FriendlyName()} {e.RoundtripTime.Count} times, {(100 - record.PackageLoss * 100)}% packages loss, min {record.MinResponse} ms, max {record.MaxResponse} ms, avg {record.AverageResponse} ms"); + if (Interlocked.Decrement(ref state.counter) == 0) + { + Save(); + FilterRawStatistics(); + } } private void AppendRecord(string serverIdentifier, StatisticsRecord record) { - List records; - if (!RawStatistics.TryGetValue(serverIdentifier, out records)) + try + { + List records; + lock (RawStatistics) + { + if (!RawStatistics.TryGetValue(serverIdentifier, out records)) + { + records = new List(); + RawStatistics[serverIdentifier] = records; + } + } + records.Add(record); + } + catch (Exception e) { - records = new List(); - RawStatistics[serverIdentifier] = records; + Logging.LogUsefulException(e); } - records.Add(record); } private void Save() { + Logging.Debug($"save statistics to {AvailabilityStatisticsFile}"); if (RawStatistics.Count == 0) { return; } try { - var content = JsonConvert.SerializeObject(RawStatistics, Formatting.None); + string content; +#if DEBUG + content = JsonConvert.SerializeObject(RawStatistics, Formatting.Indented); +#else + content = JsonConvert.SerializeObject(RawStatistics, Formatting.None); +#endif File.WriteAllText(AvailabilityStatisticsFile, content); } catch (IOException e) @@ -226,17 +257,25 @@ namespace Shadowsocks.Controller private void FilterRawStatistics() { - if (RawStatistics == null) return; - if (FilteredStatistics == null) + try { - FilteredStatistics = new Statistics(); - } + Logging.Debug("filter raw statistics"); + if (RawStatistics == null) return; + if (FilteredStatistics == null) + { + FilteredStatistics = new Statistics(); + } - foreach (var serverAndRecords in RawStatistics) + foreach (var serverAndRecords in RawStatistics) + { + var server = serverAndRecords.Key; + var filteredRecords = serverAndRecords.Value.FindAll(IsValidRecord); + FilteredStatistics[server] = filteredRecords; + } + } + catch (Exception e) { - var server = serverAndRecords.Key; - var filteredRecords = serverAndRecords.Value.FindAll(IsValidRecord); - FilteredStatistics[server] = filteredRecords; + Logging.LogUsefulException(e); } } @@ -304,6 +343,17 @@ namespace Shadowsocks.Controller }, (k, v) => (v + n)); } + class UpdateRecordsState + { + public int counter; + } + + class PingState + { + public UpdateRecordsState state; + public StatisticsRecord record; + } + class MyPing { //arguments for ICMP tests @@ -329,7 +379,10 @@ namespace Shadowsocks.Controller public void Start(object userstate) { if (server.server == "") + { + FireCompleted(new Exception("Invalid Server"), userstate); return; + } new Task(() => ICMPTest(0, userstate)).Start(); } @@ -355,6 +408,7 @@ namespace Shadowsocks.Controller { Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); Logging.LogUsefulException(e); + FireCompleted(e, userstate); } } @@ -378,6 +432,7 @@ namespace Shadowsocks.Controller { Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); Logging.LogUsefulException(ex); + FireCompleted(ex, e.UserState); } } @@ -391,17 +446,24 @@ namespace Shadowsocks.Controller } else { - Completed?.Invoke(this, new CompletedEventArgs - { - Server = server, - RoundtripTime = RoundtripTime, - UserState = userstate - }); + FireCompleted(null, userstate); } } + private void FireCompleted(Exception error, object userstate) + { + Completed?.Invoke(this, new CompletedEventArgs + { + Error = error, + Server = server, + RoundtripTime = RoundtripTime, + UserState = userstate + }); + } + public class CompletedEventArgs : EventArgs { + public Exception Error; public Server Server; public List RoundtripTime; public object UserState; diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index 977ebffd..2916265c 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -21,6 +21,7 @@ namespace Shadowsocks Utils.ReleaseMemory(true); using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) { + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); @@ -53,5 +54,13 @@ namespace Shadowsocks Application.Run(); } } + + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + Logging.Error(e.ExceptionObject?.ToString()); + MessageBox.Show($"Unexpect error, shadowsocks exited.{Environment.NewLine} {e.ExceptionObject?.ToString()}", + "Shadowsocks", MessageBoxButtons.OK, MessageBoxIcon.Error); + Application.Exit(); + } } }