dotnet / project-system

The .NET Project System for Visual Studio
MIT License
969 stars 387 forks source link

Deadlock while closing the project #2377

Closed davkean closed 4 years ago

davkean commented 7 years ago

This test ran into a deadlock while closing the project: https://github.com/dotnet/roslyn/pull/19863.

 `ntdll.dll!_NtWaitForMultipleObjects@20�() Unknown
    KERNELBASE.dll!WaitForMultipleObjectsEx(unsigned long nCount, void * const * lpHandles, int bWaitAll, unsigned long dwMilliseconds, int bAlertable) Line 1551   C
    KERNELBASE.dll!WaitForMultipleObjects(unsigned long nCount, void * const * lpHandles, int bWaitAll, unsigned long dwMilliseconds) Line 1403 C
    Microsoft.VisualStudio.Threading.ni.dll!69143812()  Unknown
    [Frames below may be incorrect and/or missing, native debugger attempting to walk managed call stack]   
    Microsoft.VisualStudio.Threading.ni.dll!69143812()  Unknown
    [Managed to Native Transition]  
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.NoMessagePumpSyncContext.Wait(System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) Line 65 C#
    mscorlib.dll!System.Threading.SynchronizationContext.InvokeWaitMethodHelper(System.Threading.SynchronizationContext syncContext, System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) Line 349   C#
    [Native to Managed Transition]  
    mscorlib.ni.dll!71a9a507()  Unknown
    [Managed to Native Transition]  
    mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout, bool exitContext) Line 203  C#
    mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout) Line 213    C#
    mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 645 C#
    mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3327  C#
    mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3259  C#
    mscorlib.dll!System.Threading.Tasks.Task.Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Line 3167  C#
    mscorlib.dll!System.Threading.Tasks.Task.Wait(System.TimeSpan timeout) Line 3083    C#
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTaskFactory.WaitSynchronouslyCore(System.Threading.Tasks.Task task) Line 313  C#
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTaskFactory.WaitSynchronously(System.Threading.Tasks.Task task) Line 290  C#
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTask.CompleteOnCurrentThread() Line 841   C#
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTask<Microsoft.VisualStudio.ProjectSystem.VS.HResult>.CompleteOnCurrentThread() Line 88   C#
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTaskFactory.Run<Microsoft.VisualStudio.ProjectSystem.VS.HResult>(System.Func<System.Threading.Tasks.Task<Microsoft.VisualStudio.ProjectSystem.VS.HResult>> asyncMethod, Microsoft.VisualStudio.Threading.JoinableTaskCreationOptions creationOptions) Line 459    C#
    Microsoft.VisualStudio.ProjectSystem.Implementation.dll!Microsoft.VisualStudio.ProjectSystem.ProjectMultiThreadedService.ExecuteSynchronously<Microsoft.VisualStudio.ProjectSystem.VS.HResult>(System.Func<System.Threading.Tasks.Task<Microsoft.VisualStudio.ProjectSystem.VS.HResult>> asyncAction) Line 139  C#
    Microsoft.VisualStudio.ProjectSystem.VS.Implementation.dll!Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.ProjectNode.HrInvoke.AnonymousMethod__0() Line 3738   C#
    Microsoft.VisualStudio.ProjectSystem.VS.dll!Microsoft.VisualStudio.ProjectSystem.VS.HResult.Invoke(System.Func<Microsoft.VisualStudio.ProjectSystem.VS.HResult> action, System.IServiceProvider vsShellServiceProvider, Microsoft.VisualStudio.ProjectSystem.IProjectFaultHandlerService projectFaultHandlerService, Microsoft.VisualStudio.ProjectSystem.UnconfiguredProject project) Line 313 C#
    Microsoft.VisualStudio.ProjectSystem.VS.Implementation.dll!Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.ProjectNode.HrInvoke(System.Func<System.Threading.Tasks.Task<Microsoft.VisualStudio.ProjectSystem.VS.HResult>> asyncAction, bool registerProjectFaultHandlerService) Line 3735    C#
    Microsoft.VisualStudio.ProjectSystem.VS.Implementation.dll!Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.ProjectNode.Close() Line 1230 C#
    Microsoft.VisualStudio.ProjectSystem.VS.Implementation.dll!Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.ProjectNodeWrapper.Close() Line 545   C#
    [Native to Managed Transition]  
    msenv.dll!CProject::Close(int fRemoveProjectDir) Line 615   C++
    msenv.dll!CSolution::RemoveProjectByCProject(CProject * pProject, int fUnloadProject, int nUnloadReasonID, unsigned long dwUnloadStatus) Line 22347 C++
    msenv.dll!CSolution::RemoveProjectByHierarchy(IVsHierarchy * pHierarchy, int fUnloadProject) Line 22305 C++
    msenv.dll!CSolution::CloseSolutionElement(unsigned long grfCloseOpts, IVsHierarchy * pHier, unsigned long docCookie) Line 6179  C++
    msenv.dll!CSolution::ClearProjectsInternal(bool fSkipNested, std::set<CProject *,std::less<CProject *>,std::allocator<CProject *> > & projectsAlreadyClosed) Line 12892 C++
    msenv.dll!CSolution::Clear(bool bClearProjects) Line 12665  C++
    msenv.dll!CSolution::Close(int fDoSave, int fSaveAll, int fUpdateMRU) Line 18612    C++
    msenv.dll!CVSolution::Close::__l2::<lambda>() Line 781  C++
    msenv.dll!VsSupportErrorInfoSafeInvoke<HRESULT <lambda>(void) >(CVSolution::Close::__l2::HRESULT <lambda>(void) pfnFunctionImpl) Line 74    C++
    msenv.dll!CVSolution::Close(short SaveFirst) Line 784   C++
    rpcrt4.dll!_Invoke@12() Line 98 Unknown
    rpcrt4.dll!NdrStubCall2(void * pThis, void * pChannel, _RPC_MESSAGE * pRpcMsg, unsigned long * pdwStubPhase) Line 1455  C++
    combase.dll!CStdStubBuffer_Invoke(IRpcStubBuffer * This, tagRPCOLEMESSAGE * prpcmsg, IRpcChannelBuffer * pRpcChannelBuffer) Line 1449   C++
    oleaut32.dll!CUnivStubWrapper::Invoke(tagRPCOLEMESSAGE * pMsg, IRpcChannelBuffer * pChnl) Line 615  C++
    [Inline Frame] combase.dll!InvokeStubWithExceptionPolicyAndTracing::__l6::<lambda_1ba7c1521bf8e7d0ebd8f0b3c0295667>::operator()() Line 1824 C++
    combase.dll!ObjectMethodExceptionHandlingAction<<lambda_1ba7c1521bf8e7d0ebd8f0b3c0295667> >(InvokeStubWithExceptionPolicyAndTracing::__l6::<lambda_1ba7c1521bf8e7d0ebd8f0b3c0295667> action, ObjectMethodExceptionHandlingInfo * pExceptionHandlingInfo, ExceptionHandlingResult * pExceptionHandlingResult, void *) Line 91    C++
    [Inline Frame] combase.dll!InvokeStubWithExceptionPolicyAndTracing(IRpcStubBuffer * pMsg, tagRPCOLEMESSAGE *) Line 1822 C++
    combase.dll!DefaultStubInvoke(bool bIsAsyncBeginMethod, IServerCall * pServerCall, IRpcChannelBuffer * pChannel, IRpcStubBuffer * pStub, unsigned long * pdwFault) Line 1891    C++
    combase.dll!ServerCall::ContextInvoke(tagRPCOLEMESSAGE * pMessage, IRpcStubBuffer * pStub, CServerChannel * pChannel, tagIPIDEntry * pIPIDEntry, unsigned long * pdwFault) Line 1541    C++
    combase.dll!AppInvoke(ServerCall * pServerCall, CServerChannel * pChannel, IRpcStubBuffer * pStub, void * pv, void * pStubBuffer, tagIPIDEntry * pIPIDEntry, WireLocalThis * pLocalb) Line 1604 C++
    combase.dll!ComInvokeWithLockAndIPID(ServerCall * pServerCall, tagIPIDEntry * pIPIDEntry, bool * pbCallerResponsibleForRequestMessageCleanup) Line 2722 C++
    combase.dll!ThreadWndProc(HWND__ * window, unsigned int message, unsigned int wparam, long params) Line 734 C++
    user32.dll!__InternalCallWinProc@20�()  Unknown
    user32.dll!UserCallWinProcCheckWow()    Unknown
    user32.dll!DispatchMessageWorker()  Unknown
    user32.dll!_DispatchMessageW@4�()   Unknown
    [Inline Frame] combase.dll!CCliModalLoop::MyDispatchMessage(tagMSG *) Line 3148 C++
    combase.dll!CCliModalLoop::PeekRPCAndDDEMessage() Line 2796 C++
    combase.dll!CCliModalLoop::FindMessage(unsigned long dwStatus) Line 2867    C++
    combase.dll!CCliModalLoop::HandleWakeForMsg() Line 2484 C++
    combase.dll!CCliModalLoop::BlockFn(void * * ahEvent, unsigned long cEvents, unsigned long * lpdwSignaled) Line 2396 C++
    combase.dll!ClassicSTAThreadWaitForHandles(unsigned long dwFlags, unsigned long dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * pdwIndex) Line 53 C++
    combase.dll!CoWaitForMultipleHandles(unsigned long dwFlags, unsigned long dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * lpdwindex) Line 123 C++
    vslog.dll!663714f6()    Unknown
    [Managed to Native Transition]  
    mscorlib.dll!System.Lazy<uint>.LazyInitValue() Line 386 C#
    mscorlib.dll!System.Lazy<uint>.Value.get() Line 339 C#
    Microsoft.VisualStudio.LanguageServices.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.FileChangeTracker.EnsureSubscription() Unknown
    Microsoft.VisualStudio.LanguageServices.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioMetadataReference.CurrentSnapshot.get()    Unknown
    Microsoft.VisualStudio.LanguageServices.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.AbstractProject.AddMetadataReferenceCore(Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioMetadataReference reference) Unknown
    Microsoft.VisualStudio.LanguageServices.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.AbstractProject.AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(string filePath, Microsoft.CodeAnalysis.MetadataReferenceProperties properties)   Unknown
    Microsoft.VisualStudio.LanguageServices.Implementation.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.CPS.CPSProject.AddMetadataReference.AnonymousMethod__0()    Unknown
    Microsoft.VisualStudio.LanguageServices.Implementation.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.CPS.CPSProject.ExecuteForegroundAction(System.Action action)    Unknown
    Microsoft.VisualStudio.LanguageServices.Implementation.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.CPS.CPSProject.AddMetadataReference(string referencePath, Microsoft.CodeAnalysis.MetadataReferenceProperties properties)    Unknown
    Microsoft.VisualStudio.ProjectSystem.Managed.dll!Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers.MetadataReferenceItemHandler.Handle(Microsoft.VisualStudio.ProjectSystem.LanguageServices.BuildOptions added, Microsoft.VisualStudio.ProjectSystem.LanguageServices.BuildOptions removed, Microsoft.VisualStudio.LanguageServices.ProjectSystem.IWorkspaceProjectContext context, bool isActiveContext) Line 37 C#
    Microsoft.VisualStudio.ProjectSystem.Managed.dll!Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers.CommandLineItemHandler.ProcessItems(Microsoft.VisualStudio.ProjectSystem.IProjectChangeDescription projectChange, Microsoft.VisualStudio.LanguageServices.ProjectSystem.IWorkspaceProjectContext context, bool isActiveContext) Line 80 C#
    Microsoft.VisualStudio.ProjectSystem.Managed.dll!Microsoft.VisualStudio.ProjectSystem.LanguageServices.Handlers.CommandLineItemHandler.Handle(Microsoft.VisualStudio.ProjectSystem.IProjectChangeDescription projectChange, Microsoft.VisualStudio.LanguageServices.ProjectSystem.IWorkspaceProjectContext context, bool isActiveContext) Line 55   C#
    Microsoft.VisualStudio.ProjectSystem.Managed.dll!Microsoft.VisualStudio.ProjectSystem.LanguageServices.LanguageServiceHost.HandleAsync.AnonymousMethod__2() Line 310    C#
    user32.dll!__InternalCallWinProc@20�()  Unknown
    user32.dll!UserCallWinProcCheckWow()    Unknown
    user32.dll!DispatchMessageWorker()  Unknown
    user32.dll!_DispatchMessageW@4�()   Unknown
    msenv.dll!MainMessageLoop::ProcessMessage(IMsoStdComponentMgr * pstdcm, IVsWindowManager * pIVsWindowManager, tagMSG & msg) Line 1857   C++
    msenv.dll!CMsoCMHandler::EnvironmentMsgLoop() Line 450  C++
    msenv.dll!CMsoCMHandler::FPushMessageLoop(unsigned long uReason) Line 358   C++
    msenv.dll!SCM::FPushMessageLoop(SCMI * pscmi, unsigned long uReason, void * pvLoopData) Line 2262   C++
    msenv.dll!SCM_MsoCompMgr::FPushMessageLoop(unsigned long dwComponentID, unsigned long uReason, void * pvLoopData) Line 2998 C++
    msenv.dll!CMsoComponent::PushMsgLoop(unsigned long msgloop) Line 705    C++
    msenv.dll!VStudioMainLogged() Line 1366 C++
    msenv.dll!VStudioMain(MAINPARAM * pMainParam) Line 1595 C++
    devenv.exe!util_CallVsMain(MAINPARAM * pMainParam, int * piRes) Line 1142   C++
    devenv.exe!CDevEnvAppId::Run(wchar_t * wszCmdLine, int nCmdShow) Line 909   C++
    devenv.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * __formal, char * lpCmdLine, int nCmdShow) Line 71 C++
    [Inline Frame] devenv.exe!invoke_main() Line 94 C++
    devenv.exe!__scrt_common_main_seh() Line 259    C++
    kernel32.dll!@BaseThreadInitThunk@12�() Unknown
    ntdll.dll!__RtlUserThreadStart()    Unknown
    ntdll.dll!__RtlUserThreadStart@8�() Unknown
    [Resuming Async Method] 
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Line 1090    C#
    mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 954  C#
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 902  C#
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Line 1070  C#
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTaskFactory.SingleExecuteProtector.TryExecute() Line 1128 C#
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTaskFactory.SingleExecuteProtector..cctor.AnonymousMethod__20_0(object state) Line 955    C#
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Line 111 C#
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Line 37 C#
    WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() Line 583  C#
    WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state) Line 528 C#
    WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj) Line 235  C#
    mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 954  C#
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 902  C#
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 891    C#
    WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 185 C#
    WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() Line 441  C#
    WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() Line 2251    C#
    WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Line 2504    C#
    WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Line 345    C#
    WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) Line 494    C#
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) Line 104 C#
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Line 37 C#
    WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) Line 1445 C#
    WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) Line 398 C#
    [Native to Managed Transition]  
    [Async Call]    
    Microsoft.VisualStudio.ProjectSystem.Managed.dll!Microsoft.VisualStudio.ProjectSystem.LanguageServices.LanguageServiceHost.ExecuteWithinLockAsync.AnonymousMethod__0() Line 159 C#
    [Async Call]    
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTask.JoinAsync(System.Threading.CancellationToken cancellationToken) Line 519 C#
    [Async Call]    
    Microsoft.VisualStudio.ProjectSystem.Managed.dll!Microsoft.VisualStudio.ProjectSystem.LanguageServices.LanguageServiceHost.HandleAsync(Microsoft.VisualStudio.ProjectSystem.IProjectVersionedValue<Microsoft.VisualStudio.ProjectSystem.IProjectSubscriptionUpdate> update, Microsoft.VisualStudio.ProjectSystem.LanguageServices.RuleHandlerType handlerType) Line 286 C#
    [Async Call]    
    Microsoft.VisualStudio.ProjectSystem.Managed.dll!Microsoft.VisualStudio.ProjectSystem.LanguageServices.LanguageServiceHost.OnProjectChangedCoreAsync.AnonymousMethod__0() Line 112  C#
    [Async Call]    
    Microsoft.VisualStudio.Threading.dll!Microsoft.VisualStudio.Threading.JoinableTask.JoinAsync(System.Threading.CancellationToken cancellationToken) Line 519 C#
    Microsoft.VisualStudio.ProjectSystem.Implementation.dll!Microsoft.VisualStudio.ProjectSystem.ProjectAsynchronousTasksServiceBase.DrainTaskQueueAsync(Microsoft.VisualStudio.ProjectSystem.ProjectCriticalOperation operation, bool drainCurrentQueueOnly, bool throwExceptions, System.Threading.CancellationToken cancellationToken) Line 288  C#
    [Async Call]    
    Microsoft.VisualStudio.ProjectSystem.Managed.dll!Microsoft.VisualStudio.ProjectSystem.LanguageServices.LanguageServiceHost.OnProjectChangedCoreAsync(Microsoft.VisualStudio.ProjectSystem.IProjectVersionedValue<Microsoft.VisualStudio.ProjectSystem.IProjectSubscriptionUpdate> e, Microsoft.VisualStudio.ProjectSystem.LanguageServices.RuleHandlerType handlerType) Line 110    C#
    [Async Call]    
    Microsoft.VisualStudio.ProjectSystem.VS.Implementation.dll!Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.ProjectNode.Close.AnonymousMethod__441_0() Line 1236  C#
davkean commented 7 years ago

Looks like the project is changing during close, we're sending on those changes to Roslyn - which is causing the project to close again due to pumping created a value. @lifengl thoughts on this? We probably shouldn't be sending on stuff to Roslyn during close, but any idea why it's been closed twice?

davkean commented 7 years ago

Okay, looked at the dump - the project isn't changing during close I read the stack wrong. This is what is happening:

1) Close is being called 2) This drains critical tasks 3) This calls LanguageServiceHost, which in turn calls Roslyn 4) Roslyn calls Lazy<>.Value which pumps (!) 5) This causes Solution.Close to be called 6) Which attempts to close again (wrapped in ExecuteSynchronously), which blocks

@Lifengl I'm not entirely sure why 6) blocks, I kinda assumed a reentrancy into http://index/?query=ProjectNode.Close&rightProject=Microsoft.VisualStudio.ProjectSystem.VS.Implementation&file=Package%5CNodes%5CProjectNode.cs&line=1236?

Anyway not sure what to do here - we should definitely avoid doing any work on Unload and I'll submit a PR to do an upfront check inside of a LoadProjectAsync - but there's still a race after that check.

Pilchie commented 7 years ago

I downloaded all the artifacts (in case the roslyn pdbs matter), and put them at \\mlangfs1\public\kevinpi\project-hang-archive.zip

lifengl commented 7 years ago

not sure, I guess Solution.Close in the 6th step will still wait critical tasks to be drained. I wonder that we should prevent the message loop reentry caused by the Lazy here. Reentry can easily cause dead lock if anything on the top waits the bottom side to finish.

davkean commented 7 years ago

@lifengl How do you suggest we avoid reentrancy? Basically any wait or lazy init is going to pump...

Pilchie commented 7 years ago

@davkean See http://source.roslyn.io/#Microsoft.VisualStudio.LanguageServices/Implementation/ProjectSystem/AbstractProject.cs,1012

lifengl commented 7 years ago

VS threading library uses some similar to @Pilchie pointed out.

davkean commented 7 years ago

@lifengl At what point do you want to avoid reentrancy? It sounds like Close itself should be doing it.

lifengl commented 7 years ago

Pumping messages and COM reentrancy can introduce some odd dead lock issues, because it can be a very random call made into the system at a random time. So if we can prevent that to happen, it reduces chances to run into dead locks. JTF library was created for that reason.

davkean commented 7 years ago

@lifengl Not sure I understand - where in the code should we stop pumping? And how?

lifengl commented 7 years ago

The reason Lazy<> can pump message is it calls Monitor.Enter, which controls whether the thread need pumps message or not is the current SynchronizationContext (so it never pumps message inside a thread pool thread). To suppress the message pump, you need set the current SynchronizationContext and recovers after that. But we generally don't want to do that, when we run random other code. So, maybe the safer bet is not to use Lazy<> here.

You can check NoMessagePumpSynchronizationContext usage inside VS threading library to see how it suppresses message pumps (around lock() {}). I think that is what @Pilchie pointed out as well. Andrew is expert on this.

davkean commented 7 years ago

I don't think a statement of "don't call code that pumps" is valid - CPS is an open system and any code can run. Even an innocuous bug fix could cause us or Roslyn to start pumping here.

I'm also not sure we're the right place to prevent pumping - NuGet runs in this code path as well, what if it starts pumping?

CyrusNajmabadi commented 7 years ago

Note: this issue is affecting reliability of Roslyn Jenkins runs (since VS is deadlocking on shutdown). This is clearly a fairly unpleasant pain point for the Roslyn dev team.

lifengl commented 7 years ago

@davkean, i guess you are right. If the project.close is coming from a COM call in the background thread, it is more a problem the background thread code violates thread rules, than the message pump itself. I would like to take a look the dump file of this issue. Can you tell me where to find it?

CyrusNajmabadi commented 7 years ago

I uploaded a dump here: https://drive.google.com/file/d/0ByISdSEZ09PBZWVaM2dOWThTR1U/view?usp=sharing

(dave thinks its the same issue).

lifengl commented 7 years ago

Thanks, I took a look the dump. Somehow, our windbg extension didn't work for that dump, (maybe need update the extension?) Anyway, what I found:

1, I don't think solution.Close has been reentered, it is just called during a message pump for a local 'lock', which is very nasty issue. But the call is unrelated to any of the product routine, it is just initialized by thread 30, which is an automation test driving thread:

10 1ea1e598 10909c86 EnvDTE!DomainNeutralILStubClass.IL_STUB_CLRtoCOM(Boolean)+0x96 *** ERROR: Module load completed but symbols could not be loaded for Microsoft.VisualStudio.IntegrationTest.Utilities.dll 11 1ea1e5e8 7320ea56 Microsoft_VisualStudio_IntegrationTest_Utilities!Microsoft.VisualStudio.IntegrationTest.Utilities.InProcess.SolutionExplorer_InProc.CleanUpOpenSolution()+0x16e

Basically, this code violate VS thread rule. It must use JTF library to switch to the UI thread before making any COM calls, without doing that, it can call inside any random time inside the UI thread. Since it is inside an automation test code, I would consider this to be a defect of tests, not a product bug.

2, the solution code will end up to wait critical background tasks to finish. It has no idea that one task is actually running in the call stack below it, so it will never have a chance to finish (until solution is closed), so there is no reason we think we can add some logic inside solution close logic to prevent that. In this case, it is Microsoft.VisualStudio.ProjectSystem.LanguageServices.LanguageServiceHost+<>c__DisplayClass24_0.b__0

Frankly, there is no reason to wait language service to finish all work before closing the project. The extra work to maintain the language service provides little value to the user, because they will be thrown away anyway. The reason the code registers itself to the critical task list is because it sometime accesses the project model, and it doesn't want the model becomes invalid at a random time, so it can crash. But the service does have a long waiting queue. I guess the code can cancel it quickly by listening project unloading event, or registers itself only when a request is being processed. But I guess it doesn't help for this bug. You can register the cancellation token, so the task project.close waits on can finish quickly, but that means the project can become invalid in the middle of your language code (which is now running on the UI thread) running. That is exactly what you want to prevent at the first place.

Anyway, put into simple words: there is nothing we can do in the solution close logic to help you get out of this. You can either push the language service code to run in the background thread (instead of switching to UI thread), and eliminates the chance of reentry problem. Or hope COM calls to follow threading rules, and treat this is a test defect.