Skip to content

Commit a3d2850

Browse files
lheckerDHowett
authored andcommitted
Fix a handoff deadlock if layout completes synchronously (microsoft#18676)
I've received a dump from an affected user, and it showed that the layout event in TerminalPage was raised synchronously. This meant that during page initialization, the handoff listener was started while still being stuck inside the handoff listener. This resulted in a deadlock. This PR fixes the issue by not holding the lock across handoff callback calls. Closes microsoft#18634 ## Validation Steps Performed * Can't repro ❌ (cherry picked from commit 2693210) Service-Card-Id: PVTI_lADOAF3p4s4AmhmQzgYLZvQ Service-Version: 1.22
1 parent cb29bed commit a3d2850

File tree

5 files changed

+20
-36
lines changed

5 files changed

+20
-36
lines changed

src/cascadia/TerminalConnection/CTerminalHandoff.cpp

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,33 @@ static DWORD g_cTerminalHandoffRegistration = 0;
1414
// Mutex so we only do start/stop/establish one at a time.
1515
static std::shared_mutex _mtx;
1616

17+
// This is the callback that will be called when a connection is received.
18+
// Call this once during startup and don't ever change it again (race condition).
19+
void CTerminalHandoff::s_setCallback(NewHandoffFunction callback) noexcept
20+
{
21+
_pfnHandoff = callback;
22+
}
23+
1724
// Routine Description:
1825
// - Starts listening for TerminalHandoff requests by registering
1926
// our class and interface with COM.
2027
// Arguments:
2128
// - pfnHandoff - Function to callback when a handoff is received
2229
// Return Value:
2330
// - S_OK, E_NOT_VALID_STATE (start called when already started) or relevant COM registration error.
24-
HRESULT CTerminalHandoff::s_StartListening(NewHandoffFunction pfnHandoff)
31+
HRESULT CTerminalHandoff::s_StartListening()
2532
try
2633
{
2734
std::unique_lock lock{ _mtx };
2835

29-
RETURN_HR_IF(E_NOT_VALID_STATE, _pfnHandoff != nullptr);
30-
3136
const auto classFactory = Make<SimpleClassFactory<CTerminalHandoff>>();
32-
33-
RETURN_IF_NULL_ALLOC(classFactory);
37+
RETURN_LAST_ERROR_IF_NULL(classFactory);
3438

3539
ComPtr<IUnknown> unk;
3640
RETURN_IF_FAILED(classFactory.As(&unk));
3741

3842
RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &g_cTerminalHandoffRegistration));
3943

40-
_pfnHandoff = pfnHandoff;
41-
4244
return S_OK;
4345
}
4446
CATCH_RETURN()
@@ -53,15 +55,6 @@ CATCH_RETURN()
5355
HRESULT CTerminalHandoff::s_StopListening()
5456
{
5557
std::unique_lock lock{ _mtx };
56-
return s_StopListeningLocked();
57-
}
58-
59-
// See s_StopListening()
60-
HRESULT CTerminalHandoff::s_StopListeningLocked()
61-
{
62-
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
63-
64-
_pfnHandoff = nullptr;
6558

6659
if (g_cTerminalHandoffRegistration)
6760
{
@@ -92,22 +85,15 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE* in, HANDLE* out, HANDLE si
9285
{
9386
try
9487
{
95-
std::unique_lock lock{ _mtx };
96-
97-
// s_StopListeningLocked sets _pfnHandoff to nullptr.
98-
// localPfnHandoff is tested for nullness below.
99-
#pragma warning(suppress : 26429) // Symbol '...' is never tested for nullness, it can be marked as not_null (f.23).
100-
auto localPfnHandoff = _pfnHandoff;
101-
10288
// Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call.
10389
// COM does not automatically clean that up for us. We must do it.
104-
LOG_IF_FAILED(s_StopListeningLocked());
90+
LOG_IF_FAILED(s_StopListening());
10591

10692
// Report an error if no one registered a handoff function before calling this.
107-
THROW_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
93+
THROW_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
10894

10995
// Call registered handler from when we started listening.
110-
THROW_IF_FAILED(localPfnHandoff(in, out, signal, reference, server, client, startupInfo));
96+
THROW_IF_FAILED(_pfnHandoff(in, out, signal, reference, server, client, startupInfo));
11197

11298
#pragma warning(suppress : 26477)
11399
TraceLoggingWrite(

src/cascadia/TerminalConnection/CTerminalHandoff.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff))
3838

3939
#pragma endregion
4040

41-
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff);
42-
static HRESULT s_StopListening();
41+
static void s_setCallback(NewHandoffFunction callback) noexcept;
42+
static HRESULT s_StartListening();
4343

4444
private:
45-
static HRESULT s_StopListeningLocked();
45+
static HRESULT s_StopListening();
4646
};
4747

4848
// Disable warnings from the CoCreatableClass macro as the value it provides for

src/cascadia/TerminalConnection/ConptyConnection.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -772,12 +772,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
772772

773773
void ConptyConnection::StartInboundListener()
774774
{
775-
THROW_IF_FAILED(CTerminalHandoff::s_StartListening(&ConptyConnection::NewHandoff));
776-
}
775+
static const auto init = []() noexcept {
776+
CTerminalHandoff::s_setCallback(&ConptyConnection::NewHandoff);
777+
return true;
778+
}();
777779

778-
void ConptyConnection::StopInboundListener()
779-
{
780-
THROW_IF_FAILED(CTerminalHandoff::s_StopListening());
780+
CTerminalHandoff::s_StartListening();
781781
}
782782

783783
// Function Description:

src/cascadia/TerminalConnection/ConptyConnection.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
3535
WORD ShowWindow() const noexcept;
3636

3737
static void StartInboundListener();
38-
static void StopInboundListener();
3938

4039
static winrt::event_token NewConnection(const NewConnectionHandler& handler);
4140
static void NewConnection(const winrt::event_token& token);

src/cascadia/TerminalConnection/ConptyConnection.idl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ namespace Microsoft.Terminal.TerminalConnection
2222

2323
static event NewConnectionHandler NewConnection;
2424
static void StartInboundListener();
25-
static void StopInboundListener();
2625

2726
static Windows.Foundation.Collections.ValueSet CreateSettings(String cmdline,
2827
String startingDirectory,

0 commit comments

Comments
 (0)