Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ if(BUILD_TESTING)
src/ui/ExpandingPromptEditor.h
src/ui/InspectorWidget.cpp
src/ui/InspectorWidget.h
src/ui/PresentationRefreshAccumulator.cpp
src/ui/PresentationRefreshAccumulator.h
src/ui/UpcomingTurnDock.cpp
src/ui/UpcomingTurnDock.h
)
Expand Down
22 changes: 21 additions & 1 deletion src/app/FrontendSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,25 @@ void mergeScope(detail::StateUpdateScope& destination,
if (!appendUniqueBounded(destination.affectedThreadIds,
source.affectedThreadIds)
|| !appendUniqueBounded(destination.fullyAffectedThreadIds,
source.fullyAffectedThreadIds))
source.fullyAffectedThreadIds)) {
destination.allThreadsAffected = true;
} else {
for (const QString& threadId : destination.fullyAffectedThreadIds)
destination.structurallyAffectedThreadIds.removeAll(threadId);
for (const QString& threadId :
source.structurallyAffectedThreadIds) {
if (destination.fullyAffectedThreadIds.contains(threadId)
|| destination.structurallyAffectedThreadIds.contains(
threadId))
continue;
if (destination.structurallyAffectedThreadIds.size()
>= detail::maximumCoalescedPresentationIdentities) {
destination.allThreadsAffected = true;
break;
}
destination.structurallyAffectedThreadIds.push_back(threadId);
}
}
}
if (!destination.allInspectorsAffected
&& !appendUniqueBounded(destination.affectedInspectorThreadIds,
Expand Down Expand Up @@ -165,6 +182,8 @@ void mergeScope(detail::StateUpdateScope& destination,
> detail::maximumCoalescedPresentationIdentities
|| destination.fullyAffectedThreadIds.size()
> detail::maximumCoalescedPresentationIdentities
|| destination.structurallyAffectedThreadIds.size()
> detail::maximumCoalescedPresentationIdentities
|| destination.removedThreadIds.size()
> detail::maximumCoalescedPresentationIdentities
|| static_cast<qsizetype>(destination.affectedItemContents.size())
Expand All @@ -182,6 +201,7 @@ void mergeScope(detail::StateUpdateScope& destination,
if (destination.allThreadsAffected) {
destination.affectedThreadIds.clear();
destination.fullyAffectedThreadIds.clear();
destination.structurallyAffectedThreadIds.clear();
// Keep exact removals even when the rest of the presentation scope
// degrades to an all-thread refresh. Global omission provenance makes
// a missing selected ID ambiguous without this bounded evidence.
Expand Down
3 changes: 3 additions & 0 deletions src/app/FrontendSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ struct StateUpdateScope {

QStringList affectedThreadIds;
QStringList fullyAffectedThreadIds;
// Pure descendant additions: always a subset of affectedThreadIds. A
// full/deletion-capable scope for the same thread always dominates.
QStringList structurallyAffectedThreadIds;
// Exact authoritative removals must survive mailbox coalescing. An
// omitted thread is otherwise indistinguishable from one deleted by an
// authoritative thread/read while the global snapshot remains bounded.
Expand Down
35 changes: 29 additions & 6 deletions src/app/FrontendSessionWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,26 @@ StateUpdateScope stateUpdateScope(const sdk::StateUpdate& update)
};
const auto addFullyAffectedThread = [&scope, &addThread, &addUnique](std::string_view id) {
addThread(id);
if (!scope.allThreadsAffected
&& !addUnique(scope.fullyAffectedThreadIds, id))
if (scope.allThreadsAffected)
return;
if (!addUnique(scope.fullyAffectedThreadIds, id)) {
scope.allThreadsAffected = true;
return;
}
scope.structurallyAffectedThreadIds.removeAll(
QString::fromUtf8(id.data(), static_cast<qsizetype>(id.size())));
};
const auto addStructurallyAffectedThread =
[&scope, &addThread, &addUnique](std::string_view id) {
addThread(id);
if (scope.allThreadsAffected)
return;
const QString threadId = QString::fromUtf8(
id.data(), static_cast<qsizetype>(id.size()));
if (scope.fullyAffectedThreadIds.contains(threadId))
return;
if (!addUnique(scope.structurallyAffectedThreadIds, id))
scope.allThreadsAffected = true;
};
const auto addInspectorThread = [&scope, &addUnique](std::string_view id) {
if (!scope.allInspectorsAffected
Expand Down Expand Up @@ -213,11 +230,16 @@ StateUpdateScope stateUpdateScope(const sdk::StateUpdate& update)
}
else if constexpr (std::is_same_v<Change, sdk::ItemUpsertedChange>)
{
if (value.threadId)
markThreadAndInspector(value.threadId->value);
if (value.threadId) {
addStructurallyAffectedThread(value.threadId->value);
addInspectorThread(value.threadId->value);
}
else if (value.turnId) {
if (const auto* turn = update.state.turn(*value.turnId))
markThreadAndInspector(turn->threadId.value);
if (const auto* turn = update.state.turn(*value.turnId)) {
addStructurallyAffectedThread(
turn->threadId.value);
addInspectorThread(turn->threadId.value);
}
else {
scope.allThreadsAffected = true;
scope.allInspectorsAffected = true;
Expand Down Expand Up @@ -308,6 +330,7 @@ StateUpdateScope stateUpdateScope(const sdk::StateUpdate& update)
if (scope.allThreadsAffected) {
scope.affectedThreadIds.clear();
scope.fullyAffectedThreadIds.clear();
scope.structurallyAffectedThreadIds.clear();
scope.affectedItemContents.clear();
scope.coalescedContentDeltaBytes = 0;
}
Expand Down
Loading