Quantum Resource Manager and Compiler Infrastructure (QRM&CI) 0.1.0
CommunicationHandler.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026 MQSS Maintainers
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 */
7
11
12#pragma once
13
14#include "mqss/Messenger.hpp"
15#include "mqss/Protocol.hpp"
16#include "mqss/Transport.hpp"
17#include "qrmci/Config.h"
18#include "qrmci/Error.h"
19
20#include <atomic>
21#include <chrono>
22#include <expected>
23#include <optional>
24#include <spdlog/spdlog.h>
25#include <string>
26#include <utility>
27
28namespace mqss::qrmci {
29
32inline constexpr std::chrono::milliseconds DefaultReceiveTimeout{500};
33
41inline constexpr std::chrono::milliseconds WaitForever{-1};
42
47[[nodiscard]] mqss::TransportOptions<mqss::RabbitMqSimple>
49
53inline std::string messageLabel(const mqss::QuantumTask &task) {
54 return "quantum task " + std::to_string(task.task_id());
55}
56
60inline std::string messageLabel(const mqss::QuantumResult &result) {
61 return "quantum result for task " + std::to_string(result.task_id());
62}
63
67inline std::string messageLabel(const mqss::Backend &backend) {
68 return "backend status for backend " + backend.name();
69}
70
75template <class Message>
76std::string messageLabel(const Message & /*unused*/) = delete;
77
88public:
92
99 CommunicationHandler &operator=(const CommunicationHandler &) = delete;
101 CommunicationHandler &operator=(CommunicationHandler &&) = delete;
102
111 template <class Message>
112 [[nodiscard]] std::expected<void, Error> send(const Message &message,
113 const std::string &queueName) {
114 auto sendStatus = messenger.send<Message>({queueName}, message);
115
116 if (!sendStatus.ok()) {
117 return std::unexpected(Error{Error::Kind::MessagingFailed,
118 "Failed to send " + messageLabel(message) +
119 ": " + sendStatus.reason()});
120 }
121 return {};
122 }
123
141 template <class Message>
142 [[nodiscard]] std::expected<std::optional<Message>, Error>
143 receive(const std::string &queueName, std::chrono::milliseconds timeout,
144 const std::atomic<bool> &terminationFlag) {
145 const bool waitIndefinitely = (timeout == WaitForever);
146 const auto pollInterval =
147 waitIndefinitely ? DefaultReceiveTimeout : timeout;
148
149 // The flag is the loop condition rather than a second conjunct alongside
150 // the timeout, so it is honoured at every timeout instead of only at 0.
151 while (!terminationFlag.load(std::memory_order_acquire)) {
152 // Manual ack keeps the broker-side no_ack flag false, so RabbitMQ's
153 // per-consumer prefetch actually throttles delivery instead of pushing
154 // every queued message onto this call's short-lived connection at once.
155 // The message is acked right after a successful decode.
156 auto received = messenger.receiveExtended<Message>(
157 {queueName}, mqss::ReceiveArgs{
158 .timeout = pollInterval,
159 .ack_mode = mqss::AckMode::Manual,
160 });
161 if (received.has_value()) {
162 auto &[message, delivery] = *received;
163 if (auto ackStatus = delivery.ack(); !ackStatus.ok()) {
164 spdlog::warn("Failed to ack {}: {}", messageLabel(message),
165 ackStatus.reason());
166 }
167 return std::optional<Message>(std::move(message));
168 }
169 if (received.error().code() != mqss::StatusCode::Timeout) {
170 return std::unexpected(
172 "Receive error: " + received.error().reason()});
173 }
174 if (!waitIndefinitely) {
175 break;
176 }
177 }
178 return std::optional<Message>{};
179 }
180
196 template <class Message>
197 [[nodiscard]] std::expected<std::optional<Message>, Error>
198 receive(const std::string &queueName,
199 std::chrono::milliseconds timeout = DefaultReceiveTimeout) {
200 static const std::atomic<bool> NeverTerminates{false};
201 return receive<Message>(queueName, timeout, NeverTerminates);
202 }
203
204private:
205 mqss::Messenger<mqss::RabbitMqSimple, mqss::ProtoJson> messenger;
206};
207} // namespace mqss::qrmci
mqss::TransportOptions< mqss::RabbitMqSimple > makeTransportOptions(const RabbitMqConnectionConfig &config)
Translate a RabbitMqConnectionConfig into the transport's own options.
constexpr std::chrono::milliseconds WaitForever
Timeout value meaning "poll until a message arrives or the termination flag is set" rather than...
constexpr std::chrono::milliseconds DefaultReceiveTimeout
How long a single receive attempt waits when the caller does not say, and the interval an indefinite ...
std::string messageLabel(const mqss::QuantumTask &task)
Describe a quantum task for an error or log line.
The runtime configuration model shared by every QRM&CI daemon.
The error type every std::expected in QRM&CI's public interfaces carries.
CommunicationHandler(const RabbitMqConnectionConfig &config)
Construct a CommunicationHandler with the given configuration.
CommunicationHandler(const CommunicationHandler &)=delete
Explicitly non-copyable and non-movable: a moved-from handler would leave its messenger's transport c...
std::expected< std::optional< Message >, Error > receive(const std::string &queueName, std::chrono::milliseconds timeout=DefaultReceiveTimeout)
Retrieve the next message of the given type, with no termination flag to interrupt the wait.
std::expected< std::optional< Message >, Error > receive(const std::string &queueName, std::chrono::milliseconds timeout, const std::atomic< bool > &terminationFlag)
Retrieve the next message of the given type from the specified queue.
std::expected< void, Error > send(const Message &message, const std::string &queueName)
Send a message to the specified queue.
A failure reported by a QRM&CI operation: a kind the caller can act on, plus a human-readable detail ...
Definition Error.h:29
@ MessagingFailed
A messaging operation against the broker failed.
Definition Error.h:46
RabbitMQ connection settings, shared by every stage's messaging.
Definition Config.h:37