1 // Copyright 2014 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 5 #include "components/timers/alarm_timer_chromeos.h" 6 7 #include <stdint.h> 8 #include <sys/timerfd.h> 9 10 #include <algorithm> 11 #include <memory> 12 #include <utility> 13 14 #include "base/bind.h" 15 #include "base/debug/task_annotator.h" 16 #include "base/files/file_util.h" 17 #include "base/logging.h" 18 #include "base/pending_task.h" 19 #include "base/trace_event/trace_event.h" 20 21 namespace timers { 22 23 SimpleAlarmTimer::SimpleAlarmTimer() 24 : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)), weak_factory_(this) {} 25 26 SimpleAlarmTimer::~SimpleAlarmTimer() { 27 DCHECK(origin_task_runner_->RunsTasksInCurrentSequence()); 28 Stop(); 29 } 30 31 void SimpleAlarmTimer::Stop() { 32 DCHECK(origin_task_runner_->RunsTasksInCurrentSequence()); 33 34 if (!IsRunning()) 35 return; 36 37 if (!CanWakeFromSuspend()) { 38 base::RetainingOneShotTimer::Stop(); 39 return; 40 } 41 42 // Cancel any previous callbacks. 43 weak_factory_.InvalidateWeakPtrs(); 44 45 base::RetainingOneShotTimer::set_is_running(false); 46 alarm_fd_watcher_.reset(); 47 pending_task_.reset(); 48 } 49 50 void SimpleAlarmTimer::Reset() { 51 DCHECK(origin_task_runner_->RunsTasksInCurrentSequence()); 52 DCHECK(!base::RetainingOneShotTimer::user_task().is_null()); 53 54 if (!CanWakeFromSuspend()) { 55 base::RetainingOneShotTimer::Reset(); 56 return; 57 } 58 59 // Cancel any previous callbacks and stop watching |alarm_fd_|. 60 weak_factory_.InvalidateWeakPtrs(); 61 alarm_fd_watcher_.reset(); 62 63 // Ensure that the delay is not negative. 64 const base::TimeDelta delay = std::max( 65 base::TimeDelta(), base::RetainingOneShotTimer::GetCurrentDelay()); 66 67 // Set up the pending task. 68 base::RetainingOneShotTimer::set_desired_run_time( 69 delay.is_zero() ? base::TimeTicks() : base::TimeTicks::Now() + delay); 70 pending_task_ = std::make_unique<base::PendingTask>( 71 base::RetainingOneShotTimer::posted_from(), 72 base::RetainingOneShotTimer::user_task(), 73 base::RetainingOneShotTimer::desired_run_time()); 74 75 // Set |alarm_fd_| to be signaled when the delay expires. If the delay is 76 // zero, |alarm_fd_| will never be signaled. This overrides the previous 77 // delay, if any. 78 itimerspec alarm_time = {}; 79 alarm_time.it_value.tv_sec = delay.InSeconds(); 80 alarm_time.it_value.tv_nsec = 81 (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) * 82 base::Time::kNanosecondsPerMicrosecond; 83 if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0) 84 PLOG(ERROR) << "Error while setting alarm time. Timer will not fire"; 85 86 // The timer is running. 87 base::RetainingOneShotTimer::set_is_running(true); 88 89 // If the delay is zero, post the task now. 90 if (delay.is_zero()) { 91 origin_task_runner_->PostTask( 92 FROM_HERE, base::BindOnce(&SimpleAlarmTimer::OnTimerFired, 93 weak_factory_.GetWeakPtr())); 94 } else { 95 // Otherwise, if the delay is not zero, generate a tracing event to indicate 96 // that the task was posted and watch |alarm_fd_|. 97 base::debug::TaskAnnotator().WillQueueTask("SimpleAlarmTimer::Reset", 98 pending_task_.get()); 99 alarm_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable( 100 alarm_fd_, 101 base::BindRepeating(&SimpleAlarmTimer::OnAlarmFdReadableWithoutBlocking, 102 weak_factory_.GetWeakPtr())); 103 } 104 } 105 106 void SimpleAlarmTimer::OnAlarmFdReadableWithoutBlocking() { 107 DCHECK(origin_task_runner_->RunsTasksInCurrentSequence()); 108 DCHECK(base::RetainingOneShotTimer::IsRunning()); 109 110 // Read from |alarm_fd_| to ack the event. 111 char val[sizeof(uint64_t)]; 112 if (!base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t))) 113 PLOG(DFATAL) << "Unable to read from timer file descriptor."; 114 115 OnTimerFired(); 116 } 117 118 void SimpleAlarmTimer::OnTimerFired() { 119 DCHECK(origin_task_runner_->RunsTasksInCurrentSequence()); 120 DCHECK(base::RetainingOneShotTimer::IsRunning()); 121 DCHECK(pending_task_.get()); 122 123 // Take ownership of the PendingTask to prevent it from being deleted if the 124 // SimpleAlarmTimer is deleted. 125 const auto pending_user_task = std::move(pending_task_); 126 127 base::WeakPtr<SimpleAlarmTimer> weak_ptr = weak_factory_.GetWeakPtr(); 128 129 // Run the task. 130 TRACE_TASK_EXECUTION("SimpleAlarmTimer::OnTimerFired", *pending_user_task); 131 base::debug::TaskAnnotator().RunTask("SimpleAlarmTimer::Reset", 132 pending_user_task.get()); 133 134 // If the timer wasn't deleted, stopped or reset by the callback, stop it. 135 if (weak_ptr) 136 Stop(); 137 } 138 139 bool SimpleAlarmTimer::CanWakeFromSuspend() const { 140 return alarm_fd_ != -1; 141 } 142 143 } // namespace timers 144