Coverage Report

Created: 2026-09-24 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpowerd/callback_manager.cc
Line
Count
Source
1
#include "callback_manager.h"
2
3
#include <memory>
4
#include <utility>
5
#include <vector>
6
7
#include "absl/functional/any_invocable.h"
8
#include "absl/synchronization/mutex.h"
9
10
namespace safepower_agent {
11
12
CallbackManager::Handle::Handle(std::shared_ptr<HandleImpl> impl)
13
0
    : impl_(std::move(impl)) {}
14
15
0
bool CallbackManager::Handle::pending() const {
16
0
  if (impl_ == nullptr) return false;
17
0
  absl::MutexLock lock(&impl_->mutex);
18
0
  return impl_->state == State::kPending || impl_->state == State::kExecuting;
19
0
}
20
21
0
bool CallbackManager::Handle::executed() const {
22
0
  if (impl_ == nullptr) return false;
23
0
  absl::MutexLock lock(&impl_->mutex);
24
0
  return impl_->state == State::kExecuted;
25
0
}
26
27
0
bool CallbackManager::Handle::TryCancel() {
28
0
  if (impl_ == nullptr) return false;
29
0
  absl::MutexLock lock(&impl_->mutex);
30
0
  if (impl_->state == State::kPending) {
31
0
    impl_->callback = nullptr;
32
0
    impl_->state = State::kCancelled;
33
0
    return true;
34
0
  }
35
0
  return false;
36
0
}
37
38
0
void CallbackManager::Handle::Wait() {
39
0
  if (impl_ == nullptr) return;
40
0
  absl::MutexLock lock(&impl_->mutex);
41
0
  impl_->mutex.Await(absl::Condition(
42
0
      +[](State* state) {
43
0
        return *state == State::kCancelled || *state == State::kExecuted;
44
0
      },
45
0
      &impl_->state));
46
0
}
47
48
49
0
CallbackManager::Handle CallbackManager::RunFirst(Callback callback) {
50
0
  auto impl = std::make_shared<HandleImpl>();
51
0
  impl->callback = std::move(callback);
52
0
  {
53
0
    absl::MutexLock lock(&mutex_);
54
0
    run_first_.push_back(impl);
55
0
  }
56
0
  return Handle(std::move(impl));
57
0
}
58
59
0
CallbackManager::Handle CallbackManager::RunLast(Callback callback) {
60
0
  auto impl = std::make_shared<HandleImpl>();
61
0
  impl->callback = std::move(callback);
62
0
  {
63
0
    absl::MutexLock lock(&mutex_);
64
0
    run_last_.push_back(impl);
65
0
  }
66
0
  return Handle(std::move(impl));
67
0
}
68
69
void CallbackManager::RunCallbackIfValid(
70
0
    const std::weak_ptr<HandleImpl>& weak_handle) {
71
0
  Callback cb;
72
0
  std::shared_ptr<HandleImpl> handle = weak_handle.lock();
73
0
  if (handle) {
74
0
    {
75
0
      absl::MutexLock lock(&handle->mutex);
76
0
      if (handle->state == State::kPending) {
77
0
        cb = std::exchange(handle->callback, nullptr);
78
0
        if (!cb) {
79
          // If the callback was registered as empty/nullptr, transition
80
          // directly to kExecuted so that we don't get stuck in kExecuting.
81
0
          handle->state = State::kExecuted;
82
0
          return;
83
0
        }
84
0
        handle->state = State::kExecuting;
85
0
      }
86
0
    }
87
0
  }
88
0
  if (cb) {
89
0
    std::move(cb)();
90
0
    absl::MutexLock lock(&handle->mutex);
91
0
    handle->state = State::kExecuted;
92
0
  }
93
0
}
94
95
0
void CallbackManager::RunCallbacks() {
96
0
  std::vector<std::weak_ptr<HandleImpl>> first_snapshot;
97
0
  std::vector<std::weak_ptr<HandleImpl>> last_snapshot;
98
0
  {
99
0
    absl::MutexLock lock(&mutex_);
100
0
    first_snapshot = std::move(run_first_);
101
0
    last_snapshot = std::move(run_last_);
102
0
    run_first_.clear();
103
0
    run_last_.clear();
104
0
  }
105
106
  // Run first callbacks in reverse (LIFO) order
107
0
  for (auto it = first_snapshot.rbegin(); it != first_snapshot.rend(); ++it) {
108
0
    RunCallbackIfValid(*it);
109
0
  }
110
111
  // Run last callbacks in forward (FIFO) order
112
0
  for (auto it = last_snapshot.begin(); it != last_snapshot.end(); ++it) {
113
0
    RunCallbackIfValid(*it);
114
0
  }
115
0
}
116
117
}  // namespace safepower_agent