Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <net/net_timeout.h>
#include <sys_clock.h>
void net_timeout_set(struct net_timeout *timeout,
uint32_t lifetime,
uint32_t now)
{
uint64_t expire_timeout;
timeout->timer_start = now;
/* Highly unlikely, but a zero timeout isn't correctly handled by the
* standard calculation.
*/
if (lifetime == 0U) {
timeout->wrap_counter = 0;
timeout->timer_timeout = 0;
return;
}
expire_timeout = (uint64_t)MSEC_PER_SEC * (uint64_t)lifetime;
timeout->wrap_counter = expire_timeout /
(uint64_t)NET_TIMEOUT_MAX_VALUE;
timeout->timer_timeout = expire_timeout -
(uint64_t)NET_TIMEOUT_MAX_VALUE *
(uint64_t)timeout->wrap_counter;
/* The implementation requires that the fractional timeout be zero
* only when the timeout has completed, so if the residual is zero
* copy over one timeout from the wrap.
*/
if (timeout->timer_timeout == 0U) {
timeout->timer_timeout = NET_TIMEOUT_MAX_VALUE;
timeout->wrap_counter -= 1;
}
}
int64_t net_timeout_deadline(const struct net_timeout *timeout,
int64_t now)
{
uint64_t start;
uint64_t deadline;
/* Reconstruct the full-precision start time assuming that the full
* precision start time is less than 2^32 ticks in the past.
*/
start = (uint64_t)now;
start -= (uint32_t)now - timeout->timer_start;
/* Offset the start time by the full precision remaining delay. */
deadline = start + timeout->timer_timeout;
deadline += (uint64_t)NET_TIMEOUT_MAX_VALUE
* (uint64_t)timeout->wrap_counter;
return (int64_t)deadline;
}
uint32_t net_timeout_remaining(const struct net_timeout *timeout,
uint32_t now)
{
int64_t ret = timeout->timer_timeout;
ret += timeout->wrap_counter * (uint64_t)NET_TIMEOUT_MAX_VALUE;
ret -= (int64_t)(int32_t)(now - timeout->timer_start);
if (ret <= 0) {
return 0;
}
return (uint32_t)((uint64_t)ret / MSEC_PER_SEC);
}
uint32_t net_timeout_evaluate(struct net_timeout *timeout,
uint32_t now)
{
uint32_t elapsed;
uint32_t last_delay;
int32_t remains;
bool wraps;
/* Time since last evaluation or set. */
elapsed = now - timeout->timer_start;
/* The delay used the last time this was evaluated. */
wraps = (timeout->wrap_counter > 0U);
last_delay = wraps
? NET_TIMEOUT_MAX_VALUE
: timeout->timer_timeout;
/* Time remaining until completion of the last delay. */
remains = (int32_t)(last_delay - elapsed);
/* If the deadline for the next event hasn't been reached yet just
* return the remaining time.
*/
if (remains > 0) {
return (uint32_t)remains;
}
/* Deadline has been reached. If we're not wrapping we've completed
* the last portion of the full timeout, so return zero to indicate
* the timeout has completed.
*/
if (!wraps) {
return 0U;
}
/* There's more to do. We need to update timer_start to correspond to
* now, then reduce the remaining time by the elapsed time. We know
* that's at least NET_TIMEOUT_MAX_VALUE, and can apply the
* reduction by decrementing the wrap count.
*/
timeout->timer_start = now;
elapsed -= NET_TIMEOUT_MAX_VALUE;
timeout->wrap_counter -= 1;
/* The residual elapsed must reduce timer_timeout, which is capped at
* NET_TIMEOUT_MAX_VALUE. But if subtracting would reduce the
* counter to zero or go negative we need to reduce the the wrap
* counter once more and add the residual to the counter, so the
* counter remains positive.
*/
if (timeout->timer_timeout > elapsed) {
timeout->timer_timeout -= elapsed;
} else {
timeout->timer_timeout += NET_TIMEOUT_MAX_VALUE - elapsed;
timeout->wrap_counter -= 1U;
}
return (timeout->wrap_counter == 0U)
? timeout->timer_timeout
: NET_TIMEOUT_MAX_VALUE;
}
|