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