Fix TerminalControl crash on exit (#10031)

## Summary of the Pull Request

ControlCore's _renderer (IRenderTarget) is allocated as std::unique_ptr,
but is given to Terminal::CreateFromSettings as a reference.
ControlCore::Close deallocates the _renderer, but if ThrottledFuncs
are still scheduled to call ControlCore::UpdatePatternLocations
it'll cause Terminal::UpdatePatterns to be called, which in turn ends up
accessing the deallocated IRenderTarget reference and lead to a crash.

A proper solution with shared pointers is nontrivial and should be
attempted at a later point in time. This solution moves the teardown of
the _renderer into ControlCore::~ControlCore, where we can be certain
that no further strong references are held by ThrottledFuncs.

## PR Checklist
* [x] Closes #9910
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed

The crash is a race condition and inherently hard to reproduce.
During validation this PR didn't appear to introduce new crashes.
This commit is contained in:
Leonard Hecker 2021-05-04 23:17:37 +02:00 committed by GitHub
parent 2559ed6efa
commit ac265aab99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 121 additions and 75 deletions

View File

@ -4,11 +4,13 @@ alignof
bitfield bitfield
bitfields bitfields
BUILDNUMBER BUILDNUMBER
charconv
CLASSNOTAVAILABLE CLASSNOTAVAILABLE
cmdletbinding cmdletbinding
colspan
COLORPROPERTY COLORPROPERTY
colspan
COMDLG COMDLG
cstdint
CXICON CXICON
CYICON CYICON
D2DERR_SHADER_COMPILE_FAILED D2DERR_SHADER_COMPILE_FAILED
@ -52,6 +54,7 @@ IFile
IInheritable IInheritable
IMap IMap
IObject IObject
iosfwd
IPackage IPackage
IPeasant IPeasant
IStorage IStorage
@ -67,7 +70,6 @@ llu
localtime localtime
lround lround
LSHIFT LSHIFT
MULTIPLEUSE
msappx msappx
MULTIPLEUSE MULTIPLEUSE
NCHITTEST NCHITTEST
@ -91,7 +93,6 @@ PAGESCROLL
PICKFOLDERS PICKFOLDERS
pmr pmr
REGCLS REGCLS
REGCLS
RETURNCMD RETURNCMD
rfind rfind
roundf roundf
@ -100,23 +101,22 @@ rx
schandle schandle
semver semver
serializer serializer
SHELLEXECUTEINFOW
shobjidl shobjidl
SINGLEUSE
SHOWMINIMIZED SHOWMINIMIZED
SINGLEUSE
SIZENS SIZENS
smoothstep smoothstep
GETDESKWALLPAPER
SHELLEXECUTEINFOW
snprintf snprintf
spsc spsc
sregex sregex
STDCPP STDCPP
strchr
STDMETHOD STDMETHOD
strchr
streambuf
Stubless Stubless
Subheader Subheader
Subpage Subpage
UPDATEINIFILE
syscall syscall
TBPF TBPF
THEMECHANGED THEMECHANGED
@ -135,12 +135,19 @@ wsregex
wwinmain wwinmain
XDocument XDocument
XElement XElement
xfacet
xhash xhash
xiosbase
xlocale
xlocbuf
xlocinfo
xlocmes xlocmes
xlocmon xlocmon
xlocnum xlocnum
xloctime xloctime
xmemory
XParse XParse
xstddef
xstring xstring
xtree xtree
xutility xutility

View File

@ -27,7 +27,6 @@ ADDREF
addressof addressof
ADDSTRING ADDSTRING
ADDTOOL ADDTOOL
aef
AEnd AEnd
AFew AFew
AFill AFill
@ -98,7 +97,6 @@ ASingle
asm asm
asmv asmv
asmx asmx
aspnet
aspx aspx
astextplain astextplain
AStomps AStomps
@ -123,7 +121,6 @@ AVerify
AVI AVI
awch awch
azuredevopspodcast azuredevopspodcast
azurewebsites
azzle azzle
backend backend
backgrounded backgrounded
@ -174,7 +171,6 @@ BODGY
BOLDFONT BOLDFONT
BOOLIFY BOOLIFY
bools bools
boostorg
Bopomofo Bopomofo
Borland Borland
BOTTOMLEFT BOTTOMLEFT
@ -187,7 +183,6 @@ branchconfig
BRK BRK
Browsable Browsable
bsearch bsearch
BSODs
bstr bstr
BTNFACE BTNFACE
buf buf
@ -202,7 +197,7 @@ BValue
byref byref
bytearray bytearray
bytebuffer bytebuffer
Cac cac
callee callee
cang cang
capslock capslock
@ -302,7 +297,7 @@ codepage
codepath codepath
codepoint codepoint
codeproject codeproject
COINIT coinit
COLLECTIONURI COLLECTIONURI
colorizing colorizing
colororacle colororacle
@ -487,7 +482,6 @@ CYSIZEFRAME
CYSMICON CYSMICON
CYVIRTUALSCREEN CYVIRTUALSCREEN
CYVSCROLL CYVSCROLL
dahall
dai dai
DATABLOCK DATABLOCK
DATAVIEW DATAVIEW
@ -575,7 +569,6 @@ defaultsettings
DEFAULTTONEAREST DEFAULTTONEAREST
DEFAULTTONULL DEFAULTTONULL
DEFAULTTOPRIMARY DEFAULTTOPRIMARY
DEFCON
defectdefs defectdefs
DEFERERASE DEFERERASE
deff deff
@ -603,7 +596,6 @@ desktopwindowxamlsource
dest dest
DESTINATIONNAME DESTINATIONNAME
devblogs devblogs
developercommunity
devicecode devicecode
devicefamily devicefamily
devops devops
@ -671,13 +663,13 @@ DUNIT
dup'ed dup'ed
dvi dvi
dw dw
dwl
DWLP DWLP
dwm dwm
dwmapi dwmapi
dword dword
dwrite dwrite
dwriteglyphrundescriptionclustermap dwriteglyphrundescriptionclustermap
dwl
dxgi dxgi
dxgidwm dxgidwm
dxinterop dxinterop
@ -770,10 +762,8 @@ fclose
fcntl fcntl
fdc fdc
FDD FDD
fde
fdopen fdopen
fdw fdw
fea
fesb fesb
FFDE FFDE
FFrom FFrom
@ -808,7 +798,6 @@ flyout
fmodern fmodern
fmtarg fmtarg
fmtid fmtid
fmtlib
FOLDERID FOLDERID
FONTCHANGE FONTCHANGE
fontdlg fontdlg
@ -1013,10 +1002,9 @@ horiz
HORZ HORZ
hostable hostable
hostlib hostlib
hotkeys
HPA HPA
HPAINTBUFFER HPAINTBUFFER
HPCON hpcon
hpj hpj
hpp hpp
HPR HPR
@ -1172,7 +1160,6 @@ IRenderer
IScheme IScheme
ISelection ISelection
IShell IShell
isocpp
issuecomment issuecomment
IState IState
IStoryboard IStoryboard
@ -1290,7 +1277,7 @@ listptr
listptrsize listptrsize
lk lk
lld lld
llvm LLVM
llx llx
LMENU LMENU
LMNOP LMNOP
@ -1435,7 +1422,6 @@ mingw
minimizeall minimizeall
minkernel minkernel
MINMAXINFO MINMAXINFO
mintty
minwin minwin
minwindef minwindef
Mip Mip
@ -1482,7 +1468,7 @@ MSIL
msix msix
msrc msrc
msvcrt msvcrt
msvcrtd MSVCRTD
MSVS MSVS
msys msys
msysgit msysgit
@ -1508,7 +1494,7 @@ namestream
nano nano
natvis natvis
nbsp nbsp
Nc nc
NCCALCSIZE NCCALCSIZE
NCCREATE NCCREATE
NCLBUTTONDOWN NCLBUTTONDOWN
@ -1631,7 +1617,6 @@ numlock
numpad numpad
NUMSCROLL NUMSCROLL
nupkg nupkg
NVDA
NVIDIA NVIDIA
NVR NVR
Nx Nx
@ -1662,11 +1647,11 @@ ONECOREWINDOWS
onehalf onehalf
ONLCR ONLCR
Oo Oo
openconsoleproxy
openbash openbash
opencode opencode
opencon opencon
openconsole openconsole
openconsoleproxy
OPENIF OPENIF
OPENLINK OPENLINK
openps openps
@ -1784,7 +1769,6 @@ pid
pidl pidl
PIDLIST PIDLIST
pii pii
pinam
pinvoke pinvoke
pipename pipename
pipestr pipestr
@ -1928,7 +1912,6 @@ pythonw
qi qi
QJ QJ
qo qo
QOL
QRSTU QRSTU
qsort qsort
queryable queryable
@ -1999,7 +1982,7 @@ REGSTR
reingest reingest
Relayout Relayout
RELBINPATH RELBINPATH
remoting Remoting
renamer renamer
renderengine renderengine
rendersize rendersize
@ -2077,8 +2060,8 @@ runut
runxamlformat runxamlformat
rvalue rvalue
RVERTICAL RVERTICAL
rxvt
RWIN RWIN
rxvt
safearray safearray
SAFECAST SAFECAST
safemath safemath
@ -2522,14 +2505,14 @@ unicode
UNICODESTRING UNICODESTRING
UNICODETEXT UNICODETEXT
UNICRT UNICRT
UNINIT uninit
uninitialize uninitialize
uninstall uninstall
Uniscribe Uniscribe
unittest unittest
unittesting unittesting
universaltest universaltest
Unk unk
unknwn unknwn
unmark unmark
UNORM UNORM
@ -2537,7 +2520,6 @@ unparseable
unpause unpause
Unregister Unregister
Unregistering Unregistering
unte
untests untests
untextured untextured
untimes untimes
@ -2594,7 +2576,6 @@ Vcount
vcpkg vcpkg
vcprintf vcprintf
vcproj vcproj
vcrt
vcvarsall vcvarsall
vcxitems vcxitems
vcxproj vcxproj
@ -2830,7 +2811,6 @@ wx
wxh wxh
xa xa
xact xact
xamarin
xaml xaml
Xamlmeta Xamlmeta
xargs xargs
@ -2872,8 +2852,9 @@ XSubstantial
xtended xtended
xterm xterm
XTest XTest
XTPUSHSGR
XTPOPSGR XTPOPSGR
XTPUSHSGR
xtr
xunit xunit
xutr xutr
xvalue xvalue

View File

@ -15,5 +15,4 @@ winui
appshellintegration appshellintegration
cppreference cppreference
gfycat gfycat
what3words
Guake Guake

62
.vscode/settings.json vendored
View File

@ -34,7 +34,67 @@
"xtree": "cpp", "xtree": "cpp",
"xutility": "cpp", "xutility": "cpp",
"span": "cpp", "span": "cpp",
"string_span": "cpp" "string_span": "cpp",
"algorithm": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"exception": "cpp",
"filesystem": "cpp",
"fstream": "cpp",
"functional": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"locale": "cpp",
"map": "cpp",
"memory_resource": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"optional": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"set": "cpp",
"shared_mutex": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"system_error": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"xfacet": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xtr1common": "cpp"
}, },
"files.exclude": { "files.exclude": {
"**/bin/**": true, "**/bin/**": true,

View File

@ -100,6 +100,29 @@ namespace winrt::Microsoft::Terminal::Control::implementation
ControlCore::~ControlCore() ControlCore::~ControlCore()
{ {
Close(); Close();
// Before destroying this instance we must ensure that we destroy the _renderer
// before the _renderEngine, as well as calling _renderer->TriggerTeardown().
// _renderEngine will be destroyed naturally after this ~destructor() returns.
decltype(_renderer) renderer;
{
// GH#8734:
// We lock the terminal here to make sure it isn't still being
// used in the connection thread before we destroy the renderer.
// However, we must unlock it again prior to triggering the
// teardown, to avoid the render thread being deadlocked. The
// renderer may be waiting to acquire the terminal lock, while
// we're waiting for the renderer to finish.
auto lock = _terminal->LockForWriting();
_renderer.swap(renderer);
}
if (renderer)
{
renderer->TriggerTeardown();
}
} }
bool ControlCore::Initialize(const double actualWidth, bool ControlCore::Initialize(const double actualWidth,
@ -1164,30 +1187,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// don't really care to wait for the connection to be completely // don't really care to wait for the connection to be completely
// closed. We can just do it whenever. // closed. We can just do it whenever.
_asyncCloseConnection(); _asyncCloseConnection();
{
// GH#8734:
// We lock the terminal here to make sure it isn't still being
// used in the connection thread before we destroy the renderer.
// However, we must unlock it again prior to triggering the
// teardown, to avoid the render thread being deadlocked. The
// renderer may be waiting to acquire the terminal lock, while
// we're waiting for the renderer to finish.
auto lock = _terminal->LockForWriting();
}
if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
{
if (auto localRenderer{ std::exchange(_renderer, nullptr) })
{
localRenderer->TriggerTeardown();
// renderer is destroyed
}
// renderEngine is destroyed
}
// we don't destroy _terminal here; it now has the same lifetime as the
// control.
} }
} }

