mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[idd] recovery: unplug monitor without helper
When LGIddHelper is unavailable, recovery requests could remain pending until timeout while the virtual monitor stayed connected. This could leave a logged-out guest without a usable display. Fall back to departing the IDD monitor for active recovery, then re-arrive it before normal recovery completes. Serialize monitor lifecycle changes and preserve recovery ordering across Helper reconnects, timeouts, and stale responses.
This commit is contained in:
@@ -385,16 +385,42 @@ void CDeviceContext::FinishAdapterInit(UINT connectorIndex)
|
|||||||
DEBUG_INFO("Preferred render adapter set");
|
DEBUG_INFO("Preferred render adapter set");
|
||||||
}
|
}
|
||||||
|
|
||||||
FinishInit(connectorIndex);
|
bool monitorDisabled;
|
||||||
|
bool arrivalPending;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
m_adapterReady = true;
|
||||||
|
monitorDisabled = m_recoveryMonitorDisabled;
|
||||||
|
arrivalPending = m_localRecoveryArrival;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!monitorDisabled)
|
||||||
|
{
|
||||||
|
FinishInit(connectorIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrivalPending)
|
||||||
|
m_monitorManager.Enable();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Recovery can intentionally disable the monitor before the adapter's
|
||||||
|
// first arrival. This is still a valid synchronization boundary: allow a
|
||||||
|
// later NORMAL request to re-enable the monitor instead of waiting for an
|
||||||
|
// arrival that ACTIVE deliberately suppressed.
|
||||||
|
m_transport->SyncRecovery();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDeviceContext::FinishInit(UINT connectorIndex)
|
void CDeviceContext::FinishInit(UINT connectorIndex)
|
||||||
{
|
{
|
||||||
CDisplayConfiguration::Description description =
|
CDisplayConfiguration::Description description =
|
||||||
m_displayConfiguration.GetDescription();
|
m_displayConfiguration.GetDescription();
|
||||||
if (m_monitorManager.Create(
|
const bool arrived = m_monitorManager.Create(
|
||||||
connectorIndex, m_adapter, std::move(description.edid), this))
|
connectorIndex, m_adapter, std::move(description.edid), this);
|
||||||
|
if (arrived)
|
||||||
m_transport->SyncRecovery();
|
m_transport->SyncRecovery();
|
||||||
|
CompleteRecoveryArrival(arrived);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDeviceContext::ReplugMonitor()
|
void CDeviceContext::ReplugMonitor()
|
||||||
@@ -489,28 +515,80 @@ bool CDeviceContext::InitializeTransport()
|
|||||||
|
|
||||||
g_pipe.SetRecoveryHandler(
|
g_pipe.SetRecoveryHandler(
|
||||||
[](void * opaque, uint64_t route, uint64_t session,
|
[](void * opaque, uint64_t route, uint64_t session,
|
||||||
uint32_t serial, bool active, LGPipeMsg::Type result)
|
uint32_t serial, bool active, CPipeServer::RecoveryResult result)
|
||||||
{
|
{
|
||||||
CDeviceContext * context =
|
CDeviceContext * context =
|
||||||
static_cast<CDeviceContext *>(opaque);
|
static_cast<CDeviceContext *>(opaque);
|
||||||
|
|
||||||
|
RecoveryAction action;
|
||||||
|
action.route = route;
|
||||||
|
action.session = session;
|
||||||
|
action.serial = serial;
|
||||||
|
action.active = active;
|
||||||
|
|
||||||
|
if (result == CPipeServer::RecoveryResult::HELPER_UNAVAILABLE)
|
||||||
|
{
|
||||||
|
// Pipe recovery messages do not carry the local operation deadline.
|
||||||
|
// Recover it from the canonical action so a late monitor transition
|
||||||
|
// cannot complete an operation after the recovery hub timed it out.
|
||||||
|
{
|
||||||
|
CSRWSharedLock lock(context->m_localRecoveryLock);
|
||||||
|
if (context->m_latestRecoveryValid &&
|
||||||
|
context->SameRecoveryAction(action,
|
||||||
|
context->m_latestRecoveryAction))
|
||||||
|
action = context->m_latestRecoveryAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context->QueueLocalRecovery(action))
|
||||||
|
context->m_transport->RecoveryStatus(
|
||||||
|
route, session, serial, active,
|
||||||
|
ITransport::Recovery::FAILED, RPC_S_SERVER_UNAVAILABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> transitionLock(
|
||||||
|
context->m_recoveryTransitionMutex);
|
||||||
|
context->m_helperRecoveryAction = action;
|
||||||
|
context->m_helperRecoveryComplete = true;
|
||||||
|
context->RemoveLocalRecovery(action);
|
||||||
|
|
||||||
ITransport::Recovery state = ITransport::Recovery::FAILED;
|
ITransport::Recovery state = ITransport::Recovery::FAILED;
|
||||||
uint32_t error = ERROR_SUCCESS;
|
uint32_t error = ERROR_SUCCESS;
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
case LGPipeMsg::RECOVERY_OFF:
|
case CPipeServer::RecoveryResult::NORMAL:
|
||||||
state = ITransport::Recovery::NORMAL;
|
state = ITransport::Recovery::NORMAL;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(context->m_localRecoveryLock);
|
||||||
|
if (context->m_latestRecoveryValid &&
|
||||||
|
context->SameRecoveryAction(action,
|
||||||
|
context->m_latestRecoveryAction))
|
||||||
|
{
|
||||||
|
context->m_recoveryActive = false;
|
||||||
|
context->m_latestRecoveryAction.deadline = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LGPipeMsg::RECOVERY_ON:
|
case CPipeServer::RecoveryResult::ACTIVE:
|
||||||
state = ITransport::Recovery::ACTIVE;
|
state = ITransport::Recovery::ACTIVE;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(context->m_localRecoveryLock);
|
||||||
|
if (context->m_latestRecoveryValid &&
|
||||||
|
context->SameRecoveryAction(action,
|
||||||
|
context->m_latestRecoveryAction))
|
||||||
|
{
|
||||||
|
context->m_recoveryActive = true;
|
||||||
|
context->m_latestRecoveryAction.deadline = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LGPipeMsg::RECOVERY_FAILED:
|
case CPipeServer::RecoveryResult::FAILED:
|
||||||
error = ERROR_GEN_FAILURE;
|
error = ERROR_GEN_FAILURE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LGPipeMsg::RECOVERY_NO_DISPLAY:
|
case CPipeServer::RecoveryResult::NO_DISPLAY:
|
||||||
error = ERROR_NOT_FOUND;
|
error = ERROR_NOT_FOUND;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -588,6 +666,8 @@ bool CDeviceContext::SetupTransport(size_t alignSize)
|
|||||||
|
|
||||||
void CDeviceContext::TransportTimer()
|
void CDeviceContext::TransportTimer()
|
||||||
{
|
{
|
||||||
|
ProcessLocalRecovery();
|
||||||
|
|
||||||
// Monitor work is deferred off IddCx callback threads.
|
// Monitor work is deferred off IddCx callback threads.
|
||||||
switch (m_monitorManager.TakeDeferredAction())
|
switch (m_monitorManager.TakeDeferredAction())
|
||||||
{
|
{
|
||||||
@@ -623,6 +703,360 @@ InteractionResult CDeviceContext::OnSetResolution(const SourceKey& source,
|
|||||||
|
|
||||||
bool CDeviceContext::OnRecoveryAction(const RecoveryAction& action)
|
bool CDeviceContext::OnRecoveryAction(const RecoveryAction& action)
|
||||||
{
|
{
|
||||||
return g_pipe.SetRecovery(this, action.route, action.session,
|
bool recoveryActive;
|
||||||
action.serial, action.active);
|
bool monitorDisabled;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> transitionLock(m_recoveryTransitionMutex);
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
m_latestRecoveryAction = action;
|
||||||
|
m_latestRecoveryValid = true;
|
||||||
|
recoveryActive = m_recoveryActive;
|
||||||
|
monitorDisabled = m_recoveryMonitorDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The monitor must exist before Helper can restore the LG display path.
|
||||||
|
// Re-arrive it first when recovery previously used the IDD-only fallback.
|
||||||
|
if (!action.active && monitorDisabled)
|
||||||
|
return QueueLocalRecovery(action);
|
||||||
|
|
||||||
|
const CPipeServer::RecoveryDispatch dispatch = g_pipe.SetRecovery(
|
||||||
|
this, action.route, action.session, action.serial, action.active,
|
||||||
|
action.active || !recoveryActive);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> transitionLock(m_recoveryTransitionMutex);
|
||||||
|
if (m_helperRecoveryComplete &&
|
||||||
|
SameRecoveryAction(m_helperRecoveryAction, action))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
switch (dispatch)
|
||||||
|
{
|
||||||
|
case CPipeServer::RecoveryDispatch::SENT:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case CPipeServer::RecoveryDispatch::UNAVAILABLE:
|
||||||
|
case CPipeServer::RecoveryDispatch::QUEUED:
|
||||||
|
return QueueLocalRecovery(action);
|
||||||
|
|
||||||
|
case CPipeServer::RecoveryDispatch::REJECTED:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeviceContext::SameRecoveryAction(
|
||||||
|
const RecoveryAction& left, const RecoveryAction& right)
|
||||||
|
{
|
||||||
|
return left.route == right.route &&
|
||||||
|
left.session == right.session &&
|
||||||
|
left.serial == right.serial &&
|
||||||
|
left.active == right.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeviceContext::QueueLocalRecovery(const RecoveryAction& action)
|
||||||
|
{
|
||||||
|
if (!action.route || !action.session || !action.serial)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
if (m_latestRecoveryValid &&
|
||||||
|
!SameRecoveryAction(action, m_latestRecoveryAction))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (m_localRecoveryArrival)
|
||||||
|
{
|
||||||
|
const RecoveryAction& pending = m_localRecoveryArrivalAction;
|
||||||
|
if (SameRecoveryAction(pending, action))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const RecoveryAction& pending : m_localRecoveryQueue)
|
||||||
|
if (SameRecoveryAction(pending, action))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_localRecoveryQueue.push_back(action);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeviceContext::RemoveLocalRecovery(const RecoveryAction& action)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
for (auto it = m_localRecoveryQueue.begin();
|
||||||
|
it != m_localRecoveryQueue.end();)
|
||||||
|
{
|
||||||
|
if (SameRecoveryAction(*it, action))
|
||||||
|
it = m_localRecoveryQueue.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeviceContext::ProcessLocalRecovery()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> transitionLock(m_recoveryTransitionMutex);
|
||||||
|
|
||||||
|
RecoveryAction action;
|
||||||
|
bool haveAction = false;
|
||||||
|
bool reconcileActive = false;
|
||||||
|
bool reconcileAdapterReady = false;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (m_localRecoveryQueue.empty())
|
||||||
|
break;
|
||||||
|
action = m_localRecoveryQueue.front();
|
||||||
|
m_localRecoveryQueue.pop_front();
|
||||||
|
const bool current = !m_latestRecoveryValid ||
|
||||||
|
SameRecoveryAction(action, m_latestRecoveryAction);
|
||||||
|
const bool expired = action.deadline &&
|
||||||
|
GetTickCount64() >= action.deadline;
|
||||||
|
if (current && !expired)
|
||||||
|
{
|
||||||
|
haveAction = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The request can expire after Helper disappears but before this timer
|
||||||
|
// consumes the handoff. Its result is stale, but an already-established
|
||||||
|
// ACTIVE state still needs the IDD monitor physically disabled.
|
||||||
|
if (current && expired && m_recoveryActive &&
|
||||||
|
!m_recoveryMonitorDisabled)
|
||||||
|
{
|
||||||
|
m_recoveryMonitorDisabled = true;
|
||||||
|
reconcileActive = true;
|
||||||
|
reconcileAdapterReady = m_adapterReady;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reconcileActive)
|
||||||
|
{
|
||||||
|
DEBUG_WARN(
|
||||||
|
"IDD Helper is unavailable; reconciling the active recovery topology");
|
||||||
|
if (!m_monitorManager.Disable())
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
m_recoveryMonitorDisabled = false;
|
||||||
|
}
|
||||||
|
else if (reconcileAdapterReady)
|
||||||
|
m_transport->SyncRecovery();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!haveAction)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (action.active)
|
||||||
|
{
|
||||||
|
DEBUG_WARN(
|
||||||
|
"IDD Helper is unavailable; disabling the virtual monitor for recovery");
|
||||||
|
bool oldRecoveryActive;
|
||||||
|
bool oldMonitorDisabled;
|
||||||
|
bool adapterReady;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
oldRecoveryActive = m_recoveryActive;
|
||||||
|
oldMonitorDisabled = m_recoveryMonitorDisabled;
|
||||||
|
m_recoveryActive = true;
|
||||||
|
m_recoveryMonitorDisabled = true;
|
||||||
|
adapterReady = m_adapterReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool disabled = m_monitorManager.Disable();
|
||||||
|
if (!disabled)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
m_recoveryActive = oldRecoveryActive;
|
||||||
|
m_recoveryMonitorDisabled = oldMonitorDisabled;
|
||||||
|
}
|
||||||
|
if (disabled && adapterReady)
|
||||||
|
m_transport->SyncRecovery();
|
||||||
|
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||||
|
true, disabled ? ITransport::Recovery::ACTIVE :
|
||||||
|
ITransport::Recovery::FAILED,
|
||||||
|
disabled ? ERROR_SUCCESS : ERROR_GEN_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool disable = false;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
disable = m_recoveryActive && !m_recoveryMonitorDisabled;
|
||||||
|
if (disable)
|
||||||
|
m_recoveryMonitorDisabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disable)
|
||||||
|
{
|
||||||
|
DEBUG_WARN(
|
||||||
|
"IDD Helper disconnected during recovery; cycling the virtual monitor");
|
||||||
|
if (!m_monitorManager.Disable())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
m_recoveryMonitorDisabled = false;
|
||||||
|
}
|
||||||
|
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||||
|
false, ITransport::Recovery::FAILED, ERROR_GEN_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enable = false;
|
||||||
|
bool waitArrival = false;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
if (m_recoveryMonitorDisabled)
|
||||||
|
{
|
||||||
|
m_localRecoveryArrivalAction = action;
|
||||||
|
m_localRecoveryArrival = true;
|
||||||
|
enable = m_adapterReady;
|
||||||
|
waitArrival = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_recoveryActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
m_monitorManager.Enable();
|
||||||
|
|
||||||
|
if (waitArrival)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The normal monitor is already present. With no Helper there is no
|
||||||
|
// user-session topology work to perform, so monitor presence is the local
|
||||||
|
// completion boundary.
|
||||||
|
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||||
|
false, ITransport::Recovery::NORMAL, ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeviceContext::CompleteRecoveryArrival(bool arrived)
|
||||||
|
{
|
||||||
|
RecoveryAction action;
|
||||||
|
RecoveryAction latest;
|
||||||
|
bool stale = false;
|
||||||
|
bool latestValid = false;
|
||||||
|
bool latestHandled = false;
|
||||||
|
bool latestCurrent = false;
|
||||||
|
std::unique_lock<std::mutex> transitionLock(m_recoveryTransitionMutex);
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
if (!m_localRecoveryArrival)
|
||||||
|
return;
|
||||||
|
|
||||||
|
action = m_localRecoveryArrivalAction;
|
||||||
|
const uint64_t now = GetTickCount64();
|
||||||
|
const bool expired = action.deadline && now >= action.deadline;
|
||||||
|
const bool superseded = m_latestRecoveryValid &&
|
||||||
|
!SameRecoveryAction(action, m_latestRecoveryAction);
|
||||||
|
stale = expired || superseded;
|
||||||
|
if (stale)
|
||||||
|
{
|
||||||
|
latestValid = m_latestRecoveryValid;
|
||||||
|
latest = m_latestRecoveryAction;
|
||||||
|
latestHandled = m_helperRecoveryComplete &&
|
||||||
|
SameRecoveryAction(m_helperRecoveryAction, latest);
|
||||||
|
latestCurrent = latestValid &&
|
||||||
|
(!latest.deadline || now < latest.deadline);
|
||||||
|
if (superseded && arrived && latestCurrent && !latest.active)
|
||||||
|
{
|
||||||
|
action = latest;
|
||||||
|
m_localRecoveryArrivalAction = latest;
|
||||||
|
stale = false;
|
||||||
|
for (auto it = m_localRecoveryQueue.begin();
|
||||||
|
it != m_localRecoveryQueue.end();)
|
||||||
|
{
|
||||||
|
if (SameRecoveryAction(*it, latest))
|
||||||
|
it = m_localRecoveryQueue.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_localRecoveryArrivalAction = {};
|
||||||
|
m_localRecoveryArrival = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrived && !stale)
|
||||||
|
{
|
||||||
|
// Arrival is the IDD-only NORMAL completion boundary. Keep the monitor
|
||||||
|
// present if Helper disappears while applying its user-session topology.
|
||||||
|
m_localRecoveryArrivalAction = {};
|
||||||
|
m_localRecoveryArrival = false;
|
||||||
|
m_recoveryActive = false;
|
||||||
|
m_recoveryMonitorDisabled = false;
|
||||||
|
}
|
||||||
|
else if (!stale)
|
||||||
|
{
|
||||||
|
m_localRecoveryArrivalAction = {};
|
||||||
|
m_localRecoveryArrival = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stale)
|
||||||
|
{
|
||||||
|
// The expired/superseded NORMAL operation must not leave the IDD monitor
|
||||||
|
// arrived while CPipe still retains the preceding ACTIVE topology. Return
|
||||||
|
// to that stable local state before allowing newer work to proceed.
|
||||||
|
const bool disabled = m_monitorManager.Disable();
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
m_recoveryActive = true;
|
||||||
|
m_recoveryMonitorDisabled = disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool queueLatest = latestValid && latest.active &&
|
||||||
|
latestCurrent && !latestHandled;
|
||||||
|
transitionLock.unlock();
|
||||||
|
if (queueLatest)
|
||||||
|
QueueLocalRecovery(latest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arrived)
|
||||||
|
{
|
||||||
|
const bool disabled = m_monitorManager.Disable();
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
if (disabled)
|
||||||
|
{
|
||||||
|
m_recoveryActive = true;
|
||||||
|
m_recoveryMonitorDisabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transitionLock.unlock();
|
||||||
|
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||||
|
false, ITransport::Recovery::FAILED, ERROR_GEN_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transitionLock.unlock();
|
||||||
|
|
||||||
|
// If Helper appeared while the monitor was disabled, let it restore the
|
||||||
|
// user-session topology now that the LG display path exists again.
|
||||||
|
const CPipeServer::RecoveryDispatch dispatch = g_pipe.SetRecovery(
|
||||||
|
this, action.route, action.session, action.serial, false, true);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> completionLock(m_recoveryTransitionMutex);
|
||||||
|
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||||
|
if (m_helperRecoveryComplete &&
|
||||||
|
SameRecoveryAction(m_helperRecoveryAction, action))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dispatch == CPipeServer::RecoveryDispatch::SENT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||||
|
false,
|
||||||
|
(dispatch == CPipeServer::RecoveryDispatch::QUEUED ||
|
||||||
|
dispatch == CPipeServer::RecoveryDispatch::UNAVAILABLE) ?
|
||||||
|
ITransport::Recovery::NORMAL : ITransport::Recovery::FAILED,
|
||||||
|
(dispatch == CPipeServer::RecoveryDispatch::QUEUED ||
|
||||||
|
dispatch == CPipeServer::RecoveryDispatch::UNAVAILABLE) ?
|
||||||
|
ERROR_SUCCESS : RPC_S_SERVER_UNAVAILABLE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,9 @@
|
|||||||
#include <wdf.h>
|
#include <wdf.h>
|
||||||
#include <IddCx.h>
|
#include <IddCx.h>
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -55,6 +57,19 @@ private:
|
|||||||
WDFTIMER m_transportTimer = nullptr;
|
WDFTIMER m_transportTimer = nullptr;
|
||||||
bool m_recoveryHandlerSet = false;
|
bool m_recoveryHandlerSet = false;
|
||||||
|
|
||||||
|
std::mutex m_recoveryTransitionMutex;
|
||||||
|
CSRWLock m_localRecoveryLock;
|
||||||
|
std::deque<RecoveryAction> m_localRecoveryQueue;
|
||||||
|
RecoveryAction m_localRecoveryArrivalAction = {};
|
||||||
|
RecoveryAction m_latestRecoveryAction = {};
|
||||||
|
RecoveryAction m_helperRecoveryAction = {};
|
||||||
|
bool m_localRecoveryArrival = false;
|
||||||
|
bool m_latestRecoveryValid = false;
|
||||||
|
bool m_helperRecoveryComplete = false;
|
||||||
|
bool m_recoveryActive = false;
|
||||||
|
bool m_recoveryMonitorDisabled = false;
|
||||||
|
bool m_adapterReady = false;
|
||||||
|
|
||||||
UINT m_iddCxVersion = 0;
|
UINT m_iddCxVersion = 0;
|
||||||
bool m_hasIddCx110DDIs = false;
|
bool m_hasIddCx110DDIs = false;
|
||||||
bool m_canProcessFP16 = false;
|
bool m_canProcessFP16 = false;
|
||||||
@@ -67,6 +82,12 @@ private:
|
|||||||
|
|
||||||
bool InitializeTransport();
|
bool InitializeTransport();
|
||||||
void TransportTimer();
|
void TransportTimer();
|
||||||
|
static bool SameRecoveryAction(
|
||||||
|
const RecoveryAction& left, const RecoveryAction& right);
|
||||||
|
bool QueueLocalRecovery(const RecoveryAction& action);
|
||||||
|
void RemoveLocalRecovery(const RecoveryAction& action);
|
||||||
|
void ProcessLocalRecovery();
|
||||||
|
void CompleteRecoveryArrival(bool arrived);
|
||||||
InteractionResult OnSetCursorPos(
|
InteractionResult OnSetCursorPos(
|
||||||
const SourceKey& source, int32_t x, int32_t y) override;
|
const SourceKey& source, int32_t x, int32_t y) override;
|
||||||
InteractionResult OnSetResolution(const SourceKey& source,
|
InteractionResult OnSetResolution(const SourceKey& source,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
||||||
std::vector<BYTE> edid, CDeviceContext * owner)
|
std::vector<BYTE> edid, CDeviceContext * owner)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lifecycleLock(m_lifecycleMutex);
|
||||||
DEBUG_INFO("Creating monitor on connector %u", connectorIndex);
|
DEBUG_INFO("Creating monitor on connector %u", connectorIndex);
|
||||||
|
|
||||||
// We support a single monitor; never create a second one if one already
|
// We support a single monitor; never create a second one if one already
|
||||||
@@ -34,6 +35,8 @@ bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
|||||||
bool haveMonitor;
|
bool haveMonitor;
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(m_lock);
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (!m_enabled)
|
||||||
|
return false;
|
||||||
haveMonitor = m_monitor != WDF_NO_HANDLE;
|
haveMonitor = m_monitor != WDF_NO_HANDLE;
|
||||||
}
|
}
|
||||||
if (haveMonitor)
|
if (haveMonitor)
|
||||||
@@ -79,18 +82,27 @@ bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
|||||||
|
|
||||||
DEBUG_INFO("Monitor object created (%p)", createOut.MonitorObject);
|
DEBUG_INFO("Monitor object created (%p)", createOut.MonitorObject);
|
||||||
|
|
||||||
|
const IDDCX_MONITOR monitor = createOut.MonitorObject;
|
||||||
|
|
||||||
|
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(monitor);
|
||||||
|
wrapper->context = new CMonitorContext(monitor, owner);
|
||||||
|
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(m_lock);
|
CSRWExclusiveLock lock(m_lock);
|
||||||
m_monitor = createOut.MonitorObject;
|
m_monitor = monitor;
|
||||||
|
m_monitorDeparted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(m_monitor);
|
|
||||||
wrapper->context = new CMonitorContext(m_monitor, owner);
|
|
||||||
|
|
||||||
IDARG_OUT_MONITORARRIVAL out = {};
|
IDARG_OUT_MONITORARRIVAL out = {};
|
||||||
status = IddCxMonitorArrival(m_monitor, &out);
|
status = IddCxMonitorArrival(monitor, &out);
|
||||||
if (FAILED(status))
|
if (FAILED(status))
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (m_monitor == monitor)
|
||||||
|
m_monitor = nullptr;
|
||||||
|
}
|
||||||
|
WdfObjectDelete((WDFOBJECT)monitor);
|
||||||
DEBUG_ERROR_HR(status, "IddCxMonitorArrival Failed");
|
DEBUG_ERROR_HR(status, "IddCxMonitorArrival Failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -99,12 +111,100 @@ bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CMonitorManager::Disable()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lifecycleLock(m_lifecycleMutex);
|
||||||
|
IDDCX_MONITOR monitor = nullptr;
|
||||||
|
bool oldReplugMonitor = false;
|
||||||
|
bool oldReplugPending = false;
|
||||||
|
bool oldMonitorDeparted = false;
|
||||||
|
bool oldWaitForSwapChainRelease = false;
|
||||||
|
LONG oldCreateQueued = 0;
|
||||||
|
LONG oldReplugQueued = 0;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (!m_enabled)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
oldReplugMonitor = m_replugMonitor;
|
||||||
|
oldReplugPending = m_replugPending;
|
||||||
|
oldMonitorDeparted = m_monitorDeparted;
|
||||||
|
oldWaitForSwapChainRelease = m_waitForSwapChainRelease;
|
||||||
|
oldCreateQueued = Atomic::Load(m_createQueued);
|
||||||
|
oldReplugQueued = Atomic::Load(m_replugQueued);
|
||||||
|
|
||||||
|
m_enabled = false;
|
||||||
|
m_replugMonitor = false;
|
||||||
|
m_replugPending = false;
|
||||||
|
Atomic::Store(m_createQueued, 0);
|
||||||
|
Atomic::Store(m_replugQueued, 0);
|
||||||
|
|
||||||
|
monitor = m_monitor;
|
||||||
|
if (monitor == WDF_NO_HANDLE)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_monitor = nullptr;
|
||||||
|
m_monitorDeparted = false;
|
||||||
|
m_waitForSwapChainRelease = m_swapChainAssigned;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_INFO("Disabling the virtual monitor for recovery");
|
||||||
|
const NTSTATUS status = IddCxMonitorDeparture(monitor);
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
m_enabled = true;
|
||||||
|
m_monitor = monitor;
|
||||||
|
m_replugMonitor = oldReplugMonitor;
|
||||||
|
m_replugPending = oldReplugPending;
|
||||||
|
m_monitorDeparted = oldMonitorDeparted;
|
||||||
|
m_waitForSwapChainRelease = oldWaitForSwapChainRelease;
|
||||||
|
Atomic::Store(m_createQueued, oldCreateQueued);
|
||||||
|
Atomic::Store(m_replugQueued, oldReplugQueued);
|
||||||
|
DEBUG_ERROR_HR(status,
|
||||||
|
"Failed to disable the virtual monitor for recovery");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
m_monitorDeparted = true;
|
||||||
|
}
|
||||||
|
DEBUG_INFO("Virtual monitor disabled for recovery");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMonitorManager::Enable()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lifecycleLock(m_lifecycleMutex);
|
||||||
|
bool create = false;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (m_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_enabled = true;
|
||||||
|
create = m_monitor == WDF_NO_HANDLE &&
|
||||||
|
!m_waitForSwapChainRelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (create)
|
||||||
|
Atomic::Store(m_createQueued, 1);
|
||||||
|
}
|
||||||
|
|
||||||
CMonitorManager::ReplugAction CMonitorManager::Replug()
|
CMonitorManager::ReplugAction CMonitorManager::Replug()
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lifecycleLock(m_lifecycleMutex);
|
||||||
IDDCX_MONITOR monitor;
|
IDDCX_MONITOR monitor;
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(m_lock);
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
|
||||||
|
if (!m_enabled)
|
||||||
|
return ReplugAction::NONE;
|
||||||
|
|
||||||
|
if (m_waitForSwapChainRelease)
|
||||||
|
return ReplugAction::NONE;
|
||||||
|
|
||||||
if (m_replugMonitor || (m_swapChainAssigned && !m_swapChainReady))
|
if (m_replugMonitor || (m_swapChainAssigned && !m_swapChainReady))
|
||||||
{
|
{
|
||||||
// Coalesce changes received while a swap chain is being initialized,
|
// Coalesce changes received while a swap chain is being initialized,
|
||||||
@@ -198,10 +298,11 @@ void CMonitorManager::OnSwapChainReleased()
|
|||||||
CSRWExclusiveLock lock(m_lock);
|
CSRWExclusiveLock lock(m_lock);
|
||||||
m_swapChainAssigned = false;
|
m_swapChainAssigned = false;
|
||||||
m_swapChainReady = false;
|
m_swapChainReady = false;
|
||||||
if (m_replugMonitor && m_waitForSwapChainRelease)
|
if (m_waitForSwapChainRelease)
|
||||||
{
|
{
|
||||||
m_waitForSwapChainRelease = false;
|
m_waitForSwapChainRelease = false;
|
||||||
rebuild = m_monitorDeparted;
|
rebuild = m_enabled && m_monitorDeparted &&
|
||||||
|
m_monitor == WDF_NO_HANDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +318,9 @@ CMonitorManager::ReadyAction CMonitorManager::OnSwapChainReady()
|
|||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(m_lock);
|
CSRWExclusiveLock lock(m_lock);
|
||||||
m_swapChainReady = true;
|
m_swapChainReady = true;
|
||||||
|
if (!m_enabled || m_waitForSwapChainRelease)
|
||||||
|
return action;
|
||||||
|
|
||||||
if (m_replugMonitor)
|
if (m_replugMonitor)
|
||||||
{
|
{
|
||||||
m_replugMonitor = false;
|
m_replugMonitor = false;
|
||||||
@@ -250,16 +354,27 @@ CMonitorManager::ReadyAction CMonitorManager::OnSwapChainReady()
|
|||||||
|
|
||||||
void CMonitorManager::QueueReplug()
|
void CMonitorManager::QueueReplug()
|
||||||
{
|
{
|
||||||
|
CSRWSharedLock lock(m_lock);
|
||||||
|
if (!m_enabled)
|
||||||
|
return;
|
||||||
Atomic::Store(m_replugQueued, 1);
|
Atomic::Store(m_replugQueued, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CMonitorManager::DeferredAction CMonitorManager::TakeDeferredAction()
|
CMonitorManager::DeferredAction CMonitorManager::TakeDeferredAction()
|
||||||
{
|
{
|
||||||
if (Atomic::Swap(m_createQueued, 0))
|
if (Atomic::Swap(m_createQueued, 0))
|
||||||
return DeferredAction::CREATE;
|
{
|
||||||
|
CSRWSharedLock lock(m_lock);
|
||||||
|
if (m_enabled)
|
||||||
|
return DeferredAction::CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
if (Atomic::Swap(m_replugQueued, 0))
|
if (Atomic::Swap(m_replugQueued, 0))
|
||||||
return DeferredAction::REPLUG;
|
{
|
||||||
|
CSRWSharedLock lock(m_lock);
|
||||||
|
if (m_enabled)
|
||||||
|
return DeferredAction::REPLUG;
|
||||||
|
}
|
||||||
|
|
||||||
return DeferredAction::NONE;
|
return DeferredAction::NONE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <wdf.h>
|
#include <wdf.h>
|
||||||
#include <IddCx.h>
|
#include <IddCx.h>
|
||||||
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "config/CSettings.h"
|
#include "config/CSettings.h"
|
||||||
@@ -61,9 +62,14 @@ private:
|
|||||||
// IddCx callback threads, the swap-chain thread, and the transport timer.
|
// IddCx callback threads, the swap-chain thread, and the transport timer.
|
||||||
CSRWLock m_lock;
|
CSRWLock m_lock;
|
||||||
|
|
||||||
|
// Serializes monitor arrival/departure calls. IddCx can finish adapter
|
||||||
|
// initialization on a different thread from transport recovery work.
|
||||||
|
std::mutex m_lifecycleMutex;
|
||||||
|
|
||||||
bool m_replugMonitor = false;
|
bool m_replugMonitor = false;
|
||||||
bool m_replugPending = false;
|
bool m_replugPending = false;
|
||||||
bool m_monitorDeparted = false;
|
bool m_monitorDeparted = false;
|
||||||
|
bool m_enabled = true;
|
||||||
bool m_swapChainAssigned = false;
|
bool m_swapChainAssigned = false;
|
||||||
bool m_swapChainReady = false;
|
bool m_swapChainReady = false;
|
||||||
bool m_waitForSwapChainRelease = false;
|
bool m_waitForSwapChainRelease = false;
|
||||||
@@ -77,6 +83,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
bool Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
bool Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
||||||
std::vector<BYTE> edid, CDeviceContext * owner);
|
std::vector<BYTE> edid, CDeviceContext * owner);
|
||||||
|
bool Disable();
|
||||||
|
void Enable();
|
||||||
ReplugAction Replug();
|
ReplugAction Replug();
|
||||||
void RequestMode(const CSettings::DisplayMode& mode);
|
void RequestMode(const CSettings::DisplayMode& mode);
|
||||||
|
|
||||||
|
|||||||
@@ -369,7 +369,8 @@ void CPipeServer::OnPipeConnected()
|
|||||||
// latest request whenever the helper reconnects so a helper restart cannot
|
// latest request whenever the helper reconnects so a helper restart cannot
|
||||||
// silently restore the IDD-only topology while recovery is active.
|
// silently restore the IDD-only topology while recovery is active.
|
||||||
if (m_recoveryValid)
|
if (m_recoveryValid)
|
||||||
m_endpoint.Send(&m_recoveryRequest, sizeof(m_recoveryRequest));
|
m_recoveryPending = m_endpoint.Send(
|
||||||
|
&m_recoveryRequest, sizeof(m_recoveryRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPipeServer::OnPipeDisconnected()
|
void CPipeServer::OnPipeDisconnected()
|
||||||
@@ -384,6 +385,37 @@ void CPipeServer::OnPipeDisconnected()
|
|||||||
m_clientAuthorityId[0] = 0;
|
m_clientAuthorityId[0] = 0;
|
||||||
m_clientAuthorityId[1] = 0;
|
m_clientAuthorityId[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSRWExclusiveLock queueLock(m_queueLock);
|
||||||
|
CSRWSharedLock recoveryLock(m_recoveryLock);
|
||||||
|
if (m_recoveryValid &&
|
||||||
|
(m_recoveryPending || m_recoveryHelperActive) &&
|
||||||
|
m_recoveryHandler)
|
||||||
|
{
|
||||||
|
const uint64_t route = m_recoveryRoute;
|
||||||
|
const uint64_t session = m_recoveryRequest.recovery.session;
|
||||||
|
const uint32_t serial = m_recoveryRequest.recovery.request &
|
||||||
|
~LGPipeMsg::RECOVERY_ACTIVE;
|
||||||
|
const bool active = (m_recoveryRequest.recovery.request &
|
||||||
|
LGPipeMsg::RECOVERY_ACTIVE) != 0;
|
||||||
|
|
||||||
|
// If NORMAL was sent while Helper still owned the recovery topology,
|
||||||
|
// do not replay it after a disconnect until the IDD monitor is present.
|
||||||
|
// Preserve the preceding ACTIVE command for reconnect in the meantime.
|
||||||
|
if (!active && !m_recoveryReplaySafe && m_recoveryActiveValid)
|
||||||
|
{
|
||||||
|
m_recoveryValid = true;
|
||||||
|
m_recoveryRoute = m_recoveryActiveRoute;
|
||||||
|
m_recoveryRequest = m_recoveryActiveRequest;
|
||||||
|
m_recoveryReplaySafe = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_recoveryPending = false;
|
||||||
|
m_recoveryHelperActive = false;
|
||||||
|
queueLock.Unlock();
|
||||||
|
m_recoveryHandler(m_recoveryOpaque, route, session, serial, active,
|
||||||
|
RecoveryResult::HELPER_UNAVAILABLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPipeServer::OnPipeMessage(const void * message, size_t size)
|
bool CPipeServer::OnPipeMessage(const void * message, size_t size)
|
||||||
@@ -494,7 +526,7 @@ void CPipeServer::HandleReloadSettings()
|
|||||||
|
|
||||||
void CPipeServer::HandleRecovery(const LGPipeMsg & msg)
|
void CPipeServer::HandleRecovery(const LGPipeMsg & msg)
|
||||||
{
|
{
|
||||||
CSRWSharedLock queueLock(m_queueLock);
|
CSRWExclusiveLock queueLock(m_queueLock);
|
||||||
if (!m_recoveryValid ||
|
if (!m_recoveryValid ||
|
||||||
msg.recovery.session != m_recoveryRequest.recovery.session ||
|
msg.recovery.session != m_recoveryRequest.recovery.session ||
|
||||||
msg.recovery.request != m_recoveryRequest.recovery.request)
|
msg.recovery.request != m_recoveryRequest.recovery.request)
|
||||||
@@ -505,14 +537,44 @@ void CPipeServer::HandleRecovery(const LGPipeMsg & msg)
|
|||||||
|
|
||||||
const uint32_t serial =
|
const uint32_t serial =
|
||||||
msg.recovery.request & ~LGPipeMsg::RECOVERY_ACTIVE;
|
msg.recovery.request & ~LGPipeMsg::RECOVERY_ACTIVE;
|
||||||
const bool active =
|
const bool active =
|
||||||
(msg.recovery.request & LGPipeMsg::RECOVERY_ACTIVE) != 0;
|
(msg.recovery.request & LGPipeMsg::RECOVERY_ACTIVE) != 0;
|
||||||
|
const uint64_t route = m_recoveryRoute;
|
||||||
|
RecoveryResult result;
|
||||||
|
switch (msg.type)
|
||||||
|
{
|
||||||
|
case LGPipeMsg::RECOVERY_OFF:
|
||||||
|
result = RecoveryResult::NORMAL;
|
||||||
|
m_recoveryHelperActive = false;
|
||||||
|
m_recoveryReplaySafe = true;
|
||||||
|
m_recoveryActiveValid = false;
|
||||||
|
m_recoveryActiveRoute = 0;
|
||||||
|
m_recoveryActiveRequest = {};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LGPipeMsg::RECOVERY_ON:
|
||||||
|
result = RecoveryResult::ACTIVE;
|
||||||
|
m_recoveryHelperActive = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LGPipeMsg::RECOVERY_FAILED:
|
||||||
|
result = RecoveryResult::FAILED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LGPipeMsg::RECOVERY_NO_DISPLAY:
|
||||||
|
result = RecoveryResult::NO_DISPLAY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_recoveryPending = false;
|
||||||
|
|
||||||
CSRWSharedLock recoveryLock(m_recoveryLock);
|
CSRWSharedLock recoveryLock(m_recoveryLock);
|
||||||
queueLock.Unlock();
|
queueLock.Unlock();
|
||||||
if (m_recoveryHandler)
|
if (m_recoveryHandler)
|
||||||
m_recoveryHandler(m_recoveryOpaque,
|
m_recoveryHandler(m_recoveryOpaque,
|
||||||
m_recoveryRoute, msg.recovery.session, serial, active, msg.type);
|
route, msg.recovery.session, serial, active, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPipeServer::SetDeviceContext(CDeviceContext * context)
|
void CPipeServer::SetDeviceContext(CDeviceContext * context)
|
||||||
@@ -526,11 +588,19 @@ void CPipeServer::SetRecoveryHandler(
|
|||||||
{
|
{
|
||||||
CSRWExclusiveLock queueLock(m_queueLock);
|
CSRWExclusiveLock queueLock(m_queueLock);
|
||||||
CSRWExclusiveLock recoveryLock(m_recoveryLock);
|
CSRWExclusiveLock recoveryLock(m_recoveryLock);
|
||||||
m_recoveryRoute = 0;
|
m_recoveryRoute = 0;
|
||||||
m_recoveryValid = false;
|
m_recoveryValid = false;
|
||||||
m_recoveryRequest = {};
|
m_recoveryPending = false;
|
||||||
m_recoveryHandler = handler;
|
m_recoveryHelperActive = false;
|
||||||
m_recoveryOpaque = opaque;
|
m_recoveryReplaySafe = true;
|
||||||
|
m_recoveryActiveValid = false;
|
||||||
|
m_recoveryRequest = {};
|
||||||
|
m_recoveryActiveRequest = {};
|
||||||
|
m_recoveryNewestRequest = {};
|
||||||
|
m_recoveryActiveRoute = 0;
|
||||||
|
m_recoveryNewestRoute = 0;
|
||||||
|
m_recoveryHandler = handler;
|
||||||
|
m_recoveryOpaque = opaque;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPipeServer::ClearRecoveryHandler(void * opaque)
|
void CPipeServer::ClearRecoveryHandler(void * opaque)
|
||||||
@@ -540,11 +610,19 @@ void CPipeServer::ClearRecoveryHandler(void * opaque)
|
|||||||
if (m_recoveryOpaque != opaque)
|
if (m_recoveryOpaque != opaque)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_recoveryRoute = 0;
|
m_recoveryRoute = 0;
|
||||||
m_recoveryValid = false;
|
m_recoveryValid = false;
|
||||||
m_recoveryRequest = {};
|
m_recoveryPending = false;
|
||||||
m_recoveryHandler = nullptr;
|
m_recoveryHelperActive = false;
|
||||||
m_recoveryOpaque = nullptr;
|
m_recoveryReplaySafe = true;
|
||||||
|
m_recoveryActiveValid = false;
|
||||||
|
m_recoveryRequest = {};
|
||||||
|
m_recoveryActiveRequest = {};
|
||||||
|
m_recoveryNewestRequest = {};
|
||||||
|
m_recoveryActiveRoute = 0;
|
||||||
|
m_recoveryNewestRoute = 0;
|
||||||
|
m_recoveryHandler = nullptr;
|
||||||
|
m_recoveryOpaque = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPipeServer::SetCursorPos(int32_t x, int32_t y)
|
bool CPipeServer::SetCursorPos(int32_t x, int32_t y)
|
||||||
@@ -596,14 +674,16 @@ void CPipeServer::ResolutionRejected(uint32_t width, uint32_t height,
|
|||||||
WriteMsg(msg);
|
WriteMsg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPipeServer::SetRecovery(void * owner, uint64_t route,
|
CPipeServer::RecoveryDispatch CPipeServer::SetRecovery(
|
||||||
uint64_t session, uint32_t serial, bool active)
|
void * owner, uint64_t route,
|
||||||
|
uint64_t session, uint32_t serial, bool active,
|
||||||
|
bool replayIfUnavailable)
|
||||||
{
|
{
|
||||||
if (!route || !session || !serial ||
|
if (!route || !session || !serial ||
|
||||||
(serial & LGPipeMsg::RECOVERY_ACTIVE))
|
(serial & LGPipeMsg::RECOVERY_ACTIVE))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Invalid recovery request correlation");
|
DEBUG_ERROR("Invalid recovery request correlation");
|
||||||
return false;
|
return RecoveryDispatch::REJECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
LGPipeMsg msg = {};
|
LGPipeMsg msg = {};
|
||||||
@@ -616,11 +696,57 @@ bool CPipeServer::SetRecovery(void * owner, uint64_t route,
|
|||||||
CSRWExclusiveLock queueLock(m_queueLock);
|
CSRWExclusiveLock queueLock(m_queueLock);
|
||||||
CSRWExclusiveLock recoveryLock(m_recoveryLock);
|
CSRWExclusiveLock recoveryLock(m_recoveryLock);
|
||||||
if (!m_recoveryHandler || m_recoveryOpaque != owner)
|
if (!m_recoveryHandler || m_recoveryOpaque != owner)
|
||||||
return false;
|
return RecoveryDispatch::REJECTED;
|
||||||
|
|
||||||
m_recoveryValid = true;
|
// RecoveryHub routes increase for the lifetime of this handler. Keep a
|
||||||
m_recoveryRoute = route;
|
// watermark separate from the replay request so restoring retained ACTIVE
|
||||||
m_recoveryRequest = msg;
|
// state cannot let a late, older NORMAL overwrite newer work.
|
||||||
m_endpoint.Send(&msg, sizeof(msg));
|
if (m_recoveryNewestRoute)
|
||||||
return true;
|
{
|
||||||
|
if (route < m_recoveryNewestRoute)
|
||||||
|
return RecoveryDispatch::REJECTED;
|
||||||
|
if (route == m_recoveryNewestRoute &&
|
||||||
|
(msg.recovery.session !=
|
||||||
|
m_recoveryNewestRequest.recovery.session ||
|
||||||
|
msg.recovery.request !=
|
||||||
|
m_recoveryNewestRequest.recovery.request))
|
||||||
|
return RecoveryDispatch::REJECTED;
|
||||||
|
}
|
||||||
|
if (route > m_recoveryNewestRoute)
|
||||||
|
{
|
||||||
|
m_recoveryNewestRoute = route;
|
||||||
|
m_recoveryNewestRequest = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool previousValid = m_recoveryValid;
|
||||||
|
const bool previousPending = m_recoveryPending;
|
||||||
|
const bool previousReplaySafe = m_recoveryReplaySafe;
|
||||||
|
const uint64_t previousRoute = m_recoveryRoute;
|
||||||
|
const LGPipeMsg previousRequest = m_recoveryRequest;
|
||||||
|
|
||||||
|
m_recoveryValid = true;
|
||||||
|
m_recoveryRoute = route;
|
||||||
|
m_recoveryRequest = msg;
|
||||||
|
m_recoveryReplaySafe = active || replayIfUnavailable;
|
||||||
|
if (active)
|
||||||
|
{
|
||||||
|
m_recoveryActiveValid = true;
|
||||||
|
m_recoveryActiveRoute = route;
|
||||||
|
m_recoveryActiveRequest = msg;
|
||||||
|
}
|
||||||
|
m_recoveryPending = m_endpoint.Send(&msg, sizeof(msg));
|
||||||
|
if (m_recoveryPending)
|
||||||
|
return RecoveryDispatch::SENT;
|
||||||
|
|
||||||
|
if (replayIfUnavailable)
|
||||||
|
return RecoveryDispatch::QUEUED;
|
||||||
|
|
||||||
|
// A NORMAL request cannot be replayed while the IDD monitor is locally
|
||||||
|
// absent. Retain the prior ACTIVE request until the monitor has re-arrived.
|
||||||
|
m_recoveryValid = previousValid;
|
||||||
|
m_recoveryPending = previousPending;
|
||||||
|
m_recoveryReplaySafe = previousReplaySafe;
|
||||||
|
m_recoveryRoute = previousRoute;
|
||||||
|
m_recoveryRequest = previousRequest;
|
||||||
|
return RecoveryDispatch::UNAVAILABLE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,17 +44,42 @@ class CPipeServer : private IPipeEndpointHandler,
|
|||||||
public IClipboardChannelDoorbell
|
public IClipboardChannelDoorbell
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class RecoveryResult
|
||||||
|
{
|
||||||
|
NORMAL,
|
||||||
|
ACTIVE,
|
||||||
|
FAILED,
|
||||||
|
NO_DISPLAY,
|
||||||
|
HELPER_UNAVAILABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class RecoveryDispatch
|
||||||
|
{
|
||||||
|
REJECTED,
|
||||||
|
UNAVAILABLE,
|
||||||
|
QUEUED,
|
||||||
|
SENT,
|
||||||
|
};
|
||||||
|
|
||||||
using RecoveryHandler = void (*)(void * opaque,
|
using RecoveryHandler = void (*)(void * opaque,
|
||||||
uint64_t route, uint64_t session, uint32_t serial, bool active,
|
uint64_t route, uint64_t session, uint32_t serial, bool active,
|
||||||
LGPipeMsg::Type result);
|
RecoveryResult result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CPipeEndpoint m_endpoint;
|
CPipeEndpoint m_endpoint;
|
||||||
CClipboardChannel m_clipboard;
|
CClipboardChannel m_clipboard;
|
||||||
CSRWLock m_queueLock;
|
CSRWLock m_queueLock;
|
||||||
std::vector<LGPipeMsg> m_queue;
|
std::vector<LGPipeMsg> m_queue;
|
||||||
bool m_recoveryValid = false;
|
bool m_recoveryValid = false;
|
||||||
LGPipeMsg m_recoveryRequest = {};
|
bool m_recoveryPending = false;
|
||||||
|
bool m_recoveryHelperActive = false;
|
||||||
|
bool m_recoveryReplaySafe = true;
|
||||||
|
bool m_recoveryActiveValid = false;
|
||||||
|
LGPipeMsg m_recoveryRequest = {};
|
||||||
|
LGPipeMsg m_recoveryActiveRequest = {};
|
||||||
|
LGPipeMsg m_recoveryNewestRequest = {};
|
||||||
|
uint64_t m_recoveryActiveRoute = 0;
|
||||||
|
uint64_t m_recoveryNewestRoute = 0;
|
||||||
|
|
||||||
CSRWLock m_deviceContextLock;
|
CSRWLock m_deviceContextLock;
|
||||||
CDeviceContext * m_deviceContext = nullptr;
|
CDeviceContext * m_deviceContext = nullptr;
|
||||||
@@ -115,8 +140,9 @@ class CPipeServer : private IPipeEndpointHandler,
|
|||||||
void SetGPUStatus(bool software);
|
void SetGPUStatus(bool software);
|
||||||
void ResolutionRejected(uint32_t width, uint32_t height,
|
void ResolutionRejected(uint32_t width, uint32_t height,
|
||||||
uint32_t requiredSizeMiB);
|
uint32_t requiredSizeMiB);
|
||||||
bool SetRecovery(void * owner, uint64_t route,
|
RecoveryDispatch SetRecovery(void * owner, uint64_t route,
|
||||||
uint64_t session, uint32_t serial, bool active);
|
uint64_t session, uint32_t serial, bool active,
|
||||||
|
bool replayIfUnavailable = true);
|
||||||
|
|
||||||
CClipboardChannel& Clipboard() { return m_clipboard; }
|
CClipboardChannel& Clipboard() { return m_clipboard; }
|
||||||
bool ClipboardKick(uint64_t epoch) override;
|
bool ClipboardKick(uint64_t epoch) override;
|
||||||
|
|||||||
Reference in New Issue
Block a user