blob: d3de37bec5d18258a16f2f53120467030f5d3647 [file] [log] [blame]
#include "callback_manager.h"
#include <iterator>
#include <list>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/synchronization/mutex.h"
namespace safepower_agent {
CallbackManager::Handle::Handle(PrivateToken, CallbackManager& manager,
std::list<Callback>::iterator it)
: manager_(&manager), it_(it) {}
CallbackManager::Handle::~Handle() {
if (manager_ == nullptr) return;
absl::MutexLock lock(manager_->mutex_);
manager_->callbacks_.erase(it_);
}
CallbackManager::Handle::Handle(Handle&& other) {
manager_ = other.manager_;
it_ = other.it_;
other.manager_ = nullptr;
}
CallbackManager::Handle& CallbackManager::Handle::operator=(Handle&& other) {
if (manager_ != nullptr) {
absl::MutexLock lock(manager_->mutex_);
manager_->callbacks_.erase(it_);
}
manager_ = other.manager_;
it_ = other.it_;
other.manager_ = nullptr;
return *this;
}
bool CallbackManager::Handle::pending() const {
if (manager_ == nullptr) return false;
absl::MutexLock lock_running(manager_->running_mutex_);
absl::MutexLock lock(manager_->mutex_);
return *it_ != nullptr;
}
bool CallbackManager::Handle::TryCancel() {
if (manager_ == nullptr) return false;
absl::MutexLock lock_running(manager_->running_mutex_);
absl::MutexLock lock(manager_->mutex_);
if (*it_ == nullptr) return false;
*it_ = nullptr;
return true;
}
CallbackManager::~CallbackManager() {
if (!mutex_.try_lock()) {
LOG(DFATAL) << "CallbackManager destroyed while mutex was held";
return;
}
for (Callback& callback : callbacks_) {
if (callback) {
LOG(DFATAL) << "CallbackManager destroyed with pending callbacks";
break;
}
}
mutex_.unlock();
}
CallbackManager::Handle CallbackManager::RunFirst(Callback callback) {
absl::MutexLock lock(mutex_);
callbacks_.push_front(std::move(callback));
return Handle({}, *this, callbacks_.begin());
}
CallbackManager::Handle CallbackManager::RunLast(Callback callback) {
absl::MutexLock lock(mutex_);
callbacks_.push_back(std::move(callback));
return Handle({}, *this, std::prev(callbacks_.end()));
}
void CallbackManager::RunCallbacks() {
// We need to move the callbacks out of the list because we will run them
// outside of the lock. This allows the callback to install / remove other
// callbacks.
std::vector<Callback> callbacks_to_run;
{
absl::MutexLock lock(mutex_);
for (auto& callback : callbacks_) {
if (callback) {
callbacks_to_run.push_back(std::move(callback));
}
}
}
absl::MutexLock lock(running_mutex_);
for (auto& callback : callbacks_to_run) {
std::move(callback)();
}
}
} // namespace safepower_agent