mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
1566 lines
39 KiB
C++
1566 lines
39 KiB
C++
/**
|
|
* Looking Glass
|
|
* Copyright © 2017-2026 The Looking Glass Authors
|
|
* https://looking-glass.io
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "transport/CTransportManager.h"
|
|
|
|
#include "capture/CFrameGraph.h"
|
|
#include "CDebug.h"
|
|
#include "Seq.h"
|
|
|
|
#include <Windows.h>
|
|
#include <new>
|
|
#include <utility>
|
|
|
|
static const uint64_t RETRY_DELAY_MS = 500;
|
|
static const uint64_t SERVICE_RETRY_DELAY_MS = 250;
|
|
|
|
class CSourceEvents final : public ITransportEvents
|
|
{
|
|
private:
|
|
BackendId m_backend;
|
|
uint32_t m_epoch;
|
|
bool m_interactions;
|
|
CInputHub& m_input;
|
|
CRecoveryHub& m_recovery;
|
|
ITransportActions& m_actions;
|
|
|
|
SourceKey Stamp(const SourceKey& source) const
|
|
{
|
|
SourceKey stamped = source;
|
|
stamped.backend = m_backend;
|
|
stamped.epoch = m_epoch;
|
|
return stamped;
|
|
}
|
|
|
|
public:
|
|
CSourceEvents(
|
|
BackendId backend, uint32_t epoch, bool interactions, CInputHub& input,
|
|
CRecoveryHub& recovery, ITransportActions& actions) :
|
|
m_backend(backend), m_epoch(epoch), m_interactions(interactions),
|
|
m_input(input), m_recovery(recovery), m_actions(actions) {}
|
|
|
|
InteractionResult OnSetCursorPos(
|
|
const SourceKey& source, int32_t x, int32_t y) override
|
|
{
|
|
if (!m_interactions)
|
|
return InteractionResult::UNAVAILABLE;
|
|
SourceKey stamped = Stamp(source);
|
|
CInputHub::InteractionPermit permit;
|
|
const InteractionResult result =
|
|
m_input.CheckInteraction(stamped, permit);
|
|
if (result != InteractionResult::ACCEPTED)
|
|
return result;
|
|
const InteractionResult applied =
|
|
m_actions.OnSetCursorPos(stamped, x, y);
|
|
if (applied == InteractionResult::ACCEPTED)
|
|
m_input.CommitInteraction(stamped, permit);
|
|
return applied;
|
|
}
|
|
|
|
InteractionResult OnSetResolution(const SourceKey& source,
|
|
uint32_t width, uint32_t height) override
|
|
{
|
|
if (!m_interactions)
|
|
return InteractionResult::UNAVAILABLE;
|
|
SourceKey stamped = Stamp(source);
|
|
CInputHub::InteractionPermit permit;
|
|
const InteractionResult result =
|
|
m_input.CheckInteraction(stamped, permit);
|
|
if (result != InteractionResult::ACCEPTED)
|
|
return result;
|
|
const InteractionResult applied =
|
|
m_actions.OnSetResolution(stamped, width, height);
|
|
if (applied == InteractionResult::ACCEPTED)
|
|
m_input.CommitInteraction(stamped, permit);
|
|
return applied;
|
|
}
|
|
|
|
RecoveryAdmission OnRecoveryRequest(const SourceKey& source,
|
|
uint64_t session, uint32_t serial, bool active) override
|
|
{
|
|
RecoveryAction action;
|
|
bool dispatch = false;
|
|
const RecoveryAdmission admission = m_recovery.Submit(
|
|
Stamp(source), session, serial, active, GetTickCount64(),
|
|
action, dispatch);
|
|
if (dispatch && !m_actions.OnRecoveryAction(action))
|
|
m_recovery.DispatchFailed(action, RPC_S_SERVER_UNAVAILABLE);
|
|
return admission;
|
|
}
|
|
};
|
|
|
|
CTransportManager::Entry::Entry()
|
|
{
|
|
idleEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
|
}
|
|
|
|
CTransportManager::Entry::~Entry()
|
|
{
|
|
if (idleEvent)
|
|
CloseHandle(idleEvent);
|
|
}
|
|
|
|
CTransportManager::CTransportManager()
|
|
{
|
|
m_phaseIdle = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
|
m_stoppedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
}
|
|
|
|
CTransportManager::~CTransportManager()
|
|
{
|
|
Stop();
|
|
if (m_stoppedEvent)
|
|
CloseHandle(m_stoppedEvent);
|
|
if (m_phaseIdle)
|
|
CloseHandle(m_phaseIdle);
|
|
}
|
|
|
|
unsigned CTransportManager::Entries(
|
|
Entry * entries[FRAME_MAX_SINKS]) const
|
|
{
|
|
CSRWSharedLock managerLock(m_lock);
|
|
for (unsigned i = 0; i < m_entryCount; ++i)
|
|
entries[i] = m_entries[i].get();
|
|
return m_entryCount;
|
|
}
|
|
|
|
CTransportManager::Entry * CTransportManager::Primary() const
|
|
{
|
|
CSRWSharedLock managerLock(m_lock);
|
|
return m_primary;
|
|
}
|
|
|
|
bool CTransportManager::BeginPhase(
|
|
Phase phase, bool wait, bool stopping)
|
|
{
|
|
const DWORD thread = GetCurrentThreadId();
|
|
for (;;)
|
|
{
|
|
HANDLE idleEvent = nullptr;
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
if ((!stopping && (m_stopping || m_stopped)) ||
|
|
(stopping && m_stopped))
|
|
return false;
|
|
|
|
if (m_phase == Phase::IDLE)
|
|
{
|
|
m_phase = phase;
|
|
m_phaseOwner = thread;
|
|
ResetEvent(m_phaseIdle);
|
|
return true;
|
|
}
|
|
|
|
if (!wait || m_phaseOwner == thread)
|
|
return false;
|
|
idleEvent = m_phaseIdle;
|
|
}
|
|
|
|
if (!idleEvent ||
|
|
WaitForSingleObject(idleEvent, INFINITE) != WAIT_OBJECT_0)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CTransportManager::EndPhase()
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
m_phase = Phase::IDLE;
|
|
m_phaseOwner = 0;
|
|
SetEvent(m_phaseIdle);
|
|
}
|
|
|
|
bool CTransportManager::BeginCall(
|
|
Entry& entry, Call call, bool wait)
|
|
{
|
|
const DWORD thread = GetCurrentThreadId();
|
|
for (;;)
|
|
{
|
|
HANDLE idleEvent = nullptr;
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
if (entry.stopRequested || entry.state == State::STOPPED)
|
|
return false;
|
|
|
|
if (entry.call == Call::IDLE)
|
|
{
|
|
entry.call = call;
|
|
entry.callOwner = thread;
|
|
ResetEvent(entry.idleEvent);
|
|
return true;
|
|
}
|
|
|
|
if (!wait || entry.callOwner == thread)
|
|
return false;
|
|
idleEvent = entry.idleEvent;
|
|
}
|
|
|
|
if (!idleEvent ||
|
|
WaitForSingleObject(idleEvent, INFINITE) != WAIT_OBJECT_0)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CTransportManager::DrainRecovery(Entry& entry,
|
|
const std::shared_ptr<ITransport>& transport)
|
|
{
|
|
static const unsigned MAX_DRAIN = 17;
|
|
for (unsigned i = 0; i < MAX_DRAIN; ++i)
|
|
{
|
|
bool sync = false;
|
|
BackendId id = 0;
|
|
uint32_t epoch = 0;
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
if (entry.stopRequested || !transport ||
|
|
transport != entry.transport ||
|
|
(entry.state != State::INITIALIZED &&
|
|
entry.state != State::READY))
|
|
{
|
|
entry.syncPending = false;
|
|
return;
|
|
}
|
|
|
|
id = entry.id;
|
|
epoch = entry.epoch;
|
|
sync = entry.syncPending;
|
|
entry.syncPending = false;
|
|
}
|
|
|
|
if (sync)
|
|
transport->SyncRecovery();
|
|
|
|
CRecoveryHub::Delivery delivery;
|
|
if (!m_recovery.TakeDelivery(id, epoch, delivery))
|
|
{
|
|
if (!sync)
|
|
return;
|
|
continue;
|
|
}
|
|
transport->RecoveryStatus(delivery.source, delivery.session,
|
|
delivery.serial, delivery.active, delivery.state, delivery.error);
|
|
}
|
|
}
|
|
|
|
void CTransportManager::EndCall(Entry& entry,
|
|
const std::shared_ptr<ITransport>& transport, bool drain)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (drain)
|
|
DrainRecovery(entry, transport);
|
|
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
if (drain && entry.syncPending)
|
|
continue;
|
|
|
|
entry.call = Call::IDLE;
|
|
entry.callOwner = 0;
|
|
SetEvent(entry.idleEvent);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CTransportManager::DetachRecovery(Entry& entry)
|
|
{
|
|
BackendId id = 0;
|
|
uint32_t epoch = 0;
|
|
bool attached = false;
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
id = entry.id;
|
|
epoch = entry.epoch;
|
|
attached = entry.recoveryAttached;
|
|
entry.recoveryAttached = false;
|
|
entry.syncPending = false;
|
|
}
|
|
if (attached)
|
|
m_recovery.Remove(id, epoch);
|
|
}
|
|
|
|
bool CTransportManager::Add(TransportInstance config, bool primary,
|
|
CreateFn create)
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
if (!m_phaseIdle || !m_stoppedEvent || m_phase != Phase::IDLE ||
|
|
m_started || m_stopping ||
|
|
m_stopped || !config.enabled || !config.id || config.kind.empty() ||
|
|
(config.services & ~TRANSPORT_SERVICE_ALL) || !create ||
|
|
m_entryCount == FRAME_MAX_SINKS || (primary && m_primary))
|
|
return false;
|
|
|
|
if (primary && (!config.required ||
|
|
!(config.services & TRANSPORT_SERVICE_FRAME)))
|
|
return false;
|
|
|
|
for (unsigned i = 0; i < m_entryCount; ++i)
|
|
if (m_entries[i]->id == config.id)
|
|
return false;
|
|
|
|
std::unique_ptr<Entry> entry(new (std::nothrow) Entry);
|
|
if (!entry || !entry->idleEvent)
|
|
return false;
|
|
|
|
entry->id = config.id;
|
|
entry->required = config.required;
|
|
entry->primary = primary;
|
|
entry->create = create;
|
|
entry->config = std::move(config);
|
|
|
|
Entry * raw = entry.get();
|
|
m_entries[m_entryCount++] = std::move(entry);
|
|
if (primary)
|
|
m_primary = raw;
|
|
return true;
|
|
}
|
|
|
|
ITransport::OpenResult CTransportManager::OpenEntry(Entry& entry)
|
|
{
|
|
std::shared_ptr<ITransport> transport;
|
|
CreateFn create = nullptr;
|
|
TransportInstance config;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
transport = entry.transport;
|
|
create = entry.create;
|
|
config = entry.config;
|
|
}
|
|
|
|
if (!transport)
|
|
{
|
|
std::unique_ptr<ITransport> created = create(config);
|
|
transport.reset(created.release());
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.transport = transport;
|
|
}
|
|
|
|
if (!transport)
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::FAILED;
|
|
return OpenResult::FAILURE;
|
|
}
|
|
|
|
const OpenResult result = transport->Open();
|
|
switch (result)
|
|
{
|
|
case OpenResult::SUCCESS:
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::OPEN;
|
|
break;
|
|
}
|
|
|
|
case OpenResult::RETRY:
|
|
ScheduleRetry(entry);
|
|
break;
|
|
|
|
case OpenResult::FAILURE:
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::FAILED;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool CTransportManager::InitializeEntry(Entry& entry)
|
|
{
|
|
std::shared_ptr<ITransport> transport;
|
|
BackendId id = 0;
|
|
uint32_t epoch = 0;
|
|
uint32_t services = 0;
|
|
bool required = false;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
if (entry.state == State::INITIALIZED || entry.state == State::READY)
|
|
return true;
|
|
if (entry.state != State::OPEN)
|
|
return false;
|
|
transport = entry.transport;
|
|
id = entry.id;
|
|
epoch = entry.epoch;
|
|
services = entry.config.services;
|
|
required = entry.required;
|
|
}
|
|
|
|
if (!transport || !transport->Initialize())
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::FAILED;
|
|
return false;
|
|
}
|
|
|
|
std::shared_ptr<const FrameCaps> frameCaps;
|
|
if (services & TRANSPORT_SERVICE_FRAME)
|
|
{
|
|
frameCaps = transport->GetFrameCaps();
|
|
if (!frameCaps && required)
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::FAILED;
|
|
return false;
|
|
}
|
|
}
|
|
const bool hasFrameCaps = frameCaps != nullptr;
|
|
bool syncNow = false;
|
|
if (!m_recovery.Attach(id, epoch, syncNow))
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::FAILED;
|
|
return false;
|
|
}
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
// The first successfully initialized instance fixes the advertised
|
|
// capability contract. Recreating an instance must not change the mode
|
|
// list during a runtime restart.
|
|
if (!entry.frameCaps)
|
|
entry.frameCaps = std::move(frameCaps);
|
|
// A best-effort instance without a capability contract can continue to
|
|
// provide its other configured services, but must not receive frames.
|
|
entry.frameAbsent = (services & TRANSPORT_SERVICE_FRAME) &&
|
|
!hasFrameCaps;
|
|
entry.recoveryAttached = true;
|
|
entry.syncPending = syncNow;
|
|
entry.state = State::INITIALIZED;
|
|
}
|
|
// Monitor readiness may be published after Attach but before this entry is
|
|
// visible to SyncRecovery. Claim any synchronization missed in that gap.
|
|
if (m_recovery.ClaimSync(id, epoch))
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
if (entry.id == id && entry.epoch == epoch && entry.recoveryAttached)
|
|
entry.syncPending = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CTransportManager::AddServices(Entry& entry)
|
|
{
|
|
std::shared_ptr<ITransport> transport;
|
|
BackendId id = 0;
|
|
uint32_t epoch = 0;
|
|
bool primary = false;
|
|
bool required = false;
|
|
uint32_t services = 0;
|
|
bool controlAdded = false;
|
|
bool controlFailed = false;
|
|
bool controlAbsent = false;
|
|
bool inputAdded = false;
|
|
bool inputFailed = false;
|
|
bool inputAbsent = false;
|
|
bool frameAdded = false;
|
|
bool frameAbsent = false;
|
|
uint64_t retryAt = 0;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
transport = entry.transport;
|
|
id = entry.id;
|
|
epoch = entry.epoch;
|
|
primary = entry.primary;
|
|
required = entry.required;
|
|
services = entry.config.services;
|
|
controlAdded = entry.controlAdded;
|
|
controlFailed = entry.controlFailed;
|
|
controlAbsent = entry.controlAbsent;
|
|
inputAdded = entry.inputAdded;
|
|
inputFailed = entry.inputFailed;
|
|
inputAbsent = entry.inputAbsent;
|
|
frameAdded = entry.frameAdded;
|
|
frameAbsent = entry.frameAbsent;
|
|
retryAt = entry.serviceRetryAt;
|
|
}
|
|
|
|
if (!transport)
|
|
return !required;
|
|
|
|
const uint64_t now = GetTickCount64();
|
|
const bool attach = now >= retryAt;
|
|
bool controlRetry = false;
|
|
bool frameRetry = false;
|
|
bool inputRetry = false;
|
|
if (!(services & TRANSPORT_SERVICE_CONTROL))
|
|
controlAbsent = true;
|
|
else if (attach && !controlAdded && !controlFailed && !controlAbsent)
|
|
{
|
|
IControlSink * control = transport->Control();
|
|
if (control && m_control.Add(id, epoch, *control))
|
|
{
|
|
controlAdded = true;
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.controlAdded = true;
|
|
}
|
|
else if (control)
|
|
controlRetry = true;
|
|
else
|
|
{
|
|
controlAbsent = true;
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.controlAbsent = true;
|
|
}
|
|
}
|
|
|
|
if (!(services & TRANSPORT_SERVICE_FRAME))
|
|
frameAbsent = true;
|
|
else if (attach && !frameAdded && !frameAbsent)
|
|
{
|
|
IFrameSink * frame = transport->FrameSink();
|
|
if (frame && m_frames.Bind(id, epoch, primary, *frame))
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.frameAdded = true;
|
|
frameAdded = true;
|
|
}
|
|
else if (frame || primary)
|
|
frameRetry = true;
|
|
else
|
|
{
|
|
frameAbsent = true;
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.frameAbsent = true;
|
|
}
|
|
}
|
|
|
|
if (!(services & TRANSPORT_SERVICE_INPUT))
|
|
inputAbsent = true;
|
|
else if (attach && !inputAdded && !inputFailed && !inputAbsent)
|
|
{
|
|
IInputSource * input = transport->Input();
|
|
if (input && m_input.Bind(id, epoch, *input))
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.inputAdded = true;
|
|
inputAdded = true;
|
|
}
|
|
else if (input)
|
|
inputRetry = true;
|
|
else
|
|
{
|
|
inputAbsent = true;
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.inputAbsent = true;
|
|
}
|
|
}
|
|
|
|
if (attach && (controlRetry || inputRetry || frameRetry))
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.serviceRetryAt = now + SERVICE_RETRY_DELAY_MS;
|
|
}
|
|
|
|
const bool servicesReady =
|
|
(!(services & TRANSPORT_SERVICE_FRAME) || frameAdded) &&
|
|
(!(services & TRANSPORT_SERVICE_CONTROL) || controlAdded) &&
|
|
(!(services & TRANSPORT_SERVICE_INPUT) || inputAdded);
|
|
return !required || servicesReady;
|
|
}
|
|
|
|
void CTransportManager::HandleServiceFailures()
|
|
{
|
|
ControlToken token;
|
|
while (m_control.TakeFailure(token))
|
|
{
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
bool restart = false;
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
if (entry.id != token.backend || entry.epoch != token.epoch ||
|
|
!entry.controlAdded)
|
|
continue;
|
|
entry.controlAdded = false;
|
|
entry.controlFailed = true;
|
|
restart = !entry.exposed;
|
|
}
|
|
|
|
m_control.Remove(token.backend, token.epoch);
|
|
m_input.RevokeInteraction(token.backend, token.epoch);
|
|
if (restart)
|
|
{
|
|
RemoveServices(entry);
|
|
ScheduleRetry(entry);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
SourceKey source;
|
|
while (m_input.TakeFailure(source))
|
|
{
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
bool restart = false;
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
if (entry.id != source.backend || entry.epoch != source.epoch ||
|
|
!entry.inputAdded)
|
|
continue;
|
|
entry.inputAdded = false;
|
|
entry.inputFailed = true;
|
|
restart = !entry.exposed;
|
|
}
|
|
|
|
m_input.Unbind(source.backend, source.epoch);
|
|
if (restart)
|
|
{
|
|
RemoveServices(entry);
|
|
ScheduleRetry(entry);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CTransportManager::SetupEntry(Entry& entry, size_t alignment)
|
|
{
|
|
std::shared_ptr<ITransport> transport;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
if (entry.state != State::INITIALIZED && entry.state != State::READY)
|
|
return false;
|
|
transport = entry.transport;
|
|
}
|
|
|
|
bool setupDone = false;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
setupDone = entry.setupDone;
|
|
}
|
|
|
|
if (!transport || (!setupDone && !transport->Setup(alignment)))
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::FAILED;
|
|
return false;
|
|
}
|
|
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.setupDone = true;
|
|
}
|
|
if (!AddServices(entry))
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::INITIALIZED;
|
|
return false;
|
|
}
|
|
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::READY;
|
|
return true;
|
|
}
|
|
|
|
void CTransportManager::ScheduleRetry(Entry& entry)
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::RETRY;
|
|
entry.retryAt = GetTickCount64() + RETRY_DELAY_MS;
|
|
}
|
|
|
|
void CTransportManager::RemoveServices(Entry& entry)
|
|
{
|
|
BackendId id = 0;
|
|
uint32_t epoch = 0;
|
|
bool frameAdded = false;
|
|
bool controlAdded = false;
|
|
bool inputAdded = false;
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
id = entry.id;
|
|
epoch = entry.epoch;
|
|
frameAdded = entry.frameAdded;
|
|
controlAdded = entry.controlAdded;
|
|
inputAdded = entry.inputAdded;
|
|
entry.frameAdded = false;
|
|
entry.controlAdded = false;
|
|
entry.inputAdded = false;
|
|
}
|
|
|
|
m_input.RevokeInteraction(id, epoch);
|
|
if (inputAdded)
|
|
m_input.Unbind(id, epoch);
|
|
if (controlAdded)
|
|
m_control.Remove(id, epoch);
|
|
if (frameAdded)
|
|
m_frames.Unbind(id, epoch);
|
|
}
|
|
|
|
void CTransportManager::RetryEntry(Entry& entry, uint64_t now,
|
|
bool initialized, bool setup, size_t alignment)
|
|
{
|
|
std::shared_ptr<ITransport> transport;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
if (entry.state != State::RETRY || now < entry.retryAt ||
|
|
(entry.primary && entry.exposed))
|
|
return;
|
|
transport = entry.transport;
|
|
}
|
|
|
|
DetachRecovery(entry);
|
|
RemoveServices(entry);
|
|
if (transport)
|
|
transport->Stop();
|
|
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.transport.reset();
|
|
entry.directMemory = DirectFrameBufferMemory {};
|
|
entry.directMemoryValid = false;
|
|
entry.setupDone = false;
|
|
entry.controlFailed = false;
|
|
entry.controlAbsent = false;
|
|
entry.inputFailed = false;
|
|
entry.inputAbsent = false;
|
|
entry.frameAbsent = false;
|
|
entry.serviceRetryAt = 0;
|
|
Seq::Inc(entry.epoch);
|
|
}
|
|
|
|
if (OpenEntry(entry) != OpenResult::SUCCESS)
|
|
return;
|
|
if (initialized && !InitializeEntry(entry))
|
|
{
|
|
ScheduleRetry(entry);
|
|
return;
|
|
}
|
|
if (setup && !SetupEntry(entry, alignment))
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
if (entry.state == State::FAILED)
|
|
{
|
|
entryLock.Unlock();
|
|
ScheduleRetry(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTransportManager::HandleProcessResult(
|
|
Entry& entry, ProcessResult result)
|
|
{
|
|
if (result == ProcessResult::OK)
|
|
return;
|
|
|
|
bool exposed = false;
|
|
bool primary = false;
|
|
std::wstring name;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
exposed = entry.exposed;
|
|
primary = entry.primary;
|
|
name = entry.config.kind;
|
|
}
|
|
|
|
DetachRecovery(entry);
|
|
RemoveServices(entry);
|
|
if (primary && exposed)
|
|
{
|
|
DEBUG_WARN("Transport %ls stopped while its frame interfaces are active",
|
|
name.c_str());
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::FAILED;
|
|
return;
|
|
}
|
|
|
|
if (result == ProcessResult::RETRY)
|
|
{
|
|
ScheduleRetry(entry);
|
|
return;
|
|
}
|
|
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::FAILED;
|
|
}
|
|
|
|
void CTransportManager::Expose(Entry& entry)
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.exposed = true;
|
|
entry.exposedTransport = entry.transport;
|
|
}
|
|
|
|
ITransport::OpenResult CTransportManager::Open()
|
|
{
|
|
if (!BeginPhase(Phase::OPEN, true))
|
|
return OpenResult::FAILURE;
|
|
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
m_started = true;
|
|
}
|
|
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
OpenResult aggregate = Primary() ?
|
|
OpenResult::SUCCESS : OpenResult::FAILURE;
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
if (!BeginCall(entry, Call::LIFECYCLE, true))
|
|
{
|
|
if (entry.required)
|
|
aggregate = OpenResult::FAILURE;
|
|
continue;
|
|
}
|
|
|
|
bool alreadyOpen = false;
|
|
std::shared_ptr<ITransport> transport;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
alreadyOpen = entry.state == State::OPEN ||
|
|
entry.state == State::INITIALIZED || entry.state == State::READY;
|
|
transport = entry.transport;
|
|
}
|
|
|
|
const OpenResult result = alreadyOpen ?
|
|
OpenResult::SUCCESS : OpenEntry(entry);
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
transport = entry.transport;
|
|
}
|
|
EndCall(entry, transport);
|
|
|
|
if (entry.required && result == OpenResult::FAILURE)
|
|
aggregate = OpenResult::FAILURE;
|
|
else if (entry.required && result == OpenResult::RETRY &&
|
|
aggregate == OpenResult::SUCCESS)
|
|
aggregate = OpenResult::RETRY;
|
|
}
|
|
|
|
EndPhase();
|
|
return aggregate;
|
|
}
|
|
|
|
bool CTransportManager::Initialize()
|
|
{
|
|
if (!BeginPhase(Phase::INITIALIZE, true))
|
|
return false;
|
|
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
bool success = true;
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
if (!BeginCall(entry, Call::LIFECYCLE, true))
|
|
{
|
|
if (entry.required)
|
|
success = false;
|
|
continue;
|
|
}
|
|
|
|
State state;
|
|
std::shared_ptr<ITransport> transport;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
state = entry.state;
|
|
transport = entry.transport;
|
|
}
|
|
|
|
if (state == State::OPEN && !InitializeEntry(entry))
|
|
{
|
|
if (entry.required)
|
|
success = false;
|
|
else
|
|
ScheduleRetry(entry);
|
|
}
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
transport = entry.transport;
|
|
if (entry.required && entry.state != State::INITIALIZED &&
|
|
entry.state != State::READY)
|
|
success = false;
|
|
}
|
|
EndCall(entry, transport);
|
|
}
|
|
|
|
Entry * primary = Primary();
|
|
if (success && primary)
|
|
{
|
|
CSRWSharedLock entryLock(primary->lock);
|
|
success = primary->state == State::INITIALIZED ||
|
|
primary->state == State::READY;
|
|
}
|
|
else
|
|
success = false;
|
|
|
|
if (success)
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
m_initialized = true;
|
|
}
|
|
EndPhase();
|
|
return success;
|
|
}
|
|
|
|
bool CTransportManager::Setup(size_t alignment)
|
|
{
|
|
if (!BeginPhase(Phase::SETUP, true))
|
|
return false;
|
|
|
|
bool initialized = false;
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
initialized = m_initialized && m_primary;
|
|
if (initialized)
|
|
m_alignment = alignment;
|
|
}
|
|
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
bool success = initialized;
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
if (!BeginCall(entry, Call::LIFECYCLE, true))
|
|
{
|
|
if (entry.required)
|
|
success = false;
|
|
continue;
|
|
}
|
|
|
|
State state;
|
|
std::shared_ptr<ITransport> transport;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
state = entry.state;
|
|
transport = entry.transport;
|
|
}
|
|
|
|
if (state != State::INITIALIZED && state != State::READY)
|
|
{
|
|
if (entry.required)
|
|
success = false;
|
|
}
|
|
else if (!SetupEntry(entry, alignment))
|
|
{
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
state = entry.state;
|
|
}
|
|
if (entry.required)
|
|
success = false;
|
|
else if (state == State::FAILED)
|
|
ScheduleRetry(entry);
|
|
}
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
transport = entry.transport;
|
|
}
|
|
EndCall(entry, transport);
|
|
}
|
|
|
|
Entry * primary = Primary();
|
|
if (primary)
|
|
{
|
|
CSRWSharedLock entryLock(primary->lock);
|
|
if (primary->state != State::READY || !primary->frameAdded)
|
|
success = false;
|
|
}
|
|
else
|
|
success = false;
|
|
|
|
if (initialized)
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
m_setup = true;
|
|
}
|
|
EndPhase();
|
|
return success;
|
|
}
|
|
|
|
CfgResult CTransportManager::Cfg(
|
|
const GraphCfg& cfg, CFrameGraph& graph)
|
|
{
|
|
CFrameGraph next;
|
|
if (!next.Begin(cfg))
|
|
return CfgResult::REJECTED;
|
|
if (!BeginPhase(Phase::CFG, true))
|
|
return CfgResult::RETRY;
|
|
|
|
struct Route
|
|
{
|
|
Entry * entry = nullptr;
|
|
std::shared_ptr<ITransport> transport;
|
|
BackendId id = 0;
|
|
uint32_t epoch = 0;
|
|
bool required = false;
|
|
bool primary = false;
|
|
bool prepared = false;
|
|
bool eligible = false;
|
|
FrameProfile profiles[FRAME_PROFILE_MAX] = {};
|
|
unsigned profileCount = 0;
|
|
};
|
|
|
|
Route routes[FRAME_MAX_SINKS];
|
|
unsigned routeCount = 0;
|
|
CfgResult result = CfgResult::ACCEPTED;
|
|
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
bool frameService = false;
|
|
bool required = false;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
frameService =
|
|
(entry.config.services & TRANSPORT_SERVICE_FRAME) != 0;
|
|
required = entry.required;
|
|
}
|
|
if (!frameService)
|
|
continue;
|
|
|
|
if (!BeginCall(entry, Call::CFG, true))
|
|
{
|
|
if (required)
|
|
{
|
|
result = CfgResult::RETRY;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
Route& route = routes[routeCount++];
|
|
route.entry = &entry;
|
|
State state;
|
|
bool frameAbsent = false;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
route.transport = entry.transport;
|
|
route.id = entry.id;
|
|
route.epoch = entry.epoch;
|
|
route.required = entry.required;
|
|
route.primary = entry.primary;
|
|
state = entry.state;
|
|
frameAbsent = entry.frameAbsent;
|
|
}
|
|
|
|
route.eligible = route.transport && !frameAbsent &&
|
|
(state == State::INITIALIZED || state == State::READY);
|
|
if (!route.eligible)
|
|
{
|
|
if (!route.required &&
|
|
(frameAbsent || state == State::FAILED))
|
|
continue;
|
|
result = frameAbsent ? CfgResult::REJECTED :
|
|
(state == State::FAILED ? CfgResult::FAILED : CfgResult::RETRY);
|
|
break;
|
|
}
|
|
|
|
unsigned profileCount = 0;
|
|
const FrameProfile * profiles =
|
|
route.transport->Profiles(profileCount);
|
|
if (profileCount > FRAME_PROFILE_MAX ||
|
|
(profileCount && !profiles))
|
|
{
|
|
route.eligible = false;
|
|
if (route.required)
|
|
{
|
|
result = CfgResult::FAILED;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
route.profileCount = profileCount;
|
|
for (unsigned profile = 0; profile < profileCount; ++profile)
|
|
route.profiles[profile] = profiles[profile];
|
|
}
|
|
|
|
if (result == CfgResult::ACCEPTED)
|
|
for (unsigned i = 0; i < routeCount; ++i)
|
|
{
|
|
Route& route = routes[i];
|
|
if (!route.eligible)
|
|
continue;
|
|
|
|
CfgResult routeResult = CfgResult::NEXT;
|
|
for (unsigned profileIndex = 0;
|
|
profileIndex < route.profileCount; ++profileIndex)
|
|
{
|
|
const FrameProfile& profile = route.profiles[profileIndex];
|
|
FrameCfg candidate;
|
|
candidate.mode = cfg.mode;
|
|
candidate.adapter = cfg.adapter;
|
|
candidate.width = cfg.width;
|
|
candidate.height = cfg.height;
|
|
candidate.profile = profile;
|
|
if (!next.Can(candidate))
|
|
continue;
|
|
|
|
routeResult = route.transport->Probe(candidate);
|
|
if (routeResult == CfgResult::NEXT)
|
|
continue;
|
|
if (routeResult != CfgResult::ACCEPTED)
|
|
break;
|
|
|
|
routeResult = route.transport->Prepare(candidate);
|
|
if (routeResult == CfgResult::NEXT)
|
|
{
|
|
route.transport->Abort();
|
|
continue;
|
|
}
|
|
if (routeResult != CfgResult::ACCEPTED)
|
|
break;
|
|
|
|
if (!next.Add(route.id, route.epoch, route.required,
|
|
route.primary, candidate))
|
|
{
|
|
route.transport->Abort();
|
|
routeResult = CfgResult::FAILED;
|
|
break;
|
|
}
|
|
route.prepared = true;
|
|
break;
|
|
}
|
|
|
|
if (route.prepared)
|
|
continue;
|
|
|
|
route.transport->Abort();
|
|
if (routeResult == CfgResult::RETRY)
|
|
{
|
|
result = CfgResult::RETRY;
|
|
break;
|
|
}
|
|
if (route.required)
|
|
{
|
|
result = routeResult == CfgResult::NEXT ?
|
|
CfgResult::REJECTED : routeResult;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result == CfgResult::ACCEPTED && !next.Seal())
|
|
result = CfgResult::FAILED;
|
|
|
|
if (result == CfgResult::ACCEPTED)
|
|
{
|
|
for (unsigned i = 0; i < routeCount; ++i)
|
|
if (routes[i].prepared)
|
|
routes[i].transport->Commit();
|
|
graph = next;
|
|
}
|
|
else
|
|
for (unsigned i = routeCount; i > 0; --i)
|
|
if (routes[i - 1].prepared)
|
|
routes[i - 1].transport->Abort();
|
|
|
|
for (unsigned i = routeCount; i > 0; --i)
|
|
EndCall(*routes[i - 1].entry, routes[i - 1].transport);
|
|
EndPhase();
|
|
return result;
|
|
}
|
|
|
|
ITransport::ProcessResult CTransportManager::Process(
|
|
ITransportActions& actions)
|
|
{
|
|
if (!BeginPhase(Phase::PROCESS, false))
|
|
{
|
|
CSRWSharedLock managerLock(m_lock);
|
|
return m_stopping || m_stopped ?
|
|
ProcessResult::FAILURE : ProcessResult::OK;
|
|
}
|
|
|
|
bool initialized = false;
|
|
bool setup = false;
|
|
size_t alignment = 0;
|
|
{
|
|
CSRWSharedLock managerLock(m_lock);
|
|
initialized = m_initialized;
|
|
setup = m_setup;
|
|
alignment = m_alignment;
|
|
}
|
|
|
|
const uint64_t now = GetTickCount64();
|
|
m_recovery.Tick(now);
|
|
HandleServiceFailures();
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
if (!BeginCall(entry, Call::PROCESS, false))
|
|
continue;
|
|
|
|
RetryEntry(entry, now, initialized, setup, alignment);
|
|
|
|
std::shared_ptr<ITransport> transport;
|
|
BackendId id = 0;
|
|
uint32_t epoch = 0;
|
|
bool process = false;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
transport = entry.transport;
|
|
id = entry.id;
|
|
epoch = entry.epoch;
|
|
process = transport &&
|
|
(entry.state == State::INITIALIZED || entry.state == State::READY);
|
|
}
|
|
|
|
if (!process)
|
|
{
|
|
EndCall(entry, transport);
|
|
continue;
|
|
}
|
|
|
|
bool setupDone = false;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
setupDone = entry.setupDone;
|
|
}
|
|
if (setup && !setupDone)
|
|
{
|
|
DrainRecovery(entry, transport);
|
|
if (!transport || !transport->Setup(alignment))
|
|
{
|
|
HandleProcessResult(entry, ProcessResult::FAILURE);
|
|
EndCall(entry, transport);
|
|
continue;
|
|
}
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.setupDone = true;
|
|
}
|
|
|
|
if (setup)
|
|
SetupEntry(entry, alignment);
|
|
|
|
bool interactions = false;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
interactions = entry.controlAdded;
|
|
}
|
|
DrainRecovery(entry, transport);
|
|
CSourceEvents sourceEvents(
|
|
id, epoch, interactions, m_input, m_recovery, actions);
|
|
const ProcessResult result = transport->Process(sourceEvents);
|
|
DrainRecovery(entry, transport);
|
|
HandleProcessResult(entry, result);
|
|
EndCall(entry, transport);
|
|
}
|
|
|
|
EndPhase();
|
|
return ProcessResult::OK;
|
|
}
|
|
|
|
void CTransportManager::Stop()
|
|
{
|
|
const DWORD thread = GetCurrentThreadId();
|
|
bool wait = false;
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
if (m_stopped)
|
|
return;
|
|
if (m_stopping)
|
|
wait = true;
|
|
else
|
|
{
|
|
if (m_phase != Phase::IDLE && m_phaseOwner == thread)
|
|
return;
|
|
m_stopping = true;
|
|
ResetEvent(m_stoppedEvent);
|
|
}
|
|
}
|
|
|
|
if (wait)
|
|
{
|
|
WaitForSingleObject(m_stoppedEvent, INFINITE);
|
|
return;
|
|
}
|
|
|
|
if (!BeginPhase(Phase::STOP, true, true))
|
|
return;
|
|
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
bool failed = false;
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
CSRWExclusiveLock entryLock(entries[i]->lock);
|
|
entries[i]->stopRequested = true;
|
|
entries[i]->syncPending = false;
|
|
}
|
|
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
for (;;)
|
|
{
|
|
HANDLE idleEvent = nullptr;
|
|
{
|
|
CSRWSharedLock entryLock(entries[i]->lock);
|
|
if (entries[i]->call == Call::IDLE)
|
|
break;
|
|
idleEvent = entries[i]->idleEvent;
|
|
}
|
|
if (!idleEvent ||
|
|
WaitForSingleObject(idleEvent, INFINITE) != WAIT_OBJECT_0)
|
|
{
|
|
failed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
CSRWExclusiveLock entryLock(entries[i]->lock);
|
|
entries[i]->stopRequested = false;
|
|
}
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
m_stopping = false;
|
|
m_phase = Phase::IDLE;
|
|
m_phaseOwner = 0;
|
|
SetEvent(m_phaseIdle);
|
|
SetEvent(m_stoppedEvent);
|
|
return;
|
|
}
|
|
|
|
for (unsigned i = count; i > 0; --i)
|
|
{
|
|
DetachRecovery(*entries[i - 1]);
|
|
RemoveServices(*entries[i - 1]);
|
|
}
|
|
|
|
m_input.Stop();
|
|
|
|
for (unsigned i = count; i > 0; --i)
|
|
{
|
|
Entry& entry = *entries[i - 1];
|
|
std::shared_ptr<ITransport> transport;
|
|
State state;
|
|
{
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
transport = entry.transport;
|
|
state = entry.state;
|
|
}
|
|
|
|
if (transport && state != State::STOPPED)
|
|
transport->Stop();
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
entry.state = State::STOPPED;
|
|
}
|
|
}
|
|
|
|
{
|
|
CSRWExclusiveLock managerLock(m_lock);
|
|
m_initialized = false;
|
|
m_setup = false;
|
|
m_stopped = true;
|
|
m_phase = Phase::IDLE;
|
|
m_phaseOwner = 0;
|
|
SetEvent(m_phaseIdle);
|
|
SetEvent(m_stoppedEvent);
|
|
}
|
|
}
|
|
|
|
void CTransportManager::SyncRecovery()
|
|
{
|
|
{
|
|
CSRWSharedLock managerLock(m_lock);
|
|
if (m_stopping || m_stopped)
|
|
return;
|
|
}
|
|
|
|
m_recovery.MarkMonitorReady();
|
|
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
std::shared_ptr<ITransport> transport;
|
|
bool call = false;
|
|
{
|
|
CSRWExclusiveLock entryLock(entry.lock);
|
|
if (entry.stopRequested || !entry.transport ||
|
|
!entry.recoveryAttached ||
|
|
(entry.state != State::INITIALIZED && entry.state != State::READY))
|
|
continue;
|
|
|
|
if (!m_recovery.ClaimSync(entry.id, entry.epoch))
|
|
continue;
|
|
|
|
if (entry.call != Call::IDLE)
|
|
{
|
|
entry.syncPending = true;
|
|
continue;
|
|
}
|
|
|
|
entry.call = Call::RECOVERY;
|
|
entry.callOwner = GetCurrentThreadId();
|
|
ResetEvent(entry.idleEvent);
|
|
transport = entry.transport;
|
|
call = true;
|
|
}
|
|
|
|
if (call)
|
|
transport->SyncRecovery();
|
|
EndCall(entry, transport);
|
|
}
|
|
}
|
|
|
|
void CTransportManager::RecoveryStatus(uint64_t route, uint64_t session,
|
|
uint32_t serial, bool active,
|
|
Recovery state, uint32_t error)
|
|
{
|
|
RecoveryAction action;
|
|
action.route = route;
|
|
action.session = session;
|
|
action.serial = serial;
|
|
action.active = active;
|
|
m_recovery.Complete(action, state, error);
|
|
}
|
|
|
|
bool CTransportManager::CanUseMode(const FrameMode& mode,
|
|
uint32_t * requiredSizeMiB) const
|
|
{
|
|
if (requiredSizeMiB)
|
|
*requiredSizeMiB = 0;
|
|
|
|
std::shared_ptr<const FrameCaps> caps[FRAME_MAX_SINKS];
|
|
unsigned capsCount = 0;
|
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
|
const unsigned count = Entries(entries);
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Entry& entry = *entries[i];
|
|
CSRWSharedLock entryLock(entry.lock);
|
|
if (!entry.required ||
|
|
!(entry.config.services & TRANSPORT_SERVICE_FRAME))
|
|
continue;
|
|
if (!entry.frameCaps)
|
|
return false;
|
|
caps[capsCount++] = entry.frameCaps;
|
|
}
|
|
|
|
if (!capsCount)
|
|
return false;
|
|
|
|
bool supported = true;
|
|
for (unsigned i = 0; i < capsCount; ++i)
|
|
{
|
|
uint32_t hint = 0;
|
|
if (caps[i]->CanUseMode(mode, capsCount == 1 ? &hint : nullptr))
|
|
continue;
|
|
supported = false;
|
|
if (requiredSizeMiB && capsCount == 1)
|
|
*requiredSizeMiB = hint;
|
|
}
|
|
return supported;
|
|
}
|
|
|
|
DirectFrameBufferMemory CTransportManager::GetDirectMemory() const
|
|
{
|
|
CTransportManager * manager = const_cast<CTransportManager *>(this);
|
|
{
|
|
CSRWSharedLock managerLock(m_lock);
|
|
if (m_stopping || m_stopped)
|
|
return DirectFrameBufferMemory {};
|
|
}
|
|
|
|
Entry * primary = manager->Primary();
|
|
if (!primary)
|
|
return DirectFrameBufferMemory {};
|
|
|
|
{
|
|
CSRWSharedLock entryLock(primary->lock);
|
|
if (primary->directMemoryValid)
|
|
return primary->directMemory;
|
|
}
|
|
|
|
if (!manager->BeginPhase(Phase::ACCESS, true))
|
|
return DirectFrameBufferMemory {};
|
|
|
|
{
|
|
CSRWSharedLock entryLock(primary->lock);
|
|
if (primary->directMemoryValid)
|
|
{
|
|
const DirectFrameBufferMemory memory = primary->directMemory;
|
|
manager->EndPhase();
|
|
return memory;
|
|
}
|
|
}
|
|
|
|
if (!manager->BeginCall(*primary, Call::ACCESS, true))
|
|
{
|
|
manager->EndPhase();
|
|
return DirectFrameBufferMemory {};
|
|
}
|
|
|
|
std::shared_ptr<ITransport> transport;
|
|
State state;
|
|
{
|
|
CSRWSharedLock entryLock(primary->lock);
|
|
transport = primary->transport;
|
|
state = primary->state;
|
|
}
|
|
|
|
DirectFrameBufferMemory memory;
|
|
if (transport && state != State::FAILED && state != State::STOPPED)
|
|
memory = transport->GetDirectMemory();
|
|
{
|
|
CSRWExclusiveLock entryLock(primary->lock);
|
|
primary->directMemory = memory;
|
|
primary->directMemoryValid = true;
|
|
primary->exposed = true;
|
|
primary->exposedTransport = transport;
|
|
}
|
|
|
|
manager->EndCall(*primary, transport);
|
|
manager->EndPhase();
|
|
return memory;
|
|
}
|
|
|
|
IFrameTransport& CTransportManager::Frames()
|
|
{
|
|
Entry * primary = Primary();
|
|
if (!primary)
|
|
return m_frames;
|
|
|
|
if (BeginPhase(Phase::ACCESS, true))
|
|
{
|
|
Expose(*primary);
|
|
EndPhase();
|
|
}
|
|
return m_frames;
|
|
}
|
|
|
|
IControlTransport& CTransportManager::Control()
|
|
{
|
|
return m_control;
|
|
}
|
|
|
|
IInputTransport& CTransportManager::Input()
|
|
{
|
|
return m_input;
|
|
}
|