Neels Hofmeyr | 25c9741 | 2021-11-13 23:19:33 +0100 | [diff] [blame] | 1 | /*! \file time_cc.h |
| 2 | * Report the cumulative counter of time for which a flag is true as rate counter. |
| 3 | */ |
| 4 | /* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> |
| 5 | * |
| 6 | * All Rights Reserved |
| 7 | * |
| 8 | * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> |
| 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify |
| 11 | * it under the terms of the GNU Affero General Public License as published by |
| 12 | * the Free Software Foundation; either version 3 of the License, or |
| 13 | * (at your option) any later version. |
| 14 | * |
| 15 | * This program is distributed in the hope that it will be useful, |
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | * GNU Affero General Public License for more details. |
| 19 | * |
| 20 | * You should have received a copy of the GNU Affero General Public License |
| 21 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 22 | * |
| 23 | */ |
| 24 | #pragma once |
| 25 | |
| 26 | #include <stdint.h> |
| 27 | |
| 28 | #include <osmocom/core/timer.h> |
| 29 | |
| 30 | /*! \defgroup time_cc Cumulative counter of time as rate counter. |
| 31 | * @{ |
| 32 | * \file time_cc.h |
| 33 | */ |
| 34 | |
| 35 | struct osmo_tdef; |
| 36 | struct rate_ctr; |
| 37 | |
| 38 | /*! Configuration for osmo_time_cc. |
| 39 | * Report the cumulative counter of time for which a flag is true as rate counter. |
| 40 | * For example, for each second that the flag is true, increment a rate counter. |
| 41 | * |
| 42 | * The flag to be monitored is reported by osmo_time_cc_set_flag(). |
| 43 | * |
| 44 | * The granularity defines how much time one rate counter increment represents: |
| 45 | * the default configuration is gran_usec = 1000000, i.e. one rate counter increment represents one second. |
| 46 | * |
| 47 | * Reporting as rate counter is configurable by round_threshold_usec and forget_sum_usec, examples: |
| 48 | * |
| 49 | * round_threshold_usec: |
| 50 | * - To get "ceil()" behavior, set round_threshold_usec = 1. This increments the rate counter for each gran_usec period |
| 51 | * where the flag was seen true, even if it was true for only a very short fraction of a gran_usec period. |
| 52 | * - To get "round()" behavior, set round_threshold_usec = half of gran_usec. The rate counter increments when the flag |
| 53 | * has been true for 0.5 of a gran_usec (and then again at 1.5 * gran_usec) of 'true' flag. round_threshold_usec = 0 |
| 54 | * is a special value that means to use half of gran_usec. |
| 55 | * - To get "floor()" behavior, set round_threshold_usec >= gran_usec. The rate counter increments when reaching full |
| 56 | * gran_usec periods of the flag being true. |
| 57 | * |
| 58 | * forget_sum_usec: |
| 59 | * This is a tradeoff between the accuracy of the reported rate counter and making sure that the events reported are not |
| 60 | * irrelevantly long ago. |
| 61 | * - To keep sub-granularity-period surplus time forever, set forget_sum_usec = 0. |
| 62 | * - To keep surplus time for up to a minute, set forget_sum_usec = 60000000 (60 seconds). |
| 63 | * - To get rid of "leftover" time (almost) immediately after the flag goes false, set forget_sum_usec = 1. |
| 64 | * - If gran_usec is set to one second and forget_sum_usec is set to one minute, the reported rate counter has a |
| 65 | * possible inaccuracy of 1/60th, but makes sure that no timings older than a minute affect the current reports. |
| 66 | * |
| 67 | * Reporting modes in detail: |
| 68 | * |
| 69 | * The rate_ctr increments when the cumulative counter passes round_threshold_usec (default: half of gran_usec). |
| 70 | * |
| 71 | * sum ^ |
| 72 | * | ________ |
| 73 | * | / |
| 74 | * | / |
| 75 | * | / |
| 76 | * 3*gran --+--------------------------------------+ |
| 77 | * | /: |
| 78 | * | / : |
| 79 | * | - - - - - - - - - - - - - - - - - / : |
| 80 | * | /. : |
| 81 | * | / . : |
| 82 | * 2*gran --+--------------------------------+ . : |
| 83 | * | /: . : |
| 84 | * | / : . : |
| 85 | * | - - - - - - - - - -_________/ : . : |
| 86 | * | / . : . : |
| 87 | * | / . : . : |
| 88 | * 1*gran --+-----------------+ . : . : |
| 89 | * | /: . : . : |
| 90 | * | / : . : . : |
| 91 | * | - - - - - - -/ : . : . : |
| 92 | * | /. : . : . : |
| 93 | * | ....-------' . : . : . : |
| 94 | * 0 +------------------------------------------------------------------------> elapsed time |
| 95 | * . : . : . : |
| 96 | * _ _ _______ ____________ |
| 97 | * flag: __| |_| |____| . : |_______|. : . : |__________ |
| 98 | * f t f t f t . : f t. : . : f |
| 99 | * round_threshold_usec : . : . : . : |
| 100 | * = 1 usec: 0 1 . :2 . :3 . :4 = "ceil()" |
| 101 | * = 0 == gran_usec/2: 0 1 : 2 : 3 : = "round()" |
| 102 | * >= gran_usec: 0 1 2 3 = "floor()" |
| 103 | * |
| 104 | */ |
| 105 | struct osmo_time_cc_cfg { |
| 106 | /*! Granularity in microseconds: nr of microseconds that one rate_ctr increment represents. A typical value is |
| 107 | * gran_usec = 1000000, meaning one rate counter increment represents one second. When zero, use 1000000. */ |
| 108 | uint64_t gran_usec; |
| 109 | /*! Nr of microseconds above n * gran_usec at which to trigger a counter increment. When zero, use half a |
| 110 | * gran_usec. */ |
| 111 | uint64_t round_threshold_usec; |
| 112 | /*! Forget counted sub-gran time after the flag was false for this long. */ |
| 113 | uint64_t forget_sum_usec; |
| 114 | /*! Rate counter to report to, or NULL to not use it. */ |
| 115 | struct rate_ctr *rate_ctr; |
| 116 | |
| 117 | /*! Update gran_usec from this T timer value, or zero to not use any T timer. */ |
| 118 | int T_gran; |
| 119 | /*! Update round_threshold_usec from this T timer value, or zero to not use any T timer. */ |
| 120 | int T_round_threshold; |
| 121 | /*! Update forget_sum_usec from this T timer value, or zero to not use any T timer. */ |
| 122 | int T_forget_sum; |
| 123 | /*! Look up T_gran and T_forget_sum in this list of timers, or NULL to not use any T timers. */ |
| 124 | struct osmo_tdef *T_defs; |
| 125 | }; |
| 126 | |
| 127 | /*! Report the cumulative counter of time for which a flag is true as rate counter. |
| 128 | * See also osmo_time_cc_cfg for details on configuring. |
| 129 | * |
| 130 | * Usage: |
| 131 | * |
| 132 | * struct my_obj { |
| 133 | * struct osmo_time_cc flag_cc; |
| 134 | * }; |
| 135 | * |
| 136 | * void my_obj_init(struct my_obj *my_obj) |
| 137 | * { |
| 138 | * osmo_time_cc_init(&my_obj->flag_cc); |
| 139 | * my_obj->flag_cc.cfg = (struct osmo_time_cc_cfg){ |
| 140 | * .gran_usec = 1000000, |
| 141 | * .forget_sum_usec = 60000000, |
| 142 | * .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, MY_CTR_IDX), |
| 143 | * }; |
| 144 | * // optional: set initial flag state, default is 'false': |
| 145 | * // osmo_time_cc_set_flag(&my_obj->flag_cc, false); |
| 146 | * } |
| 147 | * |
| 148 | * void my_obj_event(struct my_obj *my_obj, bool flag) |
| 149 | * { |
| 150 | * osmo_time_cc_set_flag(&my_obj->flag_cc, flag); |
| 151 | * } |
| 152 | * |
| 153 | * void my_obj_destruct(struct my_obj *my_obj) |
| 154 | * { |
| 155 | * osmo_time_cc_cleanup(&my_obj->flag_cc); |
| 156 | * } |
| 157 | */ |
| 158 | struct osmo_time_cc { |
| 159 | struct osmo_time_cc_cfg cfg; |
| 160 | |
| 161 | bool flag_state; |
| 162 | |
| 163 | /*! Overall cumulative sum. Does not get reset for the entire lifetime of an osmo_time_cc. |
| 164 | * (Informational only, not used by the osmo_time_cc implementation.) */ |
| 165 | uint64_t total_sum; |
| 166 | |
| 167 | struct osmo_timer_list timer; |
| 168 | |
| 169 | /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc instance started counting. */ |
| 170 | uint64_t start_time; |
| 171 | /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc last evaluated the flag state and |
| 172 | * possibly added to the cumulated sum. */ |
| 173 | uint64_t last_counted_time; |
| 174 | |
| 175 | /*! Internal cumulative counter of time that flag_state was true. It may get reset to zero regularly, depending |
| 176 | * on cfg.forget_sum_usec. This is the basis for incrementing cfg.rate_ctr. */ |
| 177 | uint64_t sum; |
| 178 | /*! The amount of time that already reported cfg.rate_ctr increments account for. This may be ahead of or behind |
| 179 | * 'sum', depending on cfg.round_threshold_usec. */ |
| 180 | uint64_t reported_sum; |
| 181 | }; |
| 182 | |
| 183 | void osmo_time_cc_init(struct osmo_time_cc *tc); |
| 184 | void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag); |
| 185 | void osmo_time_cc_cleanup(struct osmo_time_cc *tc); |
| 186 | |
| 187 | /*! @} */ |