mirror of
https://github.com/google/pebble.git
synced 2025-03-21 19:31:20 +00:00
320 lines
9.5 KiB
C
320 lines
9.5 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "fake_pbl_malloc.h"
|
|
|
|
#include "services/common/new_timer/new_timer.h"
|
|
#include "util/list.h"
|
|
#include "drivers/rtc.h"
|
|
#include "system/passert.h"
|
|
#include <stdio.h>
|
|
|
|
// Structure of a timer
|
|
typedef struct StubTimer {
|
|
//! Entry into either the running timers (s_running_timers) list or the idle timers
|
|
//! (s_idle_timers)
|
|
ListNode list_node;
|
|
|
|
TimerID id; //<! ID assigned to this timer
|
|
|
|
//! client provided callback function and argument
|
|
NewTimerCallback cb;
|
|
void* cb_data;
|
|
|
|
//! The tick value when this timer will expire (in milliseconds). If the timer isn't currently
|
|
//! running (scheduled) this value will be zero.
|
|
uint32_t timeout_ms;
|
|
|
|
//! True if this timer should automatically be rescheduled for period_ms from now
|
|
bool repeating;
|
|
uint32_t period_ms;
|
|
|
|
//! True if this timer is currently having its callback executed.
|
|
bool executing;
|
|
|
|
//! Set by the delete function of client tries to delete a timer currently executing it's callback
|
|
bool defer_delete;
|
|
|
|
} StubTimer;
|
|
|
|
|
|
|
|
// =============================================================================================
|
|
// Stubs
|
|
static ListNode *s_running_timers = NULL;
|
|
static ListNode *s_idle_timers = NULL;
|
|
|
|
// Call counters
|
|
static int s_num_new_timer_create_calls = 0;
|
|
static int s_num_new_timer_start_calls = 0;
|
|
static int s_num_new_timer_stop_calls = 0;
|
|
static int s_num_new_timer_delete_calls = 0;
|
|
static int s_num_new_timer_schedule_calls = 0;
|
|
|
|
// Last parameters
|
|
static TimerID s_new_timer_start_param_timer_id;
|
|
static uint32_t s_new_timer_start_param_timeout_ms;
|
|
static NewTimerCallback s_new_timer_start_param_cb;
|
|
static void * s_new_timer_start_param_cb_data;
|
|
|
|
// Debug utility
|
|
/*
|
|
static void prv_print_idle_list(char* title) {
|
|
printf("%s IDLE LIST:\n", title);
|
|
StubTimer *node = (StubTimer *) s_idle_timers;
|
|
while (node) {
|
|
StubTimer *next = (StubTimer *) list_get_next(&node->list_node);
|
|
printf(" %p\n", node);
|
|
if (node == next) {
|
|
printf("RECURSIVE!!!\n");
|
|
return;
|
|
}
|
|
node = next;
|
|
}
|
|
printf("\n");
|
|
}
|
|
*/
|
|
|
|
|
|
static bool prv_id_list_filter(ListNode* node, void* data) {
|
|
StubTimer* timer = (StubTimer*)node;
|
|
return timer->id == (uint32_t) data;
|
|
}
|
|
|
|
static StubTimer* prv_find_timer(TimerID timer_id)
|
|
{
|
|
// Look for this timer in either the running or idle list
|
|
ListNode* node = list_find(s_running_timers, prv_id_list_filter, (void*)(intptr_t)timer_id);
|
|
if (!node) {
|
|
node = list_find(s_idle_timers, prv_id_list_filter, (void*)(intptr_t)timer_id);
|
|
}
|
|
return (StubTimer *)node;
|
|
}
|
|
|
|
static int prv_timer_expire_compare_func(void* a, void* b) {
|
|
return (((StubTimer*)b)->timeout_ms - ((StubTimer*)a)->timeout_ms);
|
|
}
|
|
|
|
|
|
static int stub_new_timer_create(void) {
|
|
StubTimer *timer = (StubTimer *) kernel_malloc(sizeof(StubTimer));
|
|
static int s_next_timer_id = 1;
|
|
*timer = (StubTimer) {
|
|
.id = s_next_timer_id++,
|
|
};
|
|
s_idle_timers = list_insert_before(s_idle_timers, &timer->list_node);
|
|
return timer->id;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Stub manipulation:
|
|
//
|
|
bool stub_new_timer_start(TimerID timer_id, uint32_t timeout_ms, NewTimerCallback cb, void *cb_data,
|
|
uint32_t flags) {
|
|
StubTimer* timer = prv_find_timer(timer_id);
|
|
|
|
// Remove it from its current list
|
|
if (list_contains(s_running_timers, &timer->list_node)) {
|
|
list_remove(&timer->list_node, &s_running_timers /* &head */, NULL /* &tail */);
|
|
} else {
|
|
list_remove(&timer->list_node, &s_idle_timers /* &head */, NULL /* &tail */);
|
|
}
|
|
|
|
// Set timer variables
|
|
timer->cb = cb;
|
|
timer->cb_data = cb_data;
|
|
timer->timeout_ms = timeout_ms;
|
|
timer->repeating = flags & TIMER_START_FLAG_REPEATING;
|
|
timer->period_ms = timeout_ms;
|
|
|
|
// Insert into sorted order in the running list
|
|
s_running_timers = list_sorted_add(s_running_timers, &timer->list_node,
|
|
prv_timer_expire_compare_func, true);
|
|
return true;
|
|
}
|
|
|
|
bool stub_new_timer_stop(TimerID timer_id) {
|
|
StubTimer* timer = prv_find_timer(timer_id);
|
|
|
|
// Move it to the idle list if it's currently running
|
|
if (list_contains(s_running_timers, &timer->list_node)) {
|
|
list_remove(&timer->list_node, &s_running_timers /* &head */, NULL /* &tail */);
|
|
s_idle_timers = list_insert_before(s_idle_timers, &timer->list_node);
|
|
}
|
|
|
|
// Clear the repeating flag so that if they call this method from a callback it won't get
|
|
// rescheduled
|
|
timer->repeating = false;
|
|
timer->timeout_ms = 0;
|
|
return !timer->executing;
|
|
}
|
|
|
|
void stub_new_timer_delete(TimerID timer_id) {
|
|
StubTimer* timer = prv_find_timer(timer_id);
|
|
|
|
// Automatically stop it if it it's not stopped already
|
|
if (list_contains(s_running_timers, &timer->list_node)) {
|
|
timer->timeout_ms = 0;
|
|
list_remove(&timer->list_node, &s_running_timers /* &head */, NULL /* &tail */);
|
|
s_idle_timers = list_insert_before(s_idle_timers, &timer->list_node);
|
|
}
|
|
timer->repeating = false; // In case it's currently executing, make sure we don't reschedule it
|
|
|
|
if (!timer->executing) {
|
|
list_remove(&timer->list_node, &s_idle_timers /* &head */, NULL /* &tail */);
|
|
kernel_free(timer);
|
|
} else {
|
|
timer->defer_delete = true;
|
|
}
|
|
}
|
|
|
|
bool stub_new_timer_is_scheduled(TimerID timer_id) {
|
|
StubTimer* timer = prv_find_timer(timer_id);
|
|
if (timer == NULL) {
|
|
return false;
|
|
}
|
|
return list_contains(s_running_timers, &timer->list_node);
|
|
}
|
|
|
|
uint32_t stub_new_timer_timeout(TimerID timer_id) {
|
|
StubTimer* timer = prv_find_timer(timer_id);
|
|
if (timer == NULL) {
|
|
return false;
|
|
}
|
|
return timer->timeout_ms;
|
|
}
|
|
|
|
// Mark the timer as executing. This prevents it from getting deleted. In the real implementation,
|
|
// it would get deleted after it's callback returned
|
|
void stub_new_timer_set_executing(TimerID timer_id, bool set) {
|
|
StubTimer* timer = prv_find_timer(timer_id);
|
|
PBL_ASSERTN(timer != NULL);
|
|
timer->executing = true;
|
|
}
|
|
|
|
void * stub_new_timer_callback_data(TimerID timer_id) {
|
|
StubTimer* timer = prv_find_timer(timer_id);
|
|
if (timer == NULL) {
|
|
return false;
|
|
}
|
|
return timer->cb_data;
|
|
}
|
|
|
|
bool stub_new_timer_fire(TimerID timer_id) {
|
|
StubTimer* timer = prv_find_timer(timer_id);
|
|
if (timer == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (list_contains(s_running_timers, &timer->list_node)) {
|
|
list_remove(&timer->list_node, &s_running_timers /* &head */, NULL /* &tail */);
|
|
s_idle_timers = list_insert_before(s_idle_timers, &timer->list_node);
|
|
} else {
|
|
printf("WARNING: Attempted to fire a non-running timer\n");
|
|
return false;
|
|
}
|
|
|
|
timer->timeout_ms = 0;
|
|
timer->executing = true;
|
|
timer->cb(timer->cb_data);
|
|
timer->executing = false;
|
|
|
|
if (timer->defer_delete) {
|
|
// Timer was deleted from the callback, clean it up now:
|
|
list_remove(&timer->list_node, &s_idle_timers /* &head */, NULL /* &tail */);
|
|
kernel_free(timer);
|
|
return true;
|
|
}
|
|
|
|
if (timer->repeating && timer->timeout_ms == 0) {
|
|
stub_new_timer_start(timer_id, timer->period_ms, timer->cb, timer->cb_data,
|
|
TIMER_START_FLAG_REPEATING);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void stub_new_timer_cleanup(void) {
|
|
StubTimer *node = (StubTimer *) s_running_timers;
|
|
while (node) {
|
|
StubTimer *next = (StubTimer *) list_get_next(&node->list_node);
|
|
list_remove(&node->list_node, &s_running_timers, NULL);
|
|
kernel_free(node);
|
|
node = next;
|
|
}
|
|
PBL_ASSERTN(s_running_timers == NULL);
|
|
|
|
node = (StubTimer *) s_idle_timers;
|
|
while (node) {
|
|
StubTimer *next = (StubTimer *) list_get_next(&node->list_node);
|
|
list_remove(&node->list_node, &s_idle_timers, NULL);
|
|
kernel_free(node);
|
|
node = next;
|
|
}
|
|
PBL_ASSERTN(s_idle_timers == NULL);
|
|
}
|
|
|
|
TimerID stub_new_timer_get_next(void) {
|
|
StubTimer *timer = (StubTimer *) list_get_head(s_running_timers);
|
|
return timer ? timer->id : TIMER_INVALID_ID;
|
|
}
|
|
|
|
void stub_new_timer_invoke(int num_to_invoke) {
|
|
TimerID timer = stub_new_timer_get_next();
|
|
while (timer != TIMER_INVALID_ID && num_to_invoke--) {
|
|
stub_new_timer_fire(timer);
|
|
timer = stub_new_timer_get_next();
|
|
}
|
|
}
|
|
|
|
// =============================================================================================
|
|
// Fakes
|
|
|
|
// Create a new timer
|
|
TimerID new_timer_create(void) {
|
|
s_num_new_timer_create_calls++;
|
|
return stub_new_timer_create();
|
|
}
|
|
|
|
// Start a timer
|
|
bool new_timer_start(TimerID timer_id, uint32_t timeout_ms, NewTimerCallback cb, void *cb_data,
|
|
uint32_t flags) {
|
|
s_num_new_timer_start_calls++;
|
|
s_new_timer_start_param_timer_id = timer_id;
|
|
s_new_timer_start_param_timeout_ms = timeout_ms;
|
|
s_new_timer_start_param_cb = cb;
|
|
s_new_timer_start_param_cb_data = cb_data;
|
|
return stub_new_timer_start(timer_id, timeout_ms, cb, cb_data, flags);
|
|
}
|
|
|
|
// Stop a timer
|
|
bool new_timer_stop(TimerID timer_id) {
|
|
s_num_new_timer_stop_calls++;
|
|
return stub_new_timer_stop(timer_id);
|
|
}
|
|
|
|
// Delete a timer
|
|
void new_timer_delete(TimerID timer_id) {
|
|
s_num_new_timer_delete_calls++;
|
|
stub_new_timer_delete(timer_id);
|
|
}
|
|
|
|
bool new_timer_scheduled(TimerID timer, uint32_t *expire_ms_p) {
|
|
s_num_new_timer_schedule_calls++;
|
|
return stub_new_timer_is_scheduled(timer);
|
|
}
|
|
|