View File

@ -45,7 +45,7 @@ namespace
virtual void TriggerRedraw(const COORD* const){}; virtual void TriggerRedraw(const COORD* const){};
virtual void TriggerRedrawCursor(const COORD* const){}; virtual void TriggerRedrawCursor(const COORD* const){};
virtual void TriggerRedrawAll(){}; virtual void TriggerRedrawAll(){};
virtual void TriggerTeardown(){}; virtual void TriggerTeardown() noexcept {};
virtual void TriggerSelection(){}; virtual void TriggerSelection(){};
virtual void TriggerScroll(){}; virtual void TriggerScroll(){};
virtual void TriggerScroll(const COORD* const delta) virtual void TriggerScroll(const COORD* const delta)

View File

@ -51,7 +51,7 @@ void ScreenBufferRenderTarget::TriggerRedrawAll()
} }
} }
void ScreenBufferRenderTarget::TriggerTeardown() void ScreenBufferRenderTarget::TriggerTeardown() noexcept
{ {
auto* pRenderer = ServiceLocator::LocateGlobals().pRender; auto* pRenderer = ServiceLocator::LocateGlobals().pRender;
const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer();

View File

@ -33,7 +33,7 @@ public:
void TriggerRedraw(const COORD* const pcoord) override; void TriggerRedraw(const COORD* const pcoord) override;
void TriggerRedrawCursor(const COORD* const pcoord) override; void TriggerRedrawCursor(const COORD* const pcoord) override;
void TriggerRedrawAll() override; void TriggerRedrawAll() override;
void TriggerTeardown() override; void TriggerTeardown() noexcept override;
void TriggerSelection() override; void TriggerSelection() override;
void TriggerScroll() override; void TriggerScroll() override;
void TriggerScroll(const COORD* const pcoordDelta) override; void TriggerScroll(const COORD* const pcoordDelta) override;

View File

@ -319,7 +319,7 @@ void Renderer::TriggerRedrawAll()
// - <none> // - <none>
// Return Value: // Return Value:
// - <none> // - <none>
void Renderer::TriggerTeardown() void Renderer::TriggerTeardown() noexcept
{ {
// We need to shut down the paint thread on teardown. // We need to shut down the paint thread on teardown.
_pThread->WaitForPaintCompletionAndDisable(INFINITE); _pThread->WaitForPaintCompletionAndDisable(INFINITE);

View File

@ -52,7 +52,7 @@ namespace Microsoft::Console::Render
void TriggerRedraw(const COORD* const pcoord) override; void TriggerRedraw(const COORD* const pcoord) override;
void TriggerRedrawCursor(const COORD* const pcoord) override; void TriggerRedrawCursor(const COORD* const pcoord) override;
void TriggerRedrawAll() override; void TriggerRedrawAll() override;
void TriggerTeardown() override; void TriggerTeardown() noexcept override;
void TriggerSelection() override; void TriggerSelection() override;
void TriggerScroll() override; void TriggerScroll() override;

View File

@ -25,7 +25,7 @@ public:
void TriggerRedraw(const COORD* const /*pcoord*/) override {} void TriggerRedraw(const COORD* const /*pcoord*/) override {}
void TriggerRedrawCursor(const COORD* const /*pcoord*/) override {} void TriggerRedrawCursor(const COORD* const /*pcoord*/) override {}
void TriggerRedrawAll() override {} void TriggerRedrawAll() override {}
void TriggerTeardown() override {} void TriggerTeardown() noexcept override {}
void TriggerSelection() override {} void TriggerSelection() override {}
void TriggerScroll() override {} void TriggerScroll() override {}
void TriggerScroll(const COORD* const /*pcoordDelta*/) override {} void TriggerScroll(const COORD* const /*pcoordDelta*/) override {}

View File

@ -38,7 +38,7 @@ namespace Microsoft::Console::Render
virtual void TriggerRedrawCursor(const COORD* const pcoord) = 0; virtual void TriggerRedrawCursor(const COORD* const pcoord) = 0;
virtual void TriggerRedrawAll() = 0; virtual void TriggerRedrawAll() = 0;
virtual void TriggerTeardown() = 0; virtual void TriggerTeardown() noexcept = 0;
virtual void TriggerSelection() = 0; virtual void TriggerSelection() = 0;
virtual void TriggerScroll() = 0; virtual void TriggerScroll() = 0;

View File

@ -39,7 +39,7 @@ namespace Microsoft::Console::Render
virtual void TriggerRedrawCursor(const COORD* const pcoord) = 0; virtual void TriggerRedrawCursor(const COORD* const pcoord) = 0;
virtual void TriggerRedrawAll() = 0; virtual void TriggerRedrawAll() = 0;
virtual void TriggerTeardown() = 0; virtual void TriggerTeardown() noexcept = 0;
virtual void TriggerSelection() = 0; virtual void TriggerSelection() = 0;
virtual void TriggerScroll() = 0; virtual void TriggerScroll() = 0;