Fix race condition in IsInvokeRequired on Android
authorE.Z. Hart <hartez@users.noreply.github.com>
Thu, 7 Apr 2016 03:46:01 +0000 (21:46 -0600)
committerJason Smith <jason.smith@xamarin.com>
Thu, 7 Apr 2016 03:46:01 +0000 (20:46 -0700)
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/IsInvokeRequiredRaceCondition.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Platform.Android/Forms.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/IsInvokeRequiredRaceCondition.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/IsInvokeRequiredRaceCondition.cs
new file mode 100644 (file)
index 0000000..0919214
--- /dev/null
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xamarin.Forms.CustomAttributes;
+
+#if UITEST
+using Xamarin.UITest;
+using NUnit.Framework;
+#endif
+
+
+namespace Xamarin.Forms.Controls
+{
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.None, 0, "Device.IsInvokeRequired race condition causes crash")]
+       public class IsInvokeRequiredRaceCondition : TestContentPage
+       {
+               protected override void Init()
+               {
+                       var button = new Button
+                       {
+                               AutomationId = "crashButton",
+                               Text = "Start Test"
+                       };
+
+                       var success = new Label { Text = "Success", IsVisible = false, AutomationId = "successLabel" };
+
+                       var instructions = new Label { Text = "Click the Start Test button. " };
+
+                       Content = new StackLayout
+                       {
+                               HorizontalOptions = LayoutOptions.Fill,
+                               VerticalOptions = LayoutOptions.Fill,
+                               Children = { instructions, success, button }
+                       };
+
+                       button.Clicked += async (sender, args) =>
+                       {
+                               await Task.WhenAll(GenerateTasks());
+                               success.IsVisible = true;
+                       };
+               }
+
+               List<Task> GenerateTasks()
+               {
+                       var result = new List<Task>();
+
+                       for (int n = 0; n < 1000; n++)
+                       {
+                               result.Add(Task.Run(() => { var t = Device.IsInvokeRequired; } ));
+                       }
+
+                       return result;
+               }
+
+#if UITEST
+               [Test]
+               public void ShouldNotCrash()
+               {
+                       RunningApp.Tap(q => q.Marked("crashButton"));
+                       RunningApp.WaitForElement(q => q.Marked("successLabel"));
+               }
+#endif
+
+       }
+}
\ No newline at end of file
index e58378d..f843a2e 100644 (file)
@@ -99,6 +99,7 @@
       <DependentUpon>Bugzilla38416.xaml</DependentUpon>
     </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)InputTransparentIssue.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)IsInvokeRequiredRaceCondition.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)IsPasswordToggleTest.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue1025.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue1026.cs" />
index b2ded24..65bfbc4 100644 (file)
@@ -362,15 +362,7 @@ namespace Xamarin.Forms
                                return new _IsolatedStorageFile(IsolatedStorageFile.GetUserStoreForApplication());
                        }
 
-                       public bool IsInvokeRequired
-                       {
-                               get
-                               {
-                                       using (Looper my = Looper.MyLooper())
-                                       using (Looper main = Looper.MainLooper)
-                                               return my != main;
-                               }
-                       }
+                       public bool IsInvokeRequired => !Looper.MainLooper.IsCurrentThread;
 
                        public void OpenUriAction(Uri uri)
                        {