From: Pauli Date: Tue, 10 May 2022 02:05:11 +0000 (+1000) Subject: ssl: implement an event queue X-Git-Tag: openssl-3.2.0-alpha1~2435 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e6be47e427fb6650f274c418947e7665fbe08889;p=thirdparty%2Fopenssl.git ssl: implement an event queue Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/18345) --- diff --git a/include/internal/event_queue.h b/include/internal/event_queue.h new file mode 100644 index 00000000000..bc77e5acaa7 --- /dev/null +++ b/include/internal/event_queue.h @@ -0,0 +1,163 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_INTERNAL_EVENT_QUEUE_H +# define OSSL_INTERNAL_EVENT_QUEUE_H +# pragma once + +# include "internal/priority_queue.h" +# include "internal/time.h" + +/* + * Opaque type holding an event. + */ +typedef struct ossl_event_st OSSL_EVENT; + +DEFINE_PRIORITY_QUEUE_OF(OSSL_EVENT); + +/* + * Public type representing an event queue, the underlying structure being + * opaque. + */ +typedef struct ossl_event_queue_st OSSL_EVENT_QUEUE; + +/* + * Public type representing a event queue entry. + * It is (internally) public so that it can be embedded into other structures, + * it should otherwise be treated as opaque. + */ +struct ossl_event_st { + uint32_t type; /* What type of event this is */ + uint32_t priority; /* What priority this event has */ + OSSL_TIME when; /* When the event is scheduled to happen */ + void *ctx; /* User argument passed to call backs */ + void *payload; /* Event specific data of unknown kind */ + size_t payload_size; /* Length (in bytes) of event specific data */ + + /* These fields are for internal use only */ + PRIORITY_QUEUE_OF(OSSL_EVENT) *queue; /* Queue containing this event */ + size_t ref; /* ID for this event */ + unsigned int flag_dynamic : 1; /* Malloced or not? */ +}; + +/* + * Utility function to populate an event structure and add it to the queue + */ +int 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); + +/* + * Utility functions to extract event fields + */ +static ossl_unused ossl_inline +uint32_t ossl_event_get_type(const OSSL_EVENT *event) +{ + return event->type; +} + +static ossl_unused ossl_inline +uint32_t ossl_event_get_priority(const OSSL_EVENT *event) +{ + return event->priority; +} + +static ossl_unused ossl_inline +OSSL_TIME ossl_event_get_when(const OSSL_EVENT *event) +{ + return event->when; +} + +static ossl_unused ossl_inline +void *ossl_event_get0_ctx(const OSSL_EVENT *event) +{ + return event->ctx; +} + +static ossl_unused ossl_inline +void *ossl_event_get0_payload(const OSSL_EVENT *event, size_t *length) +{ + if (length != NULL) + *length = event->payload_size; + return event->payload; +} + +/* + * Create and free a queue. + */ +OSSL_EVENT_QUEUE *ossl_event_queue_new(void); +void ossl_event_queue_free(OSSL_EVENT_QUEUE *queue); + +/* + * Schedule a new event into an event queue. + * + * The event parameters are taken from the function arguments. + * + * The function reutrns NULL on failure and the added event on success. + */ +OSSL_EVENT *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) +; + +/* + * Schedule an event into an event queue. + * + * The event parameters are taken from the function arguments. + * + * The function reutrns 0 on failure and 1 on success. + */ +int 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); + +/* + * Delete an event from the queue. + * This will cause the early deletion function to be called if it is non-NULL. + * A pointer to the event structure is returned. + */ +int ossl_event_queue_remove(OSSL_EVENT_QUEUE *queue, OSSL_EVENT *event); + +/* + * Free a dynamic event. + * Is a NOP for a static event. + */ +void ossl_event_free(OSSL_EVENT *event); + +/* + * Return the time until the next event for the specified event, if the event's + * time is past, zero is returned. Once activated, the event reference becomes + * invalid and this function becomes undefined. + */ +OSSL_TIME ossl_event_time_until(const OSSL_EVENT *event); + +/* + * Return the time until the next event in the queue. + * If the next event is in the past, zero is returned. + */ +OSSL_TIME ossl_event_queue_time_until_next(const OSSL_EVENT_QUEUE *queue); + +/* + * Postpone an event to trigger at the specified time. + * If the event has triggered, this function's behaviour is undefined. + */ +int ossl_event_queue_postpone_until(OSSL_EVENT_QUEUE *queue, + OSSL_EVENT *event, + OSSL_TIME when); + +/* + * Return the next event to process. + */ +int ossl_event_queue_get1_next_event(OSSL_EVENT_QUEUE *queue, + OSSL_EVENT **event); + +#endif diff --git a/ssl/build.info b/ssl/build.info index 6b6692de2fc..2ecf5ccd225 100644 --- a/ssl/build.info +++ b/ssl/build.info @@ -43,7 +43,7 @@ IF[{- !$disabled{'deprecated-3.0'} -}] ENDIF IF[{- !$disabled{quic} -}] - SOURCE[../libssl]=priority_queue.c + SOURCE[../libssl]=priority_queue.c event_queue.c ENDIF DEFINE[../libssl]=$AESDEF diff --git a/ssl/event_queue.c b/ssl/event_queue.c new file mode 100644 index 00000000000..f828d5280fb --- /dev/null +++ b/ssl/event_queue.c @@ -0,0 +1,194 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/event_queue.h" +#include "crypto/sparse_array.h" +#include "ssl_local.h" + +struct ossl_event_queue_st { + PRIORITY_QUEUE_OF(OSSL_EVENT) *timed_events; + PRIORITY_QUEUE_OF(OSSL_EVENT) *now_events; +}; + +static int event_compare_times(const OSSL_EVENT *a, const OSSL_EVENT *b) +{ + return ossl_time_compare(a->when, b->when); +} + +static int event_compare_priority(const OSSL_EVENT *a, const OSSL_EVENT *b) +{ + if (a->priority > b->priority) + return -1; + if (a->priority < b->priority) + return 1; + return 0; +} + +OSSL_EVENT_QUEUE *ossl_event_queue_new(void) +{ + OSSL_EVENT_QUEUE *r = OPENSSL_malloc(sizeof(*r)); + + if (r != NULL) { + r->timed_events = ossl_pqueue_OSSL_EVENT_new(&event_compare_times); + r->now_events = ossl_pqueue_OSSL_EVENT_new(&event_compare_priority); + if (r->timed_events == NULL || r->now_events == NULL) { + ossl_event_queue_free(r); + return NULL; + } + } + return r; +} + +void ossl_event_free(OSSL_EVENT *event) +{ + if (event != NULL) { + if (event->flag_dynamic) + OPENSSL_free(event); + else + event->queue = NULL; + } +} + +static void event_queue_free(PRIORITY_QUEUE_OF(OSSL_EVENT) *queue) +{ + OSSL_EVENT *e; + + if (queue != NULL) { + while ((e = ossl_pqueue_OSSL_EVENT_pop(queue)) != NULL) + ossl_event_free(e); + ossl_pqueue_OSSL_EVENT_free(queue); + } +} + +void ossl_event_queue_free(OSSL_EVENT_QUEUE *queue) +{ + if (queue != NULL) { + event_queue_free(queue->now_events); + event_queue_free(queue->timed_events); + OPENSSL_free(queue); + } +} + +static ossl_inline +int event_queue_add(OSSL_EVENT_QUEUE *queue, OSSL_EVENT *event) +{ + PRIORITY_QUEUE_OF(OSSL_EVENT) *pq = + ossl_time_compare(event->when, ossl_time_now()) <= 0 + ? queue->now_events + : queue->timed_events; + + if (ossl_pqueue_OSSL_EVENT_push(pq, event, &event->ref)) { + event->queue = pq; + return 1; + } + return 0; +} + +static ossl_inline +void ossl_event_set(OSSL_EVENT *event, uint32_t type, uint32_t priority, + OSSL_TIME when, void *ctx, + void *payload, size_t payload_size) +{ + event->type = type; + event->priority = priority; + event->when = when; + event->ctx = ctx; + event->payload = payload; + event->payload_size = payload_size; +} + +OSSL_EVENT *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) +{ + OSSL_EVENT *e = OPENSSL_malloc(sizeof(*e)); + + if (e == NULL || queue == NULL) + return NULL; + ossl_event_set(e, type, priority, when, ctx, payload, payload_size); + e->flag_dynamic = 1; + if (event_queue_add(queue, e)) + return e; + OPENSSL_free(e); + return NULL; +} + +int 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) +{ + if (event == NULL || queue == NULL) + return 0; + ossl_event_set(event, type, priority, when, ctx, payload, payload_size); + event->flag_dynamic = 0; + return event_queue_add(queue, event); +} + +int ossl_event_queue_remove(OSSL_EVENT_QUEUE *queue, OSSL_EVENT *event) +{ + if (event != NULL && event->queue != NULL) { + ossl_pqueue_OSSL_EVENT_remove(event->queue, event->ref); + event->queue = NULL; + } + return 1; +} + +OSSL_TIME ossl_event_time_until(const OSSL_EVENT *event) +{ + if (event == NULL) + return OSSL_TIME_INFINITY; + return ossl_time_subtract(event->when, ossl_time_now()); +} + +OSSL_TIME ossl_event_queue_time_until_next(const OSSL_EVENT_QUEUE *queue) +{ + if (queue == NULL) + return OSSL_TIME_INFINITY; + if (ossl_pqueue_OSSL_EVENT_num(queue->now_events) > 0) + return OSSL_TIME_IMMEDIATE; + return ossl_event_time_until(ossl_pqueue_OSSL_EVENT_peek(queue->timed_events)); +} + +int ossl_event_queue_postpone_until(OSSL_EVENT_QUEUE *queue, + OSSL_EVENT *event, + OSSL_TIME when) +{ + if (ossl_event_queue_remove(queue, event)) { + event->when = when; + return event_queue_add(queue, event); + } + return 0; +} + +int ossl_event_queue_get1_next_event(OSSL_EVENT_QUEUE *queue, + OSSL_EVENT **event) +{ + OSSL_TIME now = ossl_time_now(); + OSSL_EVENT *e; + + /* Check for expired timer based events and convert them to now events */ + while ((e = ossl_pqueue_OSSL_EVENT_peek(queue->timed_events)) != NULL + && ossl_time_compare(e->when, now) <= 0) { + e = ossl_pqueue_OSSL_EVENT_pop(queue->timed_events); + if (!ossl_pqueue_OSSL_EVENT_push(queue->now_events, e, &e->ref)) { + e->queue = NULL; + return 0; + } + } + + /* + * Get next event from the now queue. + * The pop returns NULL when there is none. + */ + *event = ossl_pqueue_OSSL_EVENT_pop(queue->now_events); + return 1; +}