aboutsummaryrefslogtreecommitdiffstats
path: root/src/vppinfra/tw_timer_template.h
blob: 76755609451c76336f8105c7b19a8c7ec697d485 (plain)
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * 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.
 */

#ifndef TW_SUFFIX
#error do not include tw_timer_template.h directly
#endif

#include <vppinfra/clib.h>
#include <vppinfra/pool.h>

#ifndef _twt
#define _twt(a,b) a##b##_t
#define __twt(a,b) _twt(a,b)
#define TWT(a) __twt(a,TW_SUFFIX)

#define _tw(a,b) a##b
#define __tw(a,b) _tw(a,b)
#define TW(a) __tw(a,TW_SUFFIX)
#endif

/** @file
    @brief TW timer template header file, do not compile directly

Instantiation of tw_timer_template.h generates named structures to
implement specific timer wheel geometries. Choices include: number of
timer wheels (currently, 1 or 2), number of slots per ring (a power of
two), and the number of timers per "object handle".

Internally, user object/timer handles are 32-bit integers, so if one
selects 16 timers/object (4 bits), the resulting timer wheel handle is
limited to 2**28 objects.

Here are the specific settings required to generate a single 2048 slot
wheel which supports 2 timers per object:

    #define TW_TIMER_WHEELS 1
    #define TW_SLOTS_PER_RING 2048
    #define TW_RING_SHIFT 11
    #define TW_RING_MASK (TW_SLOTS_PER_RING -1)
    #define TW_TIMERS_PER_OBJECT 2
    #define LOG2_TW_TIMERS_PER_OBJECT 1
    #define TW_SUFFIX _2t_1w_2048sl

See tw_timer_2t_1w_2048sl.h for a complete
example.

tw_timer_template.h is not intended to be #included directly. Client
codes can include multiple timer geometry header files, although
extreme caution would required to use the TW and TWT macros in such a
case.

API usage example:

Initialize a two-timer, single 2048-slot wheel w/ a 1-second
timer granularity:

    tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel,
                                     expired_timer_single_callback,
				      1.0 / * timer interval * / );

Start a timer:

    handle = tw_timer_start_2t_1w_2048sl (&tm->single_wheel, elt_index,
                                          [0 | 1] / * timer id * / ,
                                          expiration_time_in_u32_ticks);

Stop a timer:

    tw_timer_stop_2t_1w_2048sl (&tm->single_wheel, handle);

Expired timer callback:

    static void
    expired_timer_single_callback (u32 * expired_timers)
    {
    	int i;
        u32 pool_index, timer_id;
        tw_timer_test_elt_t *e;
        tw_timer_test_main_t *tm = &tw_timer_test_main;

        for (i = 0; i < vec_len (expired_timers);
            {
            pool_index = expired_timers[i] & 0x7FFFFFFF;
            timer_id = expired_timers[i] >> 31;

            ASSERT (timer_id == 1);

            e = pool_elt_at_index (tm->test_elts, pool_index);

            if (e->expected_to_expire != tm->single_wheel.current_tick)
              {
              	fformat (stdout, "[%d] expired at %d not %d\n",
                         e - tm->test_elts, tm->single_wheel.current_tick,
                         e->expected_to_expire);
              }
         pool_put (tm->test_elts, e);
         }
     }
 */

#if (TW_TIMER_WHEELS != 1 && TW_TIMER_WHEELS != 2 && TW_TIMER_WHEELS != 3)
#error TW_TIMER_WHEELS must be 1, 2 or 3
#endif

typedef struct
{
  /** next, previous pool indices */
  u32 next;
  u32 prev;

  union
  {
    struct
    {
#if (TW_TIMER_WHEELS == 3)
      /** fast ring offset, only valid in the slow ring */
      u16 fast_ring_offset;
      /** slow ring offset, only valid in the glacier ring */
      u16 slow_ring_offset;
#endif
#if (TW_TIMER_WHEELS == 2)
      /** fast ring offset, only valid in the slow ring */
      u16 fast_ring_offset;
      /** slow ring offset, only valid in the glacier ring */
      u16 pad;
#endif
    };

#if (TW_OVERFLOW_VECTOR > 0)
    u64 expiration_time;
#endif
  };

  /** user timer handle */
  u32 user_handle;
} TWT (tw_timer);

/*
 * These structures ar used by all geometries,
 * so they need a private #include block...
 */
#ifndef __defined_tw_timer_wheel_slot__
#define __defined_tw_timer_wheel_slot__
typedef struct
{
  /** Listhead of timers which expire in this interval */
  u32 head_index;
} tw_timer_wheel_slot_t;
typedef enum
{
  /** Fast timer ring ID */
  TW_TIMER_RING_FAST,
  /** Slow timer ring ID */
  TW_TIMER_RING_SLOW,
  /** Glacier ring ID */
  TW_TIMER_RING_GLACIER,
} tw_ring_index_t;
#endif /* __defined_tw_timer_wheel_slot__ */

typedef struct
{
  /** Timer pool */
  TWT (tw_timer) * timers;

  /** Next time the wheel should run */
  f64 next_run_time;

  /** Last time the wheel ran */
  f64 last_run_time;

  /** Timer ticks per second */
  f64 ticks_per_second;

  /** Timer interval, also needed to avoid fp divide in speed path */
  f64 timer_interval;

  /** current tick */
  u64 current_tick;

  /** first expiration time */
  u64 first_expires_tick;

  /** current wheel indices */
  u32 current_index[TW_TIMER_WHEELS];

  /** wheel arrays */
  tw_timer_wheel_slot_t w[TW_TIMER_WHEELS][TW_SLOTS_PER_RING];

#if TW_OVERFLOW_VECTOR > 0
  tw_timer_wheel_slot_t overflow;
#endif

  /** expired timer callback, receives a vector of handles */
  void (*expired_timer_callback) (u32 * expired_timer_handles);

  /** vector of expired timers */
  u32 *expired_timer_handles;

  /** maximum expirations */
  u32 max_expirations;
} TWT (tw_timer_wheel);

u32 TW (tw_timer_start) (TWT (tw_timer_wheel) * tw,
			 u32 pool_index, u32 timer_id, u64 interval);

void TW (tw_timer_stop) (TWT (tw_timer_wheel) * tw, u32 handle);

void TW (tw_timer_wheel_init) (TWT (tw_timer_wheel) * tw,
			       void *expired_timer_callback,
			       f64 timer_interval, u32 max_expirations);

void TW (tw_timer_wheel_free) (TWT (tw_timer_wheel) * tw);

u32 *TW (tw_timer_expire_timers) (TWT (tw_timer_wheel) * tw, f64 now);
u32 *TW (tw_timer_expire_timers_vec) (TWT (tw_timer_wheel) * tw, f64 now,
				      u32 * vec);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */