blob: 2fc549b6b1e518317613fc4eed7907c7cf029957 [file] [log] [blame]
#ifndef PRODUCTION_BORG_MGMT_NODE_PROXY_SAFEPOWER_SAFEPOWER_AGENT_DISRUPTION_MANAGER_H_
#define PRODUCTION_BORG_MGMT_NODE_PROXY_SAFEPOWER_SAFEPOWER_AGENT_DISRUPTION_MANAGER_H_
#include <list>
#include "absl/base/nullability.h"
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "source_location"
namespace safepower_agent {
class DisruptionManager {
struct PrivateToken {};
public:
using DisruptionCallback = absl::AnyInvocable<void() &&>;
DisruptionManager() = default;
DisruptionManager(const DisruptionManager&) = delete;
DisruptionManager& operator=(const DisruptionManager&) = delete;
DisruptionManager(DisruptionManager&&) = delete;
DisruptionManager& operator=(DisruptionManager&&) = delete;
class CallbackHandle {
public:
CallbackHandle() = default;
CallbackHandle(PrivateToken, absl::Mutex& mutex,
std::list<DisruptionCallback>& list,
std::list<DisruptionCallback>::iterator it);
CallbackHandle(CallbackHandle&& other);
CallbackHandle& operator=(CallbackHandle&& other);
~CallbackHandle();
private:
absl::Mutex* mutex_ = nullptr;
std::list<DisruptionCallback>* list_ = nullptr;
std::list<DisruptionCallback>::iterator it_;
};
// Indicates that a disruption is expected to happen in the near future.
// Returns an error if there is already a pending disruption.
absl::Status ExpectDisruptionIn(absl::Duration timeout)
ABSL_LOCKS_EXCLUDED(disruption_mutex_);
// Cancels the pending disruption. Returns an error if there is no pending
// disruption.
absl::Status CancelDisruption() ABSL_LOCKS_EXCLUDED(disruption_mutex_);
// Returns the remaining duration of the pending disruption, or zero if there
// is no pending disruption.
absl::Duration PendingDisruption() const
ABSL_LOCKS_EXCLUDED(disruption_mutex_);
// Runs a callback when a disruption is expected. The callback will be called
// at most once. The move-only returned handle can be destroyed to cancel the
// callback.
// The callback may be used to delay the disruption by
// ensuring that any required side effects have taken place before it returns.
[[nodiscard]] CallbackHandle OnDisruptionStart(DisruptionCallback callback)
ABSL_LOCKS_EXCLUDED(disruption_mutex_);
// Runs a callback when a disruption is no longer expected because it was
// cancelled or because there was a timeout. The callback will be called at
// most once. The move-only returned handle can be destroyed to cancel the
// callback.
[[nodiscard]] CallbackHandle OnDisruptionEnd(DisruptionCallback callback)
ABSL_LOCKS_EXCLUDED(disruption_mutex_);
private:
absl::Status RunCallbacks(
std::list<DisruptionCallback>& list, absl::Duration delay,
std::source_location location = std::source_location::current());
CallbackHandle OnDisruptionStartHelper(DisruptionCallback callback)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(disruption_mutex_);
CallbackHandle OnDisruptionEndHelper(DisruptionCallback callback)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(disruption_mutex_);
mutable absl::Mutex disruption_mutex_;
absl::Time expect_disruption_until_ ABSL_GUARDED_BY(disruption_mutex_) =
absl::InfinitePast();
std::list<DisruptionCallback> disruption_start_callbacks_
ABSL_GUARDED_BY(disruption_mutex_);
std::list<DisruptionCallback> disruption_end_callbacks_
ABSL_GUARDED_BY(disruption_mutex_);
};
} // namespace safepower_agent
#endif // PRODUCTION_BORG_MGMT_NODE_PROXY_SAFEPOWER_SAFEPOWER_AGENT_DISRUPTION_MANAGER_H_