native_timer.cc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "chromeos/dbus/power/native_timer.h"
  5. #include <memory>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "base/callback_helpers.h"
  10. #include "base/files/file_descriptor_watcher_posix.h"
  11. #include "base/logging.h"
  12. #include "base/memory/scoped_refptr.h"
  13. #include "base/memory/weak_ptr.h"
  14. #include "base/posix/unix_domain_socket.h"
  15. #include "base/rand_util.h"
  16. #include "base/task/task_runner_util.h"
  17. #include "base/threading/platform_thread.h"
  18. #include "base/threading/sequenced_task_runner_handle.h"
  19. #include "base/time/time.h"
  20. #include "base/timer/timer.h"
  21. #include "chromeos/dbus/power/power_manager_client.h"
  22. namespace chromeos {
  23. namespace {
  24. // Value of |timer_id_| when it's not initialized.
  25. const PowerManagerClient::TimerId kNotCreatedId = -1;
  26. // Value of |timer_id_| when creation was attempted but failed.
  27. const PowerManagerClient::TimerId kErrorId = -2;
  28. } // namespace
  29. NativeTimer::NativeTimer(const std::string& tag)
  30. : timer_id_(kNotCreatedId), tag_(tag) {
  31. // Create a socket pair, one end will be sent to the power daemon the other
  32. // socket will be used to listen for the timer firing.
  33. base::ScopedFD powerd_fd;
  34. base::ScopedFD expiration_fd;
  35. base::CreateSocketPair(&powerd_fd, &expiration_fd);
  36. if (!powerd_fd.is_valid() || !expiration_fd.is_valid()) {
  37. LOG(ERROR) << "Invalid file descriptor";
  38. timer_id_ = kErrorId;
  39. return;
  40. }
  41. // Send create timer request to the power daemon.
  42. std::vector<std::pair<clockid_t, base::ScopedFD>> create_timers_request;
  43. create_timers_request.push_back(
  44. std::make_pair(CLOCK_BOOTTIME_ALARM, std::move(powerd_fd)));
  45. PowerManagerClient::Get()->CreateArcTimers(
  46. tag, std::move(create_timers_request),
  47. base::BindOnce(&NativeTimer::OnCreateTimer, weak_factory_.GetWeakPtr(),
  48. std::move(expiration_fd)));
  49. }
  50. NativeTimer::~NativeTimer() {
  51. // Delete the timer if it was created.
  52. if (timer_id_ < 0) {
  53. return;
  54. }
  55. PowerManagerClient::Get()->DeleteArcTimers(tag_, base::DoNothing());
  56. }
  57. struct NativeTimer::StartTimerParams {
  58. StartTimerParams() = default;
  59. StartTimerParams(base::TimeTicks absolute_expiration_time,
  60. base::OnceClosure timer_expiration_callback,
  61. OnStartNativeTimerCallback result_callback)
  62. : absolute_expiration_time(absolute_expiration_time),
  63. timer_expiration_callback(std::move(timer_expiration_callback)),
  64. result_callback(std::move(result_callback)) {}
  65. StartTimerParams(const StartTimerParams&) = delete;
  66. StartTimerParams& operator=(const StartTimerParams&) = delete;
  67. StartTimerParams(StartTimerParams&&) = default;
  68. ~StartTimerParams() = default;
  69. base::TimeTicks absolute_expiration_time;
  70. base::OnceClosure timer_expiration_callback;
  71. OnStartNativeTimerCallback result_callback;
  72. };
  73. void NativeTimer::Start(base::TimeTicks absolute_expiration_time,
  74. base::OnceClosure timer_expiration_callback,
  75. OnStartNativeTimerCallback result_callback) {
  76. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  77. // If the timer creation didn't succeed then tell the caller immediately.
  78. if (timer_id_ == kErrorId) {
  79. std::move(result_callback).Run(false);
  80. return;
  81. }
  82. // If the timer creation is in flight then save the parameters for this
  83. // method. |OnCreateTimer| will issue the start call.
  84. if (timer_id_ == kNotCreatedId) {
  85. // In normal scenarios of two back to back |Start| calls, the first one is
  86. // returned true in it's result callback and is overridden by the second
  87. // |Start| call. In the case of two back to back in flight |Start| calls
  88. // follow the same semantics and return true to the first caller.
  89. if (in_flight_start_timer_params_) {
  90. std::move(in_flight_start_timer_params_->result_callback).Run(true);
  91. }
  92. in_flight_start_timer_params_ = std::make_unique<StartTimerParams>(
  93. absolute_expiration_time, std::move(timer_expiration_callback),
  94. std::move(result_callback));
  95. return;
  96. }
  97. // Start semantics guarantee that it will override any old timer set. Reset
  98. // state to ensure this.
  99. ResetState();
  100. DCHECK_GE(timer_id_, 0);
  101. PowerManagerClient::Get()->StartArcTimer(
  102. timer_id_, absolute_expiration_time,
  103. base::BindOnce(&NativeTimer::OnStartTimer, weak_factory_.GetWeakPtr(),
  104. std::move(timer_expiration_callback),
  105. std::move(result_callback)));
  106. }
  107. void NativeTimer::OnCreateTimer(
  108. base::ScopedFD expiration_fd,
  109. absl::optional<std::vector<int32_t>> timer_ids) {
  110. DCHECK(expiration_fd.is_valid());
  111. if (!timer_ids.has_value()) {
  112. LOG(ERROR) << "No timers returned";
  113. timer_id_ = kErrorId;
  114. ProcessAndResetInFlightStartParams(false);
  115. return;
  116. }
  117. // Only one timer is being created.
  118. std::vector<int32_t> result = timer_ids.value();
  119. if (result.size() != 1) {
  120. LOG(ERROR) << "powerd created " << result.size() << " timers instead of 1";
  121. timer_id_ = kErrorId;
  122. ProcessAndResetInFlightStartParams(false);
  123. return;
  124. }
  125. // If timer creation failed and a |Start| call is pending then notify its
  126. // result callback an error.
  127. if (result[0] < 0) {
  128. LOG(ERROR) << "Error timer ID " << result[0];
  129. timer_id_ = kErrorId;
  130. ProcessAndResetInFlightStartParams(false);
  131. return;
  132. }
  133. // If timer creation succeeded and a |Start| call is pending then use the
  134. // stored parameters to schedule a timer.
  135. timer_id_ = result[0];
  136. expiration_fd_ = std::move(expiration_fd);
  137. ProcessAndResetInFlightStartParams(true);
  138. }
  139. void NativeTimer::OnStartTimer(base::OnceClosure timer_expiration_callback,
  140. OnStartNativeTimerCallback result_callback,
  141. bool result) {
  142. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  143. if (!result) {
  144. LOG(ERROR) << "Starting timer ID " << timer_id_ << " failed";
  145. std::move(result_callback).Run(false);
  146. return;
  147. }
  148. // At this point the timer has started, watch for its expiration and tell the
  149. // client that the start operation succeeded.
  150. timer_expiration_callback_ = std::move(timer_expiration_callback);
  151. expiration_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
  152. expiration_fd_.get(), base::BindRepeating(&NativeTimer::OnExpiration,
  153. weak_factory_.GetWeakPtr()));
  154. std::move(result_callback).Run(true);
  155. }
  156. void NativeTimer::OnExpiration() {
  157. // Write to the |expiration_fd_| to indicate to the instance that the timer
  158. // has expired. The instance expects 8 bytes on the read end similar to what
  159. // happens on a timerfd expiration. The timerfd API expects this to be the
  160. // number of expirations, however, more than one expiration isn't tracked
  161. // currently. This can block in the unlikely scenario of multiple writes
  162. // happening but the instance not reading the data. When the send queue is
  163. // full (64Kb), a write attempt here will block.
  164. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  165. DCHECK(expiration_fd_.is_valid());
  166. uint64_t timer_data;
  167. std::vector<base::ScopedFD> fds;
  168. if (!base::UnixDomainSocket::RecvMsg(expiration_fd_.get(), &timer_data,
  169. sizeof(timer_data), &fds)) {
  170. PLOG(ERROR) << "Bad data in expiration fd";
  171. }
  172. // If this isn't done then this task will keep running forever. Hence, clean
  173. // state regardless of any error above.
  174. ResetState();
  175. std::move(timer_expiration_callback_).Run();
  176. }
  177. void NativeTimer::ResetState() {
  178. weak_factory_.InvalidateWeakPtrs();
  179. expiration_fd_watcher_.reset();
  180. in_flight_start_timer_params_.reset();
  181. }
  182. void NativeTimer::ProcessAndResetInFlightStartParams(bool result) {
  183. if (!in_flight_start_timer_params_) {
  184. return;
  185. }
  186. // Run the result callback if |result| is false. Else schedule a timer with
  187. // the parameters stored.
  188. if (!result) {
  189. DCHECK_LT(timer_id_, 0);
  190. std::move(in_flight_start_timer_params_->result_callback).Run(false);
  191. in_flight_start_timer_params_.reset();
  192. return;
  193. }
  194. // The |in_flight_start_timer_params_->result_callback| will be called in
  195. // |OnStartTimer|.
  196. PowerManagerClient::Get()->StartArcTimer(
  197. timer_id_, in_flight_start_timer_params_->absolute_expiration_time,
  198. base::BindOnce(
  199. &NativeTimer::OnStartTimer, weak_factory_.GetWeakPtr(),
  200. std::move(in_flight_start_timer_params_->timer_expiration_callback),
  201. std::move(in_flight_start_timer_params_->result_callback)));
  202. // This state has been processed and must be reset to indicate that.
  203. in_flight_start_timer_params_.reset();
  204. }
  205. } // namespace chromeos