Skip to content

Commit d23ca43

Browse files
authored
Merge pull request #38356 from heejaechang/experimentService
Fix deadlock due to VS Experiement service jumping to UI thread in certain cases.
2 parents 0f86372 + 44af010 commit d23ca43

File tree

4 files changed

+50
-5
lines changed

4 files changed

+50
-5
lines changed

src/VisualStudio/CSharp/Impl/LanguageClient/CSharpLanguageServerClient.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
4+
using System.Collections;
5+
using System.Collections.Generic;
46
using System.ComponentModel.Composition;
57
using Microsoft.CodeAnalysis.Editor;
8+
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
69
using Microsoft.CodeAnalysis.Host.Mef;
10+
using Microsoft.CodeAnalysis.Options;
711
using Microsoft.CodeAnalysis.Remote;
812
using Microsoft.CodeAnalysis.Shared.TestHooks;
913
using Microsoft.VisualStudio.Composition;
@@ -25,10 +29,15 @@ internal class CSharpLanguageServerClient : AbstractLanguageServerClient
2529
[ImportingConstructor]
2630
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
2731
public CSharpLanguageServerClient(
32+
IThreadingContext threadingContext,
2833
VisualStudioWorkspace workspace,
34+
IEnumerable<Lazy<IOptionPersister>> lazyOptions,
2935
LanguageServerClientEventListener eventListener,
3036
IAsynchronousOperationListenerProvider listenerProvider)
31-
: base(workspace,
37+
: base(
38+
threadingContext,
39+
workspace,
40+
lazyOptions,
3241
eventListener,
3342
listenerProvider,
3443
languageServerName: WellKnownServiceHubServices.CSharpLanguageServer,

src/VisualStudio/Core/Def/Implementation/LanguageClient/AbstractLanguageServerClient.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,18 @@
1313
using Microsoft.CodeAnalysis.Host;
1414
using System.Composition;
1515
using Microsoft.CodeAnalysis.Shared.TestHooks;
16+
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
17+
using Microsoft.CodeAnalysis.Options;
18+
using System.Linq;
19+
using Microsoft.CodeAnalysis.Experiments;
1620

1721
namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService
1822
{
1923
internal abstract class AbstractLanguageServerClient : ILanguageClient
2024
{
25+
private readonly IThreadingContext _threadingContext;
2126
private readonly Workspace _workspace;
27+
private readonly IEnumerable<Lazy<IOptionPersister>> _lazyOptions;
2228
private readonly LanguageServerClientEventListener _eventListener;
2329
private readonly IAsynchronousOperationListener _asyncListener;
2430

@@ -55,13 +61,17 @@ internal abstract class AbstractLanguageServerClient : ILanguageClient
5561
#pragma warning restore CS0067 // event never used
5662

5763
public AbstractLanguageServerClient(
64+
IThreadingContext threadingContext,
5865
Workspace workspace,
66+
IEnumerable<Lazy<IOptionPersister>> lazyOptions,
5967
LanguageServerClientEventListener eventListener,
6068
IAsynchronousOperationListenerProvider listenerProvider,
6169
string languageServerName,
6270
string serviceHubClientName)
6371
{
72+
_threadingContext = threadingContext;
6473
_workspace = workspace;
74+
_lazyOptions = lazyOptions;
6575
_eventListener = eventListener;
6676
_asyncListener = listenerProvider.GetListener(FeatureAttribute.FindReferences);
6777

@@ -103,6 +113,9 @@ public Task OnLoadedAsync()
103113
// set up event stream so that we start LSP server once Roslyn is loaded
104114
_eventListener.WorkspaceStarted.ContinueWith(async _ =>
105115
{
116+
// initialize things on UI thread
117+
await InitializeOnUIAsync().ConfigureAwait(false);
118+
106119
// this might get called before solution is fully loaded and before file is opened.
107120
// we delay our OOP start until then, but user might do vsstart before that. so we make sure we start OOP if
108121
// it is not running yet. multiple start is no-op
@@ -122,6 +135,24 @@ public Task OnLoadedAsync()
122135
}, TaskScheduler.Default).CompletesAsyncOperation(token);
123136

124137
return Task.CompletedTask;
138+
139+
async Task InitializeOnUIAsync()
140+
{
141+
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync();
142+
143+
// this doesn't attempt to solve our JTF and some services being not free-thread issue here, but
144+
// try to fix this particular deadlock issue only. we already have long discussion on
145+
// how we need to deal with JTF, Roslyn service requirements and VS services reality conflicting
146+
// each others. architectural fix should come from the result of that discussion.
147+
148+
// Ensure the options persisters are loaded since we have to fetch options from the shell
149+
_lazyOptions.Select(o => o.Value);
150+
151+
// experimentation service unfortunately uses JTF to jump to UI thread in certain cases
152+
// which can cause deadlock if 2 parties try to enable OOP from BG and then FG before
153+
// experimentation service tries to jump to UI thread.
154+
var experimentationService = _workspace.Services.GetService<IExperimentationService>();
155+
}
125156
}
126157

127158
/// <summary>

src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@ private void SetRemoteHostBitness()
181181
var x64 = _workspace.Options.GetOption(RemoteHostOptions.OOP64Bit);
182182
if (!x64)
183183
{
184-
x64 = _workspace.Services.GetService<IExperimentationService>().IsExperimentEnabled(
185-
WellKnownExperimentNames.RoslynOOP64bit);
184+
x64 = _workspace.Services.GetService<IExperimentationService>().IsExperimentEnabled(WellKnownExperimentNames.RoslynOOP64bit);
186185
}
187186

188187
// log OOP bitness

src/VisualStudio/VisualBasic/Impl/LanguageClient/VisualBasicLanguageServerClient.vb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
Imports System.ComponentModel.Composition
44
Imports Microsoft.CodeAnalysis.Editor
5+
Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities
56
Imports Microsoft.CodeAnalysis.Host.Mef
7+
Imports Microsoft.CodeAnalysis.Options
68
Imports Microsoft.CodeAnalysis.Remote
79
Imports Microsoft.CodeAnalysis.Shared.TestHooks
810
Imports Microsoft.VisualStudio.LanguageServer.Client
@@ -23,10 +25,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageClient
2325

2426
<ImportingConstructor>
2527
<Obsolete(MefConstruction.ImportingConstructorMessage, True)>
26-
Public Sub New(workspace As VisualStudioWorkspace,
28+
Public Sub New(threadingContext As IThreadingContext,
29+
workspace As VisualStudioWorkspace,
30+
lazyOptions As IEnumerable(Of Lazy(Of IOptionPersister)),
2731
eventListener As LanguageServerClientEventListener,
2832
listenerProvider As IAsynchronousOperationListenerProvider)
29-
MyBase.New(workspace,
33+
MyBase.New(threadingContext,
34+
workspace,
35+
lazyOptions,
3036
eventListener,
3137
listenerProvider,
3238
languageServerName:=WellKnownServiceHubServices.VisualBasicLanguageServer,

0 commit comments

Comments
 (0)