blob: 8e4b22432decdde822c8a1f8c2faca6487df02ef [file]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HFT_CORE_MANAGER_FAKE_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HFT_CORE_MANAGER_FAKE_H_
#include <atomic>
#include <chrono> // NOLINT
#include <cstdint>
#include <memory>
#include <thread> // NOLINT
#include <utility>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
#include "payload.pb.h"
#include "subscription_params.pb.h"
#include "tlbmc/hft/core/manager.h"
namespace milotic_hft {
// A fake implementation of the subscription manager.
// This class is thread-compatible.
// Call `SetFakeData` before calling `AddSubscription`.
class SubscriptionManagerFake : public SubscriptionManager {
public:
void SetFakeData(Payload&& payload) { payload_ = std::move(payload); }
// The background thread will create fake data according to the subscription
// params and call the callback.
absl::StatusOr<std::shared_ptr<Subscription>> AddSubscription(
const SubscriptionParams& params,
absl::AnyInvocable<void(Payload&&)> on_data_callback) override {
absl::MutexLock lock(mutex_);
auto fake_subscription = std::make_shared<FakeSubscription>();
active_fake_subscriptions_.push_back(fake_subscription);
background_threads_.push_back(
std::thread([params, on_data_callback = std::move(on_data_callback),
fake_subscription, this]() mutable {
for (int32_t i = 0; i < params.num_batches(); ++i) {
if (fake_subscription->IsCancelled()) {
break;
}
// Sleep just for 1ms instead of `params.sampling_interval_ms()` to
// speed up the test.
std::this_thread::sleep_for(std::chrono::milliseconds(1));
Payload payload = payload_;
on_data_callback(std::move(payload));
}
}));
return fake_subscription;
}
absl::Status Unsubscribe(
const std::shared_ptr<Subscription>& subscription) override {
absl::MutexLock lock(mutex_);
auto fake_sub = std::dynamic_pointer_cast<FakeSubscription>(subscription);
if (!fake_sub) {
return absl::InvalidArgumentError(
"Subscription is not a FakeSubscription");
}
// Signal cancellation to the background thread.
fake_sub->Cancel();
// Remove from active_fake_subscriptions_.
for (auto it = active_fake_subscriptions_.begin();
it != active_fake_subscriptions_.end(); ++it) {
if (*it == fake_sub) {
active_fake_subscriptions_.erase(it);
break;
}
}
return absl::OkStatus();
}
~SubscriptionManagerFake() override {
// Ensure all background threads are signaled to stop.
absl::MutexLock lock(mutex_);
for (const auto& sub : active_fake_subscriptions_) {
sub->Cancel();
}
// Clear active_fake_subscriptions_ to release shared_ptrs.
active_fake_subscriptions_.clear();
// Join all threads. Threads should exit quickly after being cancelled.
for (std::thread& thread : background_threads_) {
if (thread.joinable()) {
thread.join();
}
}
}
private:
class FakeSubscription : public Subscription {
public:
// Signals the associated thread to stop.
void Cancel() {
if (!cancelled_.HasBeenNotified()) {
cancelled_.Notify();
}
}
// Returns true if the subscription has been cancelled.
bool IsCancelled() const { return cancelled_.HasBeenNotified(); }
private:
absl::Notification cancelled_;
};
Payload payload_;
absl::Mutex mutex_;
// Keeps track of active FakeSubscription shared pointers.
std::vector<std::shared_ptr<FakeSubscription>> active_fake_subscriptions_
ABSL_GUARDED_BY(mutex_);
// Stores the background threads. These are joined in the destructor.
std::vector<std::thread> ABSL_GUARDED_BY(mutex_) background_threads_;
};
} // namespace milotic_hft
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HFT_CORE_MANAGER_FAKE_H_