OSDN Git Service

TimerCallback内で発生した例外を必ずハンドルするように修正
authorKimura Youichi <kim.upsilon@bucyou.net>
Sat, 5 Oct 2019 10:02:34 +0000 (19:02 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Sat, 5 Oct 2019 10:03:17 +0000 (19:03 +0900)
タイマーでハンドルされない例外が生じると時々プロセスごと異常終了することがあるため

OpenTween/ApplicationEvents.cs
OpenTween/AsyncTimer.cs [new file with mode: 0644]
OpenTween/OpenTween.csproj
OpenTween/Resources/ChangeLog.txt
OpenTween/ThrottlingTimer.cs
OpenTween/Thumbnail/Services/ImgAzyobuziNet.cs
OpenTween/TimelineScheduler.cs

index 9266ecf..3f2f680 100644 (file)
@@ -102,6 +102,7 @@ namespace OpenTween
                 };
                 Application.ThreadException += (s, e) => OnUnhandledException(e.Exception);
                 AppDomain.CurrentDomain.UnhandledException += (s, e) => OnUnhandledException((Exception)e.ExceptionObject);
+                AsyncTimer.UnhandledException += (s, e) => OnUnhandledException(e.Exception);
 
                 Application.EnableVisualStyles();
                 Application.SetCompatibleTextRenderingDefault(false);
diff --git a/OpenTween/AsyncTimer.cs b/OpenTween/AsyncTimer.cs
new file mode 100644 (file)
index 0000000..ed2b7a5
--- /dev/null
@@ -0,0 +1,61 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2019 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+#nullable enable
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace OpenTween
+{
+    public sealed class AsyncTimer : IDisposable
+    {
+        private readonly Func<Task> callback;
+        private readonly Timer timer;
+
+        public static event EventHandler<ThreadExceptionEventArgs>? UnhandledException;
+
+        public AsyncTimer(Func<Task> callback)
+        {
+            this.callback = callback;
+            this.timer = new Timer(this.TimerCallback);
+        }
+
+        private async void TimerCallback(object _)
+        {
+            try
+            {
+                await this.callback().ConfigureAwait(false);
+            }
+            catch (Exception ex)
+            {
+                UnhandledException?.Invoke(this, new ThreadExceptionEventArgs(ex));
+            }
+        }
+
+        public void Change(TimeSpan dueTime, TimeSpan period)
+            => this.timer.Change(dueTime, period);
+
+        public void Dispose()
+            => this.timer.Dispose();
+    }
+}
index 961c361..e56f0f2 100644 (file)
@@ -98,6 +98,7 @@
       <SubType>Code</SubType>
     </Compile>
     <Compile Include="ApplicationSettings.cs" />
+    <Compile Include="AsyncTimer.cs" />
     <Compile Include="AtIdSupplement.cs">
       <SubType>Form</SubType>
     </Compile>
index 266586a..1324f54 100644 (file)
@@ -1,6 +1,7 @@
 更新履歴
 
 ==== Ver 2.4.2-dev(2019/xx/xx)
+ * FIX: タブ更新時にエラーが発生するとプロセスが異常終了する場合がある不具合を修正
 
 ==== Ver 2.4.1(2019/09/25)
  * FIX: 「タブを一覧の下に表示する」を無効にすると起動時にエラーが発生する不具合を修正 (thx @mulsys!)
index 3553c80..1582228 100644 (file)
@@ -35,7 +35,7 @@ namespace OpenTween
         private const int TIMER_DISABLED = 0;
         private const int TIMER_ENABLED = 1;
 
-        private readonly Timer throttlingTimer;
+        private readonly AsyncTimer throttlingTimer;
         private readonly Func<Task> timerCallback;
 
         private long lastCalledTick;
@@ -68,7 +68,7 @@ namespace OpenTween
             this.LastInvoked = DateTimeUtc.MinValue;
             this.InvokeLeading = leading;
             this.InvokeTrailing = trailing;
-            this.throttlingTimer = new Timer(this.Execute);
+            this.throttlingTimer = new AsyncTimer(this.Execute);
         }
 
         public void Call()
@@ -89,7 +89,7 @@ namespace OpenTween
             }
         }
 
-        private async void Execute(object _)
+        private async Task Execute()
         {
             var lastCalled = this.LastCalled;
             var lastInvoked = this.LastInvoked;
index 670bf21..4804950 100644 (file)
@@ -53,7 +53,7 @@ namespace OpenTween.Thumbnail.Services
 
         protected string? ApiBase;
         protected IEnumerable<Regex>? UrlRegex = null;
-        protected Timer UpdateTimer;
+        protected AsyncTimer UpdateTimer;
 
         protected HttpClient http
             => this.localHttpClient ?? Networking.Http;
@@ -74,7 +74,7 @@ namespace OpenTween.Thumbnail.Services
 
         public ImgAzyobuziNet(HttpClient? http, bool autoupdate)
         {
-            this.UpdateTimer = new Timer(async _ => await this.LoadRegexAsync());
+            this.UpdateTimer = new AsyncTimer(this.LoadRegexAsync);
             this.AutoUpdate = autoupdate;
 
             this.Enabled = true;
@@ -109,10 +109,10 @@ namespace OpenTween.Thumbnail.Services
         public bool DisabledInDM { get; set; }
 
         protected void StartAutoUpdate()
-            => this.UpdateTimer.Change(0, 30 * 60 * 1000); // 30分おきに更新
+            => this.UpdateTimer.Change(TimeSpan.Zero, TimeSpan.FromMinutes(30)); // 30分おきに更新
 
         protected void StopAutoUpdate()
-            => this.UpdateTimer.Change(Timeout.Infinite, Timeout.Infinite);
+            => this.UpdateTimer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
 
         public async Task LoadRegexAsync()
         {
index 8dcc166..2006800 100644 (file)
@@ -30,7 +30,7 @@ namespace OpenTween
 {
     public class TimelineScheduler
     {
-        private readonly Timer timer;
+        private readonly AsyncTimer timer;
 
         private bool enabled = false;
         private bool systemResumeMode = false;
@@ -125,7 +125,7 @@ namespace OpenTween
         }
 
         public TimelineScheduler()
-            => this.timer = new Timer(_ => this.TimerCallback());
+            => this.timer = new AsyncTimer(this.TimerCallback);
 
         public void RefreshSchedule()
         {
@@ -148,7 +148,7 @@ namespace OpenTween
             this.RefreshSchedule();
         }
 
-        private async void TimerCallback()
+        private async Task TimerCallback()
         {
             try
             {