1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "os/fake_timer/fake_timerfd.h"
18 
19 #include <sys/eventfd.h>
20 #include <unistd.h>
21 
22 #include <cstdint>
23 #include <map>
24 
25 namespace bluetooth {
26 namespace os {
27 namespace fake_timer {
28 
29 class FakeTimerFd {
30 public:
31   int fd;
32   bool active;
33   uint64_t trigger_ms;
34   uint64_t period_ms;
35 };
36 
37 static std::map<int, FakeTimerFd*> fake_timers;
38 static uint64_t clock = 0;
39 static uint64_t max_clock = UINT64_MAX;
40 
timespec_to_ms(const timespec * t)41 static uint64_t timespec_to_ms(const timespec* t) {
42   return t->tv_sec * 1000 + t->tv_nsec / 1000000;
43 }
44 
fake_timerfd_create(int,int)45 int fake_timerfd_create(int /* clockid */, int /* flags */) {
46   int fd = eventfd(0, EFD_SEMAPHORE);
47   if (fd == -1) {
48     return fd;
49   }
50 
51   FakeTimerFd* entry = new FakeTimerFd();
52   fake_timers[fd] = entry;
53   entry->fd = fd;
54   return fd;
55 }
56 
fake_timerfd_settime(int fd,int,const struct itimerspec * new_value,struct itimerspec *)57 int fake_timerfd_settime(int fd, int /* flags */, const struct itimerspec* new_value,
58                          struct itimerspec* /* old_value */) {
59   if (fake_timers.find(fd) == fake_timers.end()) {
60     return -1;
61   }
62 
63   FakeTimerFd* entry = fake_timers[fd];
64 
65   uint64_t trigger_delta_ms = timespec_to_ms(&new_value->it_value);
66   entry->active = trigger_delta_ms != 0;
67   if (!entry->active) {
68     return 0;
69   }
70 
71   uint64_t period_ms = timespec_to_ms(&new_value->it_interval);
72   entry->trigger_ms = clock + trigger_delta_ms;
73   entry->period_ms = period_ms;
74   return 0;
75 }
76 
fake_timerfd_close(int fd)77 int fake_timerfd_close(int fd) {
78   auto timer_iterator = fake_timers.find(fd);
79   if (timer_iterator != fake_timers.end()) {
80     delete timer_iterator->second;
81     fake_timers.erase(timer_iterator);
82   }
83   return close(fd);
84 }
85 
fake_timerfd_reset()86 void fake_timerfd_reset() {
87   clock = 0;
88   max_clock = UINT64_MAX;
89   // if there are entries still here, it is a failure of our users to clean up
90   // so let them leak and trigger errors
91   fake_timers.clear();
92 }
93 
fire_next_event(uint64_t new_clock)94 static bool fire_next_event(uint64_t new_clock) {
95   uint64_t earliest_time = new_clock;
96   FakeTimerFd* to_fire = nullptr;
97   for (auto it = fake_timers.begin(); it != fake_timers.end(); it++) {
98     FakeTimerFd* entry = it->second;
99     if (!entry->active) {
100       continue;
101     }
102 
103     if (entry->trigger_ms > clock && entry->trigger_ms <= new_clock) {
104       if (to_fire == nullptr || entry->trigger_ms < earliest_time) {
105         to_fire = entry;
106         earliest_time = entry->trigger_ms;
107       }
108     }
109   }
110 
111   if (to_fire == nullptr) {
112     return false;
113   }
114 
115   bool is_periodic = to_fire->period_ms != 0;
116   if (is_periodic) {
117     to_fire->trigger_ms += to_fire->period_ms;
118   }
119   to_fire->active = is_periodic;
120   uint64_t value = 1;
121   (void)write(to_fire->fd, &value, sizeof(uint64_t));
122   return true;
123 }
124 
fake_timerfd_advance(uint64_t ms)125 void fake_timerfd_advance(uint64_t ms) {
126   uint64_t new_clock = clock + ms;
127   if (new_clock < clock) {
128     new_clock = max_clock;
129   }
130   while (fire_next_event(new_clock)) {
131   }
132   clock = new_clock;
133 }
134 
fake_timerfd_cap_at(uint64_t ms)135 void fake_timerfd_cap_at(uint64_t ms) { max_clock = ms; }
136 
fake_timerfd_get_clock()137 uint64_t fake_timerfd_get_clock() { return clock; }
138 
139 }  // namespace fake_timer
140 }  // namespace os
141 }  // namespace bluetooth
142