1 /*
2 * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #include <stdlib.h>
11 #include "internal/event_queue.h"
12 #include "crypto/sparse_array.h"
13 #include "ssl_local.h"
14
15 struct ossl_event_queue_st {
16 PRIORITY_QUEUE_OF(OSSL_EVENT) *timed_events;
17 PRIORITY_QUEUE_OF(OSSL_EVENT) *now_events;
18 };
19
event_compare_times(const OSSL_EVENT * a,const OSSL_EVENT * b)20 static int event_compare_times(const OSSL_EVENT *a, const OSSL_EVENT *b)
21 {
22 return ossl_time_compare(a->when, b->when);
23 }
24
event_compare_priority(const OSSL_EVENT * a,const OSSL_EVENT * b)25 static int event_compare_priority(const OSSL_EVENT *a, const OSSL_EVENT *b)
26 {
27 if (a->priority > b->priority)
28 return -1;
29 if (a->priority < b->priority)
30 return 1;
31 return 0;
32 }
33
ossl_event_queue_new(void)34 OSSL_EVENT_QUEUE *ossl_event_queue_new(void)
35 {
36 OSSL_EVENT_QUEUE *r = OPENSSL_malloc(sizeof(*r));
37
38 if (r != NULL) {
39 r->timed_events = ossl_pqueue_OSSL_EVENT_new(&event_compare_times);
40 r->now_events = ossl_pqueue_OSSL_EVENT_new(&event_compare_priority);
41 if (r->timed_events == NULL || r->now_events == NULL) {
42 ossl_event_queue_free(r);
43 return NULL;
44 }
45 }
46 return r;
47 }
48
ossl_event_free(OSSL_EVENT * event)49 void ossl_event_free(OSSL_EVENT *event)
50 {
51 if (event != NULL) {
52 if (event->flag_dynamic)
53 OPENSSL_free(event);
54 else
55 event->queue = NULL;
56 }
57 }
58
event_queue_free(PRIORITY_QUEUE_OF (OSSL_EVENT)* queue)59 static void event_queue_free(PRIORITY_QUEUE_OF(OSSL_EVENT) *queue)
60 {
61 OSSL_EVENT *e;
62
63 if (queue != NULL) {
64 while ((e = ossl_pqueue_OSSL_EVENT_pop(queue)) != NULL)
65 ossl_event_free(e);
66 ossl_pqueue_OSSL_EVENT_free(queue);
67 }
68 }
69
ossl_event_queue_free(OSSL_EVENT_QUEUE * queue)70 void ossl_event_queue_free(OSSL_EVENT_QUEUE *queue)
71 {
72 if (queue != NULL) {
73 event_queue_free(queue->now_events);
74 event_queue_free(queue->timed_events);
75 OPENSSL_free(queue);
76 }
77 }
78
79 static ossl_inline
event_queue_add(OSSL_EVENT_QUEUE * queue,OSSL_EVENT * event)80 int event_queue_add(OSSL_EVENT_QUEUE *queue, OSSL_EVENT *event)
81 {
82 PRIORITY_QUEUE_OF(OSSL_EVENT) *pq =
83 ossl_time_compare(event->when, ossl_time_now()) <= 0
84 ? queue->now_events
85 : queue->timed_events;
86
87 if (ossl_pqueue_OSSL_EVENT_push(pq, event, &event->ref)) {
88 event->queue = pq;
89 return 1;
90 }
91 return 0;
92 }
93
94 static ossl_inline
ossl_event_set(OSSL_EVENT * event,uint32_t type,uint32_t priority,OSSL_TIME when,void * ctx,void * payload,size_t payload_size)95 void ossl_event_set(OSSL_EVENT *event, uint32_t type, uint32_t priority,
96 OSSL_TIME when, void *ctx,
97 void *payload, size_t payload_size)
98 {
99 event->type = type;
100 event->priority = priority;
101 event->when = when;
102 event->ctx = ctx;
103 event->payload = payload;
104 event->payload_size = payload_size;
105 }
106
ossl_event_queue_add_new(OSSL_EVENT_QUEUE * queue,uint32_t type,uint32_t priority,OSSL_TIME when,void * ctx,void * payload,size_t payload_size)107 OSSL_EVENT *ossl_event_queue_add_new(OSSL_EVENT_QUEUE *queue,
108 uint32_t type, uint32_t priority,
109 OSSL_TIME when, void *ctx,
110 void *payload, size_t payload_size)
111 {
112 OSSL_EVENT *e = OPENSSL_malloc(sizeof(*e));
113
114 if (e == NULL || queue == NULL)
115 return NULL;
116 ossl_event_set(e, type, priority, when, ctx, payload, payload_size);
117 e->flag_dynamic = 1;
118 if (event_queue_add(queue, e))
119 return e;
120 OPENSSL_free(e);
121 return NULL;
122 }
123
ossl_event_queue_add(OSSL_EVENT_QUEUE * queue,OSSL_EVENT * event,uint32_t type,uint32_t priority,OSSL_TIME when,void * ctx,void * payload,size_t payload_size)124 int ossl_event_queue_add(OSSL_EVENT_QUEUE *queue, OSSL_EVENT *event,
125 uint32_t type, uint32_t priority,
126 OSSL_TIME when, void *ctx,
127 void *payload, size_t payload_size)
128 {
129 if (event == NULL || queue == NULL)
130 return 0;
131 ossl_event_set(event, type, priority, when, ctx, payload, payload_size);
132 event->flag_dynamic = 0;
133 return event_queue_add(queue, event);
134 }
135
ossl_event_queue_remove(OSSL_EVENT_QUEUE * queue,OSSL_EVENT * event)136 int ossl_event_queue_remove(OSSL_EVENT_QUEUE *queue, OSSL_EVENT *event)
137 {
138 if (event != NULL && event->queue != NULL) {
139 ossl_pqueue_OSSL_EVENT_remove(event->queue, event->ref);
140 event->queue = NULL;
141 }
142 return 1;
143 }
144
ossl_event_time_until(const OSSL_EVENT * event)145 OSSL_TIME ossl_event_time_until(const OSSL_EVENT *event)
146 {
147 if (event == NULL)
148 return ossl_time_infinite();
149 return ossl_time_subtract(event->when, ossl_time_now());
150 }
151
ossl_event_queue_time_until_next(const OSSL_EVENT_QUEUE * queue)152 OSSL_TIME ossl_event_queue_time_until_next(const OSSL_EVENT_QUEUE *queue)
153 {
154 if (queue == NULL)
155 return ossl_time_infinite();
156 if (ossl_pqueue_OSSL_EVENT_num(queue->now_events) > 0)
157 return ossl_time_zero();
158 return ossl_event_time_until(ossl_pqueue_OSSL_EVENT_peek(queue->timed_events));
159 }
160
ossl_event_queue_postpone_until(OSSL_EVENT_QUEUE * queue,OSSL_EVENT * event,OSSL_TIME when)161 int ossl_event_queue_postpone_until(OSSL_EVENT_QUEUE *queue,
162 OSSL_EVENT *event,
163 OSSL_TIME when)
164 {
165 if (ossl_event_queue_remove(queue, event)) {
166 event->when = when;
167 return event_queue_add(queue, event);
168 }
169 return 0;
170 }
171
ossl_event_queue_get1_next_event(OSSL_EVENT_QUEUE * queue,OSSL_EVENT ** event)172 int ossl_event_queue_get1_next_event(OSSL_EVENT_QUEUE *queue,
173 OSSL_EVENT **event)
174 {
175 OSSL_TIME now = ossl_time_now();
176 OSSL_EVENT *e;
177
178 /* Check for expired timer based events and convert them to now events */
179 while ((e = ossl_pqueue_OSSL_EVENT_peek(queue->timed_events)) != NULL
180 && ossl_time_compare(e->when, now) <= 0) {
181 e = ossl_pqueue_OSSL_EVENT_pop(queue->timed_events);
182 if (!ossl_pqueue_OSSL_EVENT_push(queue->now_events, e, &e->ref)) {
183 e->queue = NULL;
184 return 0;
185 }
186 }
187
188 /*
189 * Get next event from the now queue.
190 * The pop returns NULL when there is none.
191 */
192 *event = ossl_pqueue_OSSL_EVENT_pop(queue->now_events);
193 return 1;
194 }
195