[idd] scheduler: reserve slots for frame subscribers

Replace the oldest provisional schedule entry when the bounded client
table has no empty slot. Never evict a client whose frame subscription has
already been observed.

This prevents rapid failed reconnects from filling every scheduler slot
before a real frame subscriber can be registered.
This commit is contained in:
Geoffrey McRae
2026-08-06 16:57:12 +10:00
parent ece6afd74e
commit 3e9dadf340
2 changed files with 37 additions and 18 deletions

View File

@@ -84,6 +84,40 @@ CFrameScheduler::Client * CFrameScheduler::FindClient(uint32_t clientID)
return nullptr;
}
CFrameScheduler::Client * CFrameScheduler::FindOrAllocateClient(
uint32_t clientID)
{
Client * client = FindClient(clientID);
if (client || !clientID)
return client;
Client * replacement = nullptr;
for (Client& candidate : m_clients)
{
if (!candidate.clientID)
{
replacement = &candidate;
break;
}
// A schedule can arrive before its frame subscriptions are visible.
// Such provisional entries must not prevent a real subscriber from
// obtaining one of the bounded scheduler slots.
if (candidate.subscribed || candidate.subscriptionSeen)
continue;
if (!replacement || candidate.expiry < replacement->expiry)
replacement = &candidate;
}
if (replacement)
{
*replacement = {};
replacement->clientID = clientID;
}
return replacement;
}
CFrameScheduler::Publication * CFrameScheduler::FindPublication(
const Schedule& schedule, uint32_t frameSerial)
{
@@ -272,15 +306,7 @@ void CFrameScheduler::UpdateSubscribers(const uint32_t * clientIDs,
for (unsigned i = 0; i < count; ++i)
{
Client * client = FindClient(clientIDs[i]);
if (!client)
for (Client& candidate : m_clients)
if (!candidate.clientID)
{
candidate.clientID = clientIDs[i];
client = &candidate;
break;
}
Client * client = FindOrAllocateClient(clientIDs[i]);
if (client)
{
@@ -362,15 +388,7 @@ bool CFrameScheduler::UpdateSchedule(uint32_t sourceClientID,
return false;
AcquireSRWLockExclusive(&m_lock);
Client * client = FindClient(schedule.clientID);
if (!client)
for (Client& candidate : m_clients)
if (!candidate.clientID)
{
candidate.clientID = schedule.clientID;
client = &candidate;
break;
}
Client * client = FindOrAllocateClient(schedule.clientID);
if (!client)
{

View File

@@ -114,6 +114,7 @@ private:
uint64_t m_lastLogPublished = 0;
Client * FindClient(uint32_t clientID);
Client * FindOrAllocateClient(uint32_t clientID);
Publication * FindPublication(const Schedule& schedule,
uint32_t frameSerial);
bool ElectOwner(uint64_t now, uint32_t resetClientID = 0);