ssl: implement an event queue

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18345)
This commit is contained in:
Pauli 2022-05-10 12:05:11 +10:00
parent 16612c1929
commit e6be47e427
3 changed files with 358 additions and 1 deletions

View File

@ -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

View File

@ -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

194
ssl/event_queue.c Normal file
View File

@ -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 <stdlib.h>
#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;
}