activity_authority_query_owner.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #include "activity_authority_query_owner.h"
  2. #include <bit>
  3. #include <limits>
  4. namespace sunrise::server::bap::authority_query {
  5. namespace {
  6. namespace authority = middleware::bap::activity_message::entity_authority;
  7. /** The client answers on its next view tick; this bound permits a full activity load stall. */
  8. constexpr std::uint64_t kResponseTimeoutMs = 20'000;
  9. /** Advances through every signed token value except the client's -1 idle sentinel. */
  10. [[nodiscard]] std::int32_t reserve_correlation(Owner& owner) noexcept {
  11. std::int32_t reserved = owner.nextCorrelation;
  12. if (reserved == -1) {
  13. reserved = 0;
  14. }
  15. std::uint32_t next = std::bit_cast<std::uint32_t>(reserved) + 1U;
  16. if (next == (std::numeric_limits<std::uint32_t>::max)()) {
  17. next = 0;
  18. }
  19. owner.nextCorrelation = std::bit_cast<std::int32_t>(next);
  20. return reserved;
  21. }
  22. /** Adds a timeout interval without wrapping the monotonic tick count. */
  23. [[nodiscard]] std::uint64_t deadline_from(std::uint64_t now) noexcept {
  24. const std::uint64_t remaining = (std::numeric_limits<std::uint64_t>::max)() - now;
  25. return remaining < kResponseTimeoutMs ? (std::numeric_limits<std::uint64_t>::max)()
  26. : now + kResponseTimeoutMs;
  27. }
  28. } // namespace
  29. /** Cancels one old binding while preserving the connection's fresh-token sequence. */
  30. void reset(Owner& owner, std::uint64_t bindingGeneration) noexcept {
  31. const std::int32_t next = owner.nextCorrelation;
  32. owner = {};
  33. owner.bindingGeneration = bindingGeneration;
  34. owner.correlation = -1;
  35. owner.nextCorrelation = next == -1 ? 0 : next;
  36. }
  37. /** Opens one bounded query and allocates its non--1 correlation value. */
  38. ActivityAuthorityQueryStatus
  39. request(Owner& owner, std::uint64_t bindingGeneration, std::int32_t& correlation) noexcept {
  40. correlation = -1;
  41. if (bindingGeneration == 0 || owner.bindingGeneration != bindingGeneration) {
  42. return ActivityAuthorityQueryStatus::staleActivityClient;
  43. }
  44. if (owner.phase == Phase::pending || owner.phase == Phase::awaiting || owner.staged) {
  45. return ActivityAuthorityQueryStatus::busy;
  46. }
  47. owner.snapshot = {};
  48. owner.deadlineTick = 0;
  49. owner.correlation = reserve_correlation(owner);
  50. owner.phase = Phase::pending;
  51. owner.staged = false;
  52. correlation = owner.correlation;
  53. return ActivityAuthorityQueryStatus::queued;
  54. }
  55. /** Checks whether the exact binding has one msg-30 body waiting to be sent. */
  56. bool pending(const Owner& owner, std::uint64_t bindingGeneration) noexcept {
  57. return bindingGeneration != 0 && owner.bindingGeneration == bindingGeneration
  58. && owner.phase == Phase::pending && !owner.staged && owner.correlation != -1;
  59. }
  60. /** Marks the current pending query as present in a staged transport frame. */
  61. bool stage(Owner& owner, std::uint64_t bindingGeneration, std::int32_t& correlation) noexcept {
  62. correlation = -1;
  63. if (bindingGeneration == 0 || owner.bindingGeneration != bindingGeneration
  64. || owner.phase != Phase::pending || owner.staged || owner.correlation == -1) {
  65. return false;
  66. }
  67. owner.staged = true;
  68. correlation = owner.correlation;
  69. return true;
  70. }
  71. /** Starts the response timeout after the complete msg-30 frame reaches the caller. */
  72. void commit_staged(Owner& owner, std::uint64_t bindingGeneration, std::uint64_t now) noexcept {
  73. if (bindingGeneration == 0 || owner.bindingGeneration != bindingGeneration || !owner.staged
  74. || owner.phase != Phase::pending) {
  75. return;
  76. }
  77. owner.staged = false;
  78. owner.phase = Phase::awaiting;
  79. owner.deadlineTick = deadline_from(now);
  80. }
  81. /** Returns a staged msg-30 to pending when its frame is refused. */
  82. void discard_staged(Owner& owner, std::uint64_t bindingGeneration) noexcept {
  83. if (owner.bindingGeneration == bindingGeneration && owner.phase == Phase::pending) {
  84. owner.staged = false;
  85. }
  86. }
  87. /** Applies one correlated response before its closed deadline. */
  88. AnswerStatus submit(Owner& owner,
  89. std::uint64_t bindingGeneration,
  90. std::uint64_t now,
  91. const authority::QueryAnswer& answer) noexcept {
  92. if (bindingGeneration == 0 || owner.bindingGeneration != bindingGeneration) {
  93. return AnswerStatus::staleBinding;
  94. }
  95. if (owner.phase != Phase::awaiting) {
  96. return AnswerStatus::noQuery;
  97. }
  98. if (now >= owner.deadlineTick) {
  99. owner.deadlineTick = 0;
  100. owner.phase = Phase::timedOut;
  101. return AnswerStatus::timedOut;
  102. }
  103. if (answer.correlation != owner.correlation) {
  104. return AnswerStatus::tokenMismatch;
  105. }
  106. if (answer.hasSelector) {
  107. // Only the selector bound is checked. Mask content, arrival order, and repeats are the
  108. // client's to choose; a repeat overwrites its own row.
  109. if (!answer.hasMask
  110. || answer.selector >= static_cast<std::uint8_t>(owner.snapshot.bubbleMasks.size())) {
  111. return AnswerStatus::invalidShape;
  112. }
  113. if (!owner.snapshot.bubblePresent[answer.selector]) {
  114. owner.snapshot.bubblePresent[answer.selector] = true;
  115. ++owner.snapshot.bubbleResponseCount;
  116. }
  117. owner.snapshot.bubbleMasks[answer.selector] = answer.mask;
  118. owner.snapshot.lastBubble = answer.selector;
  119. return AnswerStatus::bubbleAccepted;
  120. }
  121. if (!answer.hasMask) {
  122. return AnswerStatus::invalidShape;
  123. }
  124. owner.snapshot.authorityMask = answer.mask;
  125. owner.snapshot.bindingGeneration = owner.bindingGeneration;
  126. owner.snapshot.correlation = owner.correlation;
  127. owner.snapshot.complete = true;
  128. owner.deadlineTick = 0;
  129. owner.phase = Phase::complete;
  130. return AnswerStatus::complete;
  131. }
  132. /** Closes an unanswered query once its bounded wait has elapsed. */
  133. bool expire(Owner& owner, std::uint64_t bindingGeneration, std::uint64_t now) noexcept {
  134. if (bindingGeneration == 0 || owner.bindingGeneration != bindingGeneration
  135. || owner.phase != Phase::awaiting || now < owner.deadlineTick) {
  136. return false;
  137. }
  138. owner.deadlineTick = 0;
  139. owner.phase = Phase::timedOut;
  140. return true;
  141. }
  142. /** Copies one complete stable result for a later API consumer. */
  143. ActivityAuthorityQueryStatus snapshot(const Owner& owner,
  144. std::uint64_t bindingGeneration,
  145. ActivityAuthorityQuerySnapshot& output) noexcept {
  146. output = {};
  147. if (bindingGeneration == 0 || owner.bindingGeneration != bindingGeneration) {
  148. return ActivityAuthorityQueryStatus::staleActivityClient;
  149. }
  150. switch (owner.phase) {
  151. case Phase::pending:
  152. case Phase::awaiting:
  153. return ActivityAuthorityQueryStatus::pending;
  154. case Phase::complete:
  155. output = owner.snapshot;
  156. return ActivityAuthorityQueryStatus::complete;
  157. case Phase::timedOut:
  158. return ActivityAuthorityQueryStatus::timedOut;
  159. case Phase::idle:
  160. return ActivityAuthorityQueryStatus::noResult;
  161. }
  162. return ActivityAuthorityQueryStatus::noResult;
  163. }
  164. } // namespace sunrise::server::bap::authority_query