blob: 5b7389beba8a0c129e44a3c972717b07a32941f4 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "os/fuzz/fake_timerfd.h"
#include <sys/eventfd.h>
#include <unistd.h>
#include <map>
namespace bluetooth {
namespace os {
namespace fuzz {
class FakeTimerFd {
public:
int fd;
bool active;
uint64_t trigger_ms;
uint64_t period_ms;
};
static std::map<int, FakeTimerFd*> fake_timers;
static uint64_t clock = 0;
static uint64_t max_clock = UINT64_MAX;
static uint64_t timespec_to_ms(const timespec* t) {
return t->tv_sec * 1000 + t->tv_nsec / 1000000;
}
int fake_timerfd_create(int clockid, int flags) {
int fd = eventfd(0, 0);
if (fd == -1) {
return fd;
}
FakeTimerFd* entry = new FakeTimerFd();
fake_timers[fd] = entry;
entry->fd = fd;
return fd;
}
int fake_timerfd_settime(int fd, int flags, const struct itimerspec* new_value, struct itimerspec* old_value) {
if (fake_timers.find(fd) == fake_timers.end()) {
return -1;
}
FakeTimerFd* entry = fake_timers[fd];
uint64_t trigger_delta_ms = timespec_to_ms(&new_value->it_value);
entry->active = trigger_delta_ms != 0;
if (!entry->active) {
return 0;
}
uint64_t period_ms = timespec_to_ms(&new_value->it_value);
entry->trigger_ms = clock + trigger_delta_ms;
entry->period_ms = period_ms;
return 0;
}
int fake_timerfd_close(int fd) {
auto timer_iterator = fake_timers.find(fd);
if (timer_iterator != fake_timers.end()) {
delete timer_iterator->second;
fake_timers.erase(timer_iterator);
}
return close(fd);
}
void fake_timerfd_reset() {
clock = 0;
max_clock = UINT64_MAX;
// if there are entries still here, it is a failure of our users to clean up
// so let them leak and trigger errors
fake_timers.clear();
}
static bool fire_next_event(uint64_t new_clock) {
uint64_t earliest_time = new_clock;
FakeTimerFd* to_fire = nullptr;
for (auto it = fake_timers.begin(); it != fake_timers.end(); it++) {
FakeTimerFd* entry = it->second;
if (!entry->active) {
continue;
}
if (entry->trigger_ms > clock && entry->trigger_ms <= new_clock) {
if (to_fire == nullptr || entry->trigger_ms < earliest_time) {
to_fire = entry;
earliest_time = entry->trigger_ms;
}
}
}
if (to_fire == nullptr) {
return false;
}
bool is_periodic = to_fire->period_ms != 0;
if (is_periodic) {
to_fire->trigger_ms += to_fire->period_ms;
}
to_fire->active = is_periodic;
uint64_t value = 1;
write(to_fire->fd, &value, sizeof(uint64_t));
return true;
}
void fake_timerfd_advance(uint64_t ms) {
uint64_t new_clock = clock + ms;
if (new_clock > max_clock) {
new_clock = max_clock;
}
while (fire_next_event(new_clock)) {
}
clock = new_clock;
}
void fake_timerfd_cap_at(uint64_t ms) {
max_clock = ms;
}
} // namespace fuzz
} // namespace os
} // namespace bluetooth