1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup alarm
36 * @ingroup dnscore
37 * @brief Alarm functions
38 *
39 * @{
40 */
41
42 #include <dnscore/timems.h>
43 #include "dnscore/dnscore-config.h"
44 #include "dnscore/logger.h"
45 #include "dnscore/alarm.h"
46 #include "dnscore/mutex.h"
47 #include "dnscore/zalloc.h"
48 #if DEBUG
49 #include "dnscore/timeformat.h"
50 #endif // DEBUG
51
52 #define HAS_ALARM_DUMP 0
53
54 #define MODULE_MSG_HANDLE g_system_logger
55
56 /*
57 * List of events (double-linked), sorted by epoch (then insersion time).
58 * Array of handles (expandable), with the list of events they hold. (so they can easily be foud by id and closed)
59 *
60 * The descriptions are stored in both collections. (two double-linked structures)
61 *
62 * TIMELIST->[TIME#0]->[TIME#1]->...->[TIME#n]
63 * | |
64 * v [NODE]
65 * HNDLLIST->[NODE]--+ |
66 * +->[NODE]-- ...
67 */
68
69 #define ALARM_NODE_LIST_TAG 0x5453494c4d524c41
70 #define ALARM_NODE_DESC_TAG 0x4353444e4d524c41
71 #define ALARM_NODE_TIME_TAG 0x454d49544d524c41
72 #define ALARM_HANDLE_TAG 0x4c444e484d524c41
73
74 struct alarm_event_list
75 {
76 alarm_event_node *first;
77 alarm_event_node *last;
78 };
79
80 typedef struct alarm_event_list alarm_event_list;
81
82 struct alarm_handle
83 {
84 alarm_event_list events; // events for that handle
85 const u8 *owner_dnsname;
86 };
87
88 typedef struct alarm_handle alarm_handle;
89
90 struct alarm_time_node
91 {
92 struct alarm_time_node *next;
93 alarm_event_list events;
94 u32 epoch;
95 };
96
97 typedef struct alarm_time_node alarm_time_node;
98
99 static ptr_vector alarm_handles = PTR_VECTOR_EMPTY;
100 static alarm_time_node doomsday = { NULL, {NULL, NULL}, MAX_U32 }; // nothing after this point
101 static alarm_time_node time_list = { &doomsday, {NULL, NULL}, 0 };
102 static mutex_t alarm_mutex = MUTEX_INITIALIZER;
103 static int alarm_handles_next_free = -1;
104 #if DEBUG
105 static volatile bool alarm_mutex_locked = FALSE;
106 static volatile u32 alarm_event_in_handles_count = 0;
107 static volatile u32 alarm_event_in_time_count = 0;
108 static volatile u32 alarm_event_alarm_set_count = 0;
109 static smp_int alarm_event_instanciated = SMP_INT_INITIALIZER;
110 #endif
111
112 static void alarm_handle_close(alarm_handle *handle_struct);
113
114 /**/
115
116 #if HAS_ALARM_DUMP
117 void alarm_log_dump_nolock(bool check);
118 void alarm_log_dump();
119 #endif
120
121 struct timespec __alarm__approximate_time_10s = { 0, 0 };
122 struct timespec __alarm__approximate_time_30s = { 0, 0 };
123
124 /**
125 * Allocates an uninitialised event node.
126 *
127 * @return
128 */
129
130 static alarm_event_node *
alarm_event_alloc()131 alarm_event_alloc()
132 {
133 alarm_event_node *node;
134 ZALLOC_OBJECT_OR_DIE(node, alarm_event_node, ALARM_NODE_DESC_TAG);
135
136 #if DEBUG
137 memset(node, 0xac,sizeof(alarm_event_node));
138 int total = smp_int_inc_get(&alarm_event_instanciated);
139 log_debug6("alarm_event_alloc(%p) (total=%d)", node, total);
140 #endif
141
142 return node;
143 }
144
145 /**
146 * Instanciates an event node
147 *
148 * @param epoch
149 * @param key
150 * @param function
151 * @param args
152 * @param flags
153 * @param text
154 * @return
155 */
156
157 alarm_event_node*
alarm_event_new(u32 epoch,u32 key,alarm_function_callback * function,void * args,u8 flags,const char * text)158 alarm_event_new(
159 u32 epoch,
160 u32 key,
161 alarm_function_callback *function,
162 void *args,
163 u8 flags,
164 const char *text)
165 {
166 alarm_event_node *event = alarm_event_alloc();
167 event->epoch = epoch;
168 event->key = key;
169 event->function = function;
170 event->args = args;
171
172 event->flags = flags;
173 event->text = text;
174
175 #if DEBUG
176 int total = smp_int_get(&alarm_event_instanciated);
177 log_debug6("alarm_event_new(%p %08x '%s' %T) (total=%d)", event, event->key, event->text, event->epoch, total);
178 #endif
179
180 return event;
181 }
182
183 /**
184 * Destroys an event node
185 *
186 * @param node
187 */
188
189 void
alarm_event_free(alarm_event_node * node)190 alarm_event_free(alarm_event_node *node)
191 {
192 #if DEBUG
193 int total = smp_int_dec_get(&alarm_event_instanciated);
194 log_debug6("alarm_event_free(%p %08x '%s' %T) (total=%d)", node, node->key, node->text, node->epoch, total);
195 memset(node, 0xfe,sizeof(alarm_event_node));
196 #endif
197 ZFREE_OBJECT(node);
198 }
199
200 static void
alarm_event_list_init(alarm_event_list * head)201 alarm_event_list_init(alarm_event_list *head)
202 {
203 head->first = alarm_event_alloc();
204 head->last = head->first;
205 ZEROMEMORY(head->first, sizeof(alarm_event_node));
206 #if DEBUG
207 head->first->text = "ALARM EVENT LIST SENTINEL";
208 #endif
209 }
210
211 static void
alarm_event_list_finalize(alarm_event_list * head)212 alarm_event_list_finalize(alarm_event_list *head)
213 {
214 if(head->first != NULL && head->first == head->last)
215 {
216 alarm_event_free(head->first);
217 head->first = NULL;
218 head->last = NULL;
219 }
220 #if DEBUG
221 else
222 {
223 log_warn("alarm_event_list_finalize called on an non-empty list");
224 }
225 #endif
226 }
227
228 static bool
alarm_event_list_isempty(alarm_event_list * head)229 alarm_event_list_isempty(alarm_event_list *head)
230 {
231 assert(alarm_mutex_locked);
232
233 return head->first == head->last;
234 }
235
236 static alarm_event_node *
alarm_event_list_removefirst(alarm_event_list * time_event)237 alarm_event_list_removefirst(alarm_event_list *time_event)
238 {
239 assert(alarm_mutex_locked);
240 assert(!alarm_event_list_isempty(time_event));
241 assert((time_event->first != NULL) && (time_event->last != NULL));
242
243 // prerequisite: (time node) list is not empty
244
245 // detach from the (time node) list
246
247 alarm_event_node *time_node = time_event->first;
248 time_event->first = time_node->time_next;
249 time_event->first->time_prev = NULL;
250
251 #if DEBUG
252 --alarm_event_in_time_count;
253 #endif
254
255 assert(time_node->hndl_next != NULL);
256
257 // detach from the handle list
258
259 if(time_node->hndl_prev != NULL)
260 {
261 // in the middle of the list : easy
262 time_node->hndl_prev->hndl_next = time_node->hndl_next;
263 }
264 else
265 {
266 // at the beginning of the list : have to find the list first
267 alarm_event_list *handler_list = ptr_vector_get(&alarm_handles, time_node->handle);
268 handler_list->first = time_node->hndl_next;
269 }
270
271 /* if(node->hndl_next != NULL) There is at least the sentinel */
272
273 time_node->hndl_next->hndl_prev = time_node->hndl_prev;
274
275 #if DEBUG
276
277 time_node->hndl_next = (alarm_event_node*)~0;
278 time_node->hndl_prev = (alarm_event_node*)~0;
279
280 time_node->time_next = (alarm_event_node*)~0;
281 time_node->time_prev = (alarm_event_node*)~0;
282
283 --alarm_event_in_handles_count;
284 #endif
285
286 #if DEBUG
287 log_debug6("alarm_event_list_removefirst(%p %08x '%s' %T)", time_node, time_node->key, time_node->text, time_node->epoch);
288 #endif
289
290 return time_node;
291 }
292
293 /*
294 * Append at end.
295 */
296
297 static void
alarm_event_append(alarm_event_list * handle_list,alarm_event_list * time_list,alarm_event_node * node)298 alarm_event_append(alarm_event_list *handle_list, alarm_event_list *time_list, alarm_event_node *node)
299 {
300 assert(alarm_mutex_locked);
301
302 /*
303 * List not empty ?
304 */
305
306 #if DEBUG
307 log_debug6("alarm_event_append(%p,%p,%p %08x '%s' %T)", handle_list, time_list, node, node->key, node->text, node->epoch);
308 #endif
309
310 if(handle_list->first != handle_list->last)
311 {
312 /*
313 * Insert the node before the last one.
314 */
315
316 handle_list->last->hndl_prev->hndl_next = node; // BL ->N L
317 node->hndl_prev = handle_list->last->hndl_prev; // BL<->N L
318
319 handle_list->last->hndl_prev = node; // BL<->N<- L
320 node->hndl_next = handle_list->last; // BL<->N<->L
321 }
322 else
323 {
324 handle_list->first = node; // F = N L
325 node->hndl_next = handle_list->last; // ->F ->L
326 handle_list->last->hndl_prev = node; // ->F<->L
327 node->hndl_prev = NULL; // 0<->F<->L
328 }
329
330 #if DEBUG
331 ++alarm_event_in_handles_count;
332 #endif
333
334 if(time_list->first != time_list->last)
335 {
336 time_list->last->time_prev->time_next = node;
337 node->time_prev = time_list->last->time_prev;
338
339 time_list->last->time_prev = node;
340 node->time_next = time_list->last;
341 }
342 else
343 {
344 time_list->first = node;
345 node->time_next = time_list->last;
346 time_list->last->time_prev = node;
347 node->time_prev = NULL;
348 }
349
350 #if DEBUG
351 ++alarm_event_in_time_count;
352 #endif
353 }
354
355 /**
356 * Removes the node from both the handle list and the time list.
357 * Does not releases memory.
358 *
359 * @param handle_list
360 * @param time_list
361 * @param node
362 */
363
364 static void
alarm_event_remove(alarm_event_list * handle_list,alarm_event_list * time_list,alarm_event_node * node)365 alarm_event_remove(alarm_event_list *handle_list, alarm_event_list *time_list, alarm_event_node *node)
366 {
367 assert(alarm_mutex_locked);
368 assert(time_list != NULL);
369 assert(node != NULL);
370
371 #if DEBUG
372 log_debug6("alarm_event_remove(%p,%p,%p %08x '%s' %T)", handle_list, time_list, node, node->key, node->text, node->epoch);
373 #endif
374
375 if(node->hndl_prev != NULL) // A<- N<->B ?
376 {
377 node->hndl_prev->hndl_next = node->hndl_next; // N<--N<->B
378 }
379 else
380 {
381 handle_list->first = node->hndl_next; // F = N<->B
382 }
383
384 node->hndl_next->hndl_prev = node->hndl_prev; // 0/A<-?B
385
386 #if DEBUG
387 --alarm_event_in_handles_count;
388 #endif
389
390 if(node->time_prev != NULL)
391 {
392 node->time_prev->time_next = node->time_next;
393 }
394 else
395 {
396 time_list->first = node->time_next;
397 // scan-build false positive : time cannot be null because the call
398 // that provides the value only gives a null for non-existing nodes
399 // AND the call is made using an existing node (not null).
400 }
401
402 node->time_next->time_prev = node->time_prev;
403
404 #if DEBUG
405
406 node->hndl_next = (alarm_event_node*)~0;
407 node->hndl_prev = (alarm_event_node*)~0;
408
409 node->time_next = (alarm_event_node*)~0;
410 node->time_prev = (alarm_event_node*)~0;
411
412 --alarm_event_in_time_count;
413 #endif
414
415 }
416
417 static alarm_time_node *
alarm_time_alloc()418 alarm_time_alloc()
419 {
420 alarm_time_node *time_node;
421 ZALLOC_OBJECT_OR_DIE(time_node, alarm_time_node, ALARM_NODE_TIME_TAG);
422
423 #if DEBUG
424 memset(time_node, 0xf0,sizeof(alarm_time_node));
425 log_debug6("alarm_time_alloc() = %p", time_node);
426 #endif
427
428 assert(alarm_mutex_locked);
429
430 alarm_event_list_init(&time_node->events); /* newly allocated : NO LOCK */
431
432 /* node->next : for the caller */
433 return time_node;
434 }
435
436 static void
alarm_time_free(alarm_time_node * time_node)437 alarm_time_free(alarm_time_node *time_node)
438 {
439 assert(alarm_mutex_locked);
440 assert(time_node->events.first == time_node->events.last);
441 #if DEBUG
442 log_debug6("alarm_time_free(%p)", time_node);
443
444 alarm_event_list_finalize(&time_node->events);
445
446 memset(time_node, 0xfe,sizeof(alarm_time_node));
447 #endif
448 ZFREE_OBJECT(time_node);
449 }
450
451 /*
452 * Get the time node for the exact epoch ...
453 */
454
455 static alarm_time_node *
alarm_time_get(u32 epoch)456 alarm_time_get(u32 epoch)
457 {
458 assert(alarm_mutex_locked);
459 assert(epoch != MAX_U32);
460
461 alarm_time_node *time_node = time_list.next;
462
463 while(time_node->epoch < epoch)
464 {
465 time_node = time_node->next;
466 }
467
468 if(time_node->epoch == epoch)
469 {
470 #if DEBUG
471 log_debug6("alarm_time_get(%T) = %p", epoch, time_node);
472 #endif
473 return time_node;
474 }
475 else
476 {
477 #if DEBUG
478 log_debug6("alarm_time_get(%T) = %p", epoch, NULL);
479 #endif
480 return NULL;
481 }
482 }
483
484 static alarm_time_node *
alarm_time_create(u32 epoch)485 alarm_time_create(u32 epoch)
486 {
487 assert(alarm_mutex_locked);
488
489 assert(epoch != MAX_U32);
490 if(epoch == 0)
491
492 {
493 epoch = time(NULL);
494 }
495
496 alarm_time_node *time_prev = &time_list;
497 alarm_time_node *time_node = time_list.next;
498
499 // find a node at or after the one we need
500
501 while(time_node->epoch < epoch)
502 {
503 time_prev = time_node;
504 time_node = time_node->next;
505
506 assert(time_node->epoch > time_prev->epoch);
507 }
508
509 // if it's a match, return it
510
511 if(time_node->epoch == epoch)
512 {
513 #if DEBUG
514 log_debug6("alarm_time_create(%T) = %p", epoch, time_node);
515 #endif
516
517 return time_node;
518 }
519 else // else insert a node just after it
520 {
521
522 alarm_time_node *new_time_node = alarm_time_alloc();
523 time_prev->next = new_time_node;
524 new_time_node->next = time_node;
525 new_time_node->epoch = epoch;
526
527 #if DEBUG
528 log_debug6("alarm_time_create(%T) = %p", epoch, new_time_node);
529 #endif
530
531 return new_time_node;
532 }
533 }
534
535 void
alarm_init()536 alarm_init()
537 {
538 if(ptr_vector_size(&alarm_handles) == 0)
539 {
540 ptr_vector_resize(&alarm_handles, 64);
541
542 alarm_event_list_init(&time_list.events); /* init: NO LOCK */
543
544 s32 now_s = time(NULL);
545 now_s += 10;
546 __alarm__approximate_time_10s.tv_sec = now_s;
547 now_s += 20;
548 __alarm__approximate_time_30s.tv_sec = now_s;
549 }
550 }
551
552 void
alarm_finalize()553 alarm_finalize()
554 {
555 ptr_vector to_close = PTR_VECTOR_EMPTY;
556 mutex_lock(&alarm_mutex);
557 #if DEBUG
558 alarm_mutex_locked = TRUE;
559
560 log_debug("alarm: %u handles, %u times, %u sets, %i events",
561 alarm_event_in_handles_count,
562 alarm_event_in_time_count,
563 alarm_event_alarm_set_count,
564 smp_int_get(&alarm_event_instanciated));
565 #endif
566
567 if(ptr_vector_size(&alarm_handles) > 0) // guarantees ptr_vector_last_index(&alarm_handles); is >= 0
568 {
569 intptr alarm_handles_last_index = (intptr)ptr_vector_last_index(&alarm_handles);
570 for(intptr i = 0; i <= alarm_handles_last_index; ++i)
571 {
572 alarm_handle *handle_struct = (alarm_handle*)ptr_vector_get(&alarm_handles, i);
573
574 if(((intptr)handle_struct > alarm_handles_last_index) && ((intptr)handle_struct != (intptr)ALARM_HANDLE_INVALID))
575 {
576 #if DEBUG
577 log_err("alarm: handle %i was not closed at shutdown", i);
578 #else
579 log_debug("alarm: handle %i was not closed at shutdown", i);
580 #endif
581 ptr_vector_append(&to_close, handle_struct);
582 }
583 }
584
585 mutex_unlock(&alarm_mutex);
586
587 for(int i = 0; i <= ptr_vector_last_index(&to_close); ++i)
588 {
589 alarm_handle *handle_struct = (alarm_handle*)ptr_vector_get(&to_close, i);
590 alarm_handle_close(handle_struct);
591 }
592
593 mutex_lock(&alarm_mutex);
594
595 if(alarm_event_list_isempty(&time_list.events))
596 {
597 alarm_event_list_finalize(&time_list.events);
598 }
599 else
600 {
601 log_debug("alarm: event list not empty");
602 }
603 }
604
605 ptr_vector_destroy(&alarm_handles);
606
607 #if DEBUG
608 alarm_mutex_locked = FALSE;
609 #endif
610 mutex_unlock(&alarm_mutex);
611 }
612
613 alarm_t
alarm_open(const u8 * owner_dnsname)614 alarm_open(const u8 *owner_dnsname)
615 {
616 alarm_handle *handle_struct;
617 ZALLOC_OBJECT_OR_DIE(handle_struct, alarm_handle, ALARM_HANDLE_TAG);
618
619 #if DEBUG
620 memset(handle_struct, 0xac, sizeof(alarm_handle));
621 #endif
622
623 alarm_event_list_init(&handle_struct->events); /* newly allocated: NO LOCK */
624 handle_struct->owner_dnsname = owner_dnsname;
625
626 mutex_lock(&alarm_mutex);
627 #if DEBUG
628 alarm_mutex_locked = TRUE;
629 #endif
630
631 intptr h;
632
633 if(alarm_handles_next_free >= 0)
634 {
635 h = (intptr)alarm_handles_next_free; // unsigned value
636 // get the next one if any
637 alarm_handles_next_free = (intptr)ptr_vector_get(&alarm_handles, h);
638 ptr_vector_set(&alarm_handles, (intptr)h, handle_struct);
639 }
640 else
641 {
642 ptr_vector_append(&alarm_handles, handle_struct);
643 h = (intptr)alarm_handles.offset;
644 }
645
646 #if DEBUG
647 alarm_mutex_locked = FALSE;
648 #endif
649 mutex_unlock(&alarm_mutex);
650
651 log_debug("alarm_open(%{dnsname}) opened alarm with handle %x", handle_struct->owner_dnsname, (int)h);
652
653 return (alarm_t)h;
654 }
655
656 static alarm_handle *
alarm_get_struct_from_handle(alarm_t hndl)657 alarm_get_struct_from_handle(alarm_t hndl)
658 {
659 assert(alarm_mutex_locked);
660
661 if((hndl > alarm_handles.offset) || (hndl < 0))
662 {
663 /* ERROR ! */
664
665 #if DEBUG
666 log_debug("invalid alarm handle: %x", hndl);
667 #endif
668
669 return NULL;
670 }
671
672 alarm_handle *handle_struct = ptr_vector_get(&alarm_handles, hndl);
673
674 return handle_struct;
675 }
676
677 static void
alarm_clear_struct_from_handle(alarm_t hndl)678 alarm_clear_struct_from_handle(alarm_t hndl)
679 {
680 assert(alarm_mutex_locked);
681
682 if((hndl > alarm_handles.offset) || (hndl < 0))
683 {
684 /* ERROR ! */
685
686 #if DEBUG
687 log_debug("invalid alarm handle: %x", hndl);
688 #endif
689
690 return;
691 }
692
693 ptr_vector_set(&alarm_handles, hndl, (void*)(intptr)alarm_handles_next_free);
694 alarm_handles_next_free = (intptr)hndl;
695 }
696
697 static void
alarm_handle_close(alarm_handle * handle_struct)698 alarm_handle_close(alarm_handle *handle_struct)
699 {
700 alarm_event_node *node = handle_struct->events.first;
701 #if DEBUG
702 #if HAS_ALARM_DUMP
703 alarm_log_dump_nolock(FALSE);
704 #endif
705 u32 removed_events = 0;
706 #endif
707
708 bool obsolete_times = FALSE;
709
710 while(node != NULL)
711 {
712 alarm_event_node *node_next = node->hndl_next;
713
714 alarm_time_node *time_node = alarm_time_get(node->epoch);
715
716 if(time_node != NULL)
717 {
718 alarm_event_list *time_list = &time_node->events;
719
720 alarm_event_remove(&handle_struct->events, time_list, node);
721
722 if(alarm_event_list_isempty(&time_node->events))
723 {
724 obsolete_times = TRUE;
725 }
726
727 #if DEBUG
728 ++removed_events;
729 #endif
730 }
731
732 if(node->function != NULL)
733 {
734 node->function(node->args, TRUE);
735 }
736
737 alarm_event_free(node);
738
739 node = node_next;
740 }
741
742 // clear the obsolete times
743
744 #if DEBUG
745 #if HAS_ALARM_DUMP
746 alarm_log_dump_nolock(FALSE);
747 #endif
748 #endif
749
750 if(obsolete_times)
751 {
752
753 alarm_time_node *time_node_prev = &time_list;
754 alarm_time_node *time_node = time_node_prev->next;
755
756 while(time_node->next != NULL)
757 {
758 if(!alarm_event_list_isempty(&time_node->events))
759 {
760 time_node_prev = time_node;
761 time_node = time_node->next;
762 }
763 else
764 {
765 time_node_prev->next = time_node->next;
766 alarm_time_free(time_node);
767 time_node = time_node_prev->next;
768 }
769 }
770 }
771
772 #if DEBUG
773 log_debug("alarm_handle_close(%p) removed %u events for %{dnsname}",
774 handle_struct,
775 removed_events,
776 handle_struct->owner_dnsname);
777
778 memset(handle_struct, 0xe4, sizeof(alarm_event_list));
779
780 #if HAS_ALARM_DUMP
781 alarm_log_dump_nolock(FALSE);
782 #endif
783 #endif
784
785 ZFREE_OBJECT(handle_struct);
786 }
787
788 void
alarm_close(alarm_t hndl)789 alarm_close(alarm_t hndl)
790 {
791 if(hndl == ALARM_HANDLE_INVALID)
792 {
793 return;
794 }
795
796 mutex_lock(&alarm_mutex);
797
798 #if DEBUG
799 alarm_mutex_locked = TRUE;
800 #endif
801 alarm_handle *handle_struct = alarm_get_struct_from_handle(hndl);
802
803 if(handle_struct == NULL)
804 {
805 #if DEBUG
806 alarm_mutex_locked = FALSE;
807 #endif
808 mutex_unlock(&alarm_mutex);
809
810 log_err("alarm_close(%x) invalid alarm handle", hndl);
811
812 return;
813 }
814
815 log_debug("alarm_close(%x) closing alarm for %{dnsname}", hndl, handle_struct->owner_dnsname);
816
817 alarm_handle_close(handle_struct);
818
819 alarm_clear_struct_from_handle(hndl);
820
821 #if DEBUG
822 alarm_mutex_locked = FALSE;
823 #endif
824
825 mutex_unlock(&alarm_mutex);
826 }
827
828 void
alarm_set(alarm_t hndl,alarm_event_node * desc)829 alarm_set(alarm_t hndl, alarm_event_node *desc)
830 {
831 mutex_lock(&alarm_mutex);
832
833 #if DEBUG
834 alarm_mutex_locked = TRUE;
835 ++alarm_event_alarm_set_count;
836 #endif
837
838 // get the handle struct, if it exists
839
840 alarm_handle *handle_struct = alarm_get_struct_from_handle(hndl);
841
842 if(handle_struct == NULL)
843 {
844
845 #if DEBUG
846 alarm_mutex_locked = FALSE;
847 #endif
848
849 mutex_unlock(&alarm_mutex);
850
851 log_err("alarm_set(%p,%x = '%s') invalid alarm handle", hndl, desc, STRNULL(desc->text));
852
853 return;
854 }
855
856 if(desc->epoch == MAX_U32)
857 {
858
859 #if DEBUG
860 alarm_mutex_locked = FALSE;
861 #endif
862
863 mutex_unlock(&alarm_mutex);
864
865 log_debug("alarm_set(%p,%x = '%s') alarm set for doomsday.", hndl, desc, STRNULL(desc->text));
866
867 return;
868 }
869
870 alarm_event_list *head = &handle_struct->events;
871
872 #if DEBUG
873 log_debug("alarm_set: %p: at %T, for '%{dnsname}' call key=%x '%s', %p(%p) (call #%i)", desc, desc->epoch, handle_struct->owner_dnsname, desc->key, STRNULL(desc->text), desc->function, desc->args, alarm_event_alarm_set_count);
874 #endif
875
876 if(desc->flags != ALARM_DUP_NOP)
877 {
878 /* Cleanup first */
879
880 if(desc->flags == ALARM_DUP_REMOVE_EARLIER)
881 {
882 alarm_event_node* node = head->first;
883
884 while(node != head->last)
885 {
886 /// the list is not sorted by time, as in practice there are relatively few events registered by handle
887
888 if(node->key == desc->key)
889 {
890 log_debug("alarm_set: %p: dropping earliest dup", desc);
891
892 if(desc->epoch < node->epoch)
893 {
894 /* desc is earlier : cancel and destroy */
895
896 if(desc->function != NULL)
897 {
898 desc->function(desc->args, TRUE);
899 }
900
901 alarm_event_free(desc);
902
903 #if DEBUG
904 alarm_mutex_locked = FALSE;
905 #endif
906
907 mutex_unlock(&alarm_mutex);
908
909 return;
910 }
911
912 alarm_event_node *node_next = node->hndl_next;
913
914 alarm_time_node *events_node_at_epoch = alarm_time_get(node->epoch);
915 #if DEBUG
916 log_debug6("about to alarm_event_remove(%p,%p,%p %08x '%s' %T) (earlier)", hndl, &events_node_at_epoch->events, node, node->key, node->text, node->epoch);
917 #endif
918 yassert(events_node_at_epoch != NULL);
919 alarm_event_remove(head, &events_node_at_epoch->events, node);
920
921 // cancel the event
922 node->function(node->args, TRUE);
923 ZFREE_OBJECT(node);
924 node = node_next;
925 }
926 else
927 {
928 node = node->hndl_next;
929 }
930 }
931 }
932 else
933 {
934 alarm_event_node* node = head->first;
935 while(node != head->last)
936 {
937 if(node->key == desc->key)
938 {
939 #if DEBUG
940 log_debug("alarm_set: %p: dropping latest dup", desc);
941 #endif
942 if(desc->epoch > node->epoch)
943 {
944 /* desc is later */
945
946 if(desc->function != NULL)
947 {
948 desc->function(desc->args, TRUE);
949 }
950 alarm_event_free(desc);
951
952 #if DEBUG
953 alarm_mutex_locked = FALSE;
954 #endif
955
956 mutex_unlock(&alarm_mutex);
957
958 return;
959 }
960
961 alarm_event_node *node_next = node->hndl_next;
962 alarm_time_node *events_node_at_epoch = alarm_time_get(node->epoch);
963 #if DEBUG
964 log_debug6("alarm_set: about to alarm_event_remove(%p,%p,%p %08x '%s' %T) (latest)", head, &events_node_at_epoch->events, node, node->key, node->text, node->epoch);
965 #endif
966 yassert(events_node_at_epoch != NULL);
967 alarm_event_remove(head, &events_node_at_epoch->events, node);
968
969 // cancel the event
970 node->function(node->args, TRUE);
971 ZFREE_OBJECT(node);
972 node = node_next;
973 }
974 else
975 {
976 node = node->hndl_next;
977 }
978 }
979 }
980 }
981
982 #if DEBUG
983 log_debug("alarm_set: %p: added", desc);
984 #endif
985
986 /* Create/get the time head */
987
988 alarm_time_node *time_node = alarm_time_create(desc->epoch);
989
990 /* Link desc at the end of time list and in the hndl list */
991
992 desc->handle = hndl;
993 alarm_event_append(head, &time_node->events, desc);
994
995 #if DEBUG
996 alarm_mutex_locked = FALSE;
997 #endif
998
999 mutex_unlock(&alarm_mutex);
1000 }
1001
1002 void
alarm_run_tick(u32 epoch)1003 alarm_run_tick(u32 epoch)
1004 {
1005 assert(epoch != MAX_U32);
1006
1007 mutex_lock(&alarm_mutex);
1008
1009 #if DEBUG
1010 alarm_mutex_locked = TRUE;
1011 #endif
1012
1013 #if DEBUG
1014 #if HAS_ALARM_DUMP
1015 alarm_log_dump_nolock(TRUE);
1016 #endif
1017 #endif
1018 s32 now_s;
1019 s64 fetch_start = timeus_and_s(&now_s);
1020 s32 event_count = 0;
1021
1022 now_s += 10;
1023 __alarm__approximate_time_10s.tv_sec = now_s;
1024 now_s += 20;
1025 __alarm__approximate_time_30s.tv_sec = now_s;
1026
1027 // while the time node is in the past or the present
1028
1029 alarm_event_node event_dummy;
1030 alarm_event_node *event_stack = &event_dummy;
1031
1032 for(;;)
1033 {
1034 // detach the node
1035
1036 alarm_time_node *time_node = time_list.next;
1037
1038 if(time_node->epoch > epoch)
1039 {
1040 break;
1041 }
1042
1043 time_list.next = time_node->next;
1044
1045 // while there are events in the time node
1046
1047 while(!alarm_event_list_isempty(&time_node->events))
1048 {
1049 // process all the events at that time
1050
1051 // take the next event of the time node
1052 alarm_event_node *event = alarm_event_list_removefirst(&time_node->events);
1053
1054 event_stack->time_next = event;
1055 event_stack = event;
1056
1057 ++event_count;
1058 }
1059
1060 yassert(time_node->events.first->time_prev == NULL);
1061 yassert(time_node->events.last->time_next == NULL);
1062 yassert(time_node->events.first == time_node->events.last);
1063
1064 #if DEBUG
1065 log_debug("alarm: releasing time node %d@%p, next time node is %d@%p",
1066 time_node->epoch, time_node,
1067 time_node->next->epoch, time_node->next
1068 );
1069 #endif
1070
1071 alarm_time_free(time_node);
1072 }
1073
1074 s64 fetch_stop = timeus();
1075
1076 double fetch_delta_ms = (double)(fetch_stop - fetch_start);
1077 fetch_delta_ms /= 1000.0;
1078
1079 log_debug("alarm: fetched %u events in %.3fms", event_count, fetch_delta_ms);
1080
1081 event_stack->time_next = NULL;
1082
1083 // event_dummy is the head of all events to execute
1084
1085 s64 total_run = 0;
1086
1087 #if DEBUG
1088 #if HAS_ALARM_DUMP
1089 alarm_log_dump_nolock(TRUE);
1090 alarm_mutex_locked = FALSE;
1091 #endif
1092 #endif
1093
1094 mutex_unlock(&alarm_mutex);
1095
1096 ptr_vector rearm = PTR_VECTOR_EMPTY;
1097
1098 for(alarm_event_node *event = event_dummy.time_next; event != NULL; )
1099 {
1100 #if 1
1101 if(!dnscore_shuttingdown())
1102 {
1103 #endif
1104 /* EXECUTE EVENT */
1105
1106 log_debug("alarm: '%s': %p: %p(%p) running (expected for %T)", event->text, event, event->function, event->args, event->epoch);
1107
1108 s64 event_run_start = timeus();
1109
1110 ya_result ret = event->function(event->args, FALSE);
1111
1112 s64 event_run_stop = timeus();
1113
1114 total_run += event_run_stop - event_run_start;
1115 double event_run_delta_ms = (double)(event_run_stop - event_run_start);
1116 event_run_delta_ms /= 1000.0;
1117
1118 log_debug("alarm: '%s': %p: %p(%p) returned %r (%.3fms elapsed)", event->text, event, event->function, event->args, ret, event_run_delta_ms);
1119
1120 alarm_event_node *event_time_next;
1121
1122 if(ret == ALARM_REARM)
1123 {
1124 ptr_vector_append(&rearm, event);
1125 event_time_next = event->time_next;
1126 }
1127 else
1128 {
1129 event_time_next = event->time_next;
1130 alarm_event_free(event);
1131 }
1132
1133 event = event_time_next;
1134 }
1135 else
1136 {
1137 alarm_event_node *event_time_next = event->time_next;
1138
1139 log_debug("alarm: '%s': %p: %p(%p): cancelling (expected for %T)", event->text, event, event->function, event->args, event->epoch);
1140
1141 s64 event_run_start = timeus();
1142 ya_result ret = event->function(event->args, TRUE);
1143 s64 event_run_stop = timeus();
1144
1145 total_run += event_run_stop - event_run_start;
1146 double event_run_delta_ms = (double)(event_run_stop - event_run_start);
1147 event_run_delta_ms /= 1000.0;
1148
1149 log_debug("alarm: '%s': %p: %p(%p) cancelled %r (%.3fms elapsed)", event->text, event, event->function, event->args, ret, event_run_delta_ms);
1150
1151 event = event_time_next;
1152 }
1153 }
1154
1155 for(int i = 0; i <= ptr_vector_last_index(&rearm); ++i)
1156 {
1157 alarm_event_node *event = (alarm_event_node*)ptr_vector_get(&rearm, i);
1158 event->epoch = epoch + 5;
1159 alarm_set(event->handle, event); // can use the handle as it's a re-arm
1160 }
1161 ptr_vector_destroy(&rearm);
1162
1163 double total_run_ms = total_run;
1164 total_run_ms /= 1000.0;
1165
1166 log_debug("alarm: tick times fetch %.3fms + run %.3fms = total %.3fms)", fetch_delta_ms, total_run_ms, fetch_delta_ms + total_run_ms);
1167 }
1168
1169 void
alarm_lock()1170 alarm_lock()
1171 {
1172 mutex_lock(&alarm_mutex);
1173
1174 #if DEBUG
1175 alarm_mutex_locked = TRUE;
1176 #endif
1177
1178 }
1179
1180 void
alarm_unlock()1181 alarm_unlock()
1182 {
1183 #if DEBUG
1184 alarm_mutex_locked = FALSE;
1185 #endif
1186
1187 mutex_unlock(&alarm_mutex);
1188 }
1189
1190 alarm_event_node *
alarm_get_first(alarm_t hndl)1191 alarm_get_first(alarm_t hndl)
1192 {
1193 assert(alarm_mutex_locked);
1194
1195 alarm_handle *handle_struct = alarm_get_struct_from_handle(hndl);
1196
1197 return handle_struct->events.first;
1198 }
1199
1200 #if HAS_ALARM_DUMP
1201
alarm_log_dump_nolock(bool check)1202 void alarm_log_dump_nolock(bool check)
1203 {
1204 #if DEBUG
1205 u32 handles_count = 0;
1206 u32 time_count = 0;
1207 int events_count = smp_int_get(&alarm_event_instanciated);
1208 log_debug("alarm_log_dump: begin %u / %u ; %u ; instantiated=%d",
1209 alarm_event_in_handles_count, alarm_event_in_time_count, alarm_event_alarm_set_count, events_count);
1210
1211 for(int i = 0; i <= ptr_vector_last_index(&alarm_handles); ++i)
1212 {
1213 alarm_handle *handle = (alarm_handle*)ptr_vector_get(&alarm_handles, i);
1214 intptr handle_as_index = (intptr)handle;
1215 if((handle == NULL) || (handle_as_index <= (intptr)ptr_vector_last_index(&alarm_handles)) || (handle_as_index == MAX_U64))
1216 {
1217 continue;
1218 }
1219 log_debug("alarm_handle: %2i@%p: %{dnsname}", i, handle, handle->owner_dnsname);
1220 for(alarm_event_node *node = handle->events.first; node != handle->events.last; node = node->hndl_next)
1221 {
1222 log_debug("alarm_event: %p %08x '%s' %T", node, node->key, node->text, node->epoch);
1223 ++handles_count;
1224 }
1225 }
1226
1227 for(alarm_time_node *time_node = &time_list; time_node != NULL; time_node = time_node->next)
1228 {
1229 log_debug("alarm_time: %T:", time_node->epoch);
1230 for(alarm_event_node *node = time_node->events.first; node != time_node->events.last; node = node->time_next)
1231 {
1232 log_debug("alarm_event: %p %08x '%s' %T", node, node->key, node->text, node->epoch);
1233 ++time_count;
1234 }
1235
1236 yassert(time_node != time_node->next);
1237 }
1238
1239 log_debug("alarm_log_dump: end %u / %u ; instantiated=%d", handles_count, time_count, events_count);
1240
1241 if(check)
1242 {
1243 yassert(handles_count == alarm_event_in_handles_count);
1244 yassert(time_count == alarm_event_in_time_count);
1245 }
1246 #endif
1247 }
1248
alarm_log_dump()1249 void alarm_log_dump()
1250 {
1251 mutex_lock(&alarm_mutex);
1252
1253 alarm_log_dump_nolock(FALSE);
1254
1255 mutex_unlock(&alarm_mutex);
1256 }
1257
1258 #endif
1259
1260 /** @} */
1261