diff options
author | Lluis Sanchez <llsan@microsoft.com> | 2019-03-08 21:59:16 +0300 |
---|---|---|
committer | Lluis Sanchez <llsan@microsoft.com> | 2019-03-08 21:59:16 +0300 |
commit | d2566c3f41e9326b2ce121bbdbdd762896cbcc80 (patch) | |
tree | 83f954e96bd91af530f3cdeb37f2192f3487b325 /main | |
parent | d9b2683518bca9d8e6cdd067d1726ed1faf4612d (diff) | |
parent | 789eb8dccef5fd4d4b35a2c2a44ef743f0217bd3 (diff) |
Merge remote-tracking branch 'origin/release-8.0-integration' into merge-integration-11
Diffstat (limited to 'main')
4 files changed, 89 insertions, 3 deletions
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs index 020f46d3bd..3f675e740f 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs @@ -1487,8 +1487,9 @@ namespace MonoDevelop.SourceEditor } else if (args.Button == 1) { if (!string.IsNullOrEmpty (Document.FileName)) { if (args.LineSegment != null) { - int column = TextEditor.Caret.Line == args.LineNumber ? TextEditor.Caret.Column : 1; - + int column = TextEditor.Caret.Line == args.LineNumber ? + Math.Min (TextEditor.Caret.Column, args.LineSegment.Length) : 1; + lock (breakpoints) breakpoints.Toggle (Document.FileName, args.LineNumber, column); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/AsyncCriticalSection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/AsyncCriticalSection.cs index 98c6874003..78d8b18fa5 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/AsyncCriticalSection.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/AsyncCriticalSection.cs @@ -67,7 +67,13 @@ namespace MonoDevelop.Projects locked = true; return Task.FromResult (criticalSectionDisposer); } - var s = new TaskCompletionSource<IDisposable> (); + + // When the TaskCompletionSource's SetResult method is called then all the async continuations waiting + // on this lock may be invoked synchronously. This can cause a stack overflow if many tasks are queued. + // To avoid this the continuations are run asynchronously by creating the TaskCompletionSource with + // TaskCreationOptions.RunContinuationsAsynchronously. + // https://stackoverflow.com/questions/28321457/taskcontinuationoptions-runcontinuationsasynchronously-and-stack-dives + var s = new TaskCompletionSource<IDisposable> (TaskCreationOptions.RunContinuationsAsynchronously); queue.Enqueue (s); return s.Task; } diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj index f556b66d0d..97762a7653 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj @@ -117,6 +117,7 @@ <Compile Include="MonoDevelop.Core\SdkResolverTests.cs" /> <Compile Include="MonoDevelop.Core\FileServiceEventQueueTests.cs" /> <Compile Include="MonoDevelop.Core\FileServiceEventStateMachineTests.cs" /> + <Compile Include="MonoDevelop.Projects\AsyncCriticalSectionTests.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\src\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/AsyncCriticalSectionTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/AsyncCriticalSectionTests.cs new file mode 100644 index 0000000000..3406f814e1 --- /dev/null +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/AsyncCriticalSectionTests.cs @@ -0,0 +1,78 @@ +// +// AsyncCriticalSectionTests.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2019 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace MonoDevelop.Projects +{ + [TestFixture] + public class AsyncCriticalSectionTests + { + AsyncCriticalSection referenceCacheLock; + ManualResetEvent unlockEvent; + ManualResetEvent doneEvent; + const int maxLoadProjectCalls = 10000; + + [Test] + public void StackOverflowTest () + { + referenceCacheLock = new AsyncCriticalSection (); + unlockEvent = new ManualResetEvent (false); + doneEvent = new ManualResetEvent (false); + + for (int i = 0; i < maxLoadProjectCalls; ++i) { + Run (i); + } + + Thread.Sleep (500); + + unlockEvent.Set (); + bool result = doneEvent.WaitOne (5000); + if (!result) + Assert.Fail ("Done event not fired."); + } + + void Run (int i) + { + Task.Run (async () => { + await LoadProject (i); + }); + } + + async Task LoadProject (int i) + { + using (await referenceCacheLock.EnterAsync ().ConfigureAwait (false)) { + unlockEvent.WaitOne (); + + if (i == maxLoadProjectCalls - 1) + doneEvent.Set (); + } + } + } +} |