Add a module to manage a list of connections

This commit is contained in:
Thomas Kolb 2024-11-02 18:30:25 +01:00
parent a0623668a7
commit ec3244a61f
2 changed files with 220 additions and 0 deletions

View file

@ -0,0 +1,142 @@
#include <string.h>
#include <assert.h>
#include "connection_list.h"
#include "results.h"
#define SEQ_NR_MASK 0xF
// find the location where the timestamp should be inserted to maintain
// ascending timestamp order and return the then-previous entry.
//
// If NULL is returned, the list is either empty or the entry should be
// inserted before the head. This must be checked externally.
static connection_list_entry_t* find_prev_for_timestamp(connection_list_t *list, uint64_t timestamp)
{
if(!list->head) {
return NULL;
}
if(timestamp < list->head->next_access_time) {
return NULL;
}
connection_list_entry_t *prev = list->head;
while(prev->next) {
if(prev->next_access_time < timestamp
&& prev->next->next_access_time >= timestamp) {
// location found!
break;
}
prev = prev->next;
}
// either the correct location was found or prev now points to the last entry
// in the list.
return prev;
}
result_t connection_list_init(connection_list_t *list)
{
list->head = NULL;
return OK;
}
void connection_list_destroy(connection_list_t *list)
{
// delete all list entries
while(list->head) {
connection_list_entry_t *next = list->head->next;
free(list->head);
list->head = next;
}
}
result_t connection_list_insert(connection_list_t *list, const connection_ctx_t *conn, uint64_t next_access_time)
{
connection_list_entry_t *new_entry = malloc(sizeof(connection_list_entry_t));
if(!new_entry) {
return ERR_NO_MEM;
}
new_entry->connection = *conn;
new_entry->next_access_time = next_access_time;
connection_list_entry_t *prev = find_prev_for_timestamp(list, next_access_time);
if(!prev) {
// the new entry should become the lists head (also works if the list is empty!)
new_entry->next = list->head;
list->head = new_entry;
} else {
// insert after prev
new_entry->next = prev->next;
prev->next = new_entry;
}
return OK;
}
connection_list_entry_t* connection_list_get_head(connection_list_t *list)
{
return list->head;
}
result_t connection_list_reschedule_head(connection_list_t *list, uint64_t next_access_timestamp)
{
if(!list->head) {
return ERR_INVALID_STATE;
}
if(!list->head->next) {
// nothing to do because there is only one entry.
return OK;
}
if(next_access_timestamp < list->head->next->next_access_time) {
// the head does not need to be moved because the new timestamp is still
// smaller than that of the second entry => only update the timestamp.
list->head->next_access_time = next_access_timestamp;
return OK;
}
// detach the entry from the list
connection_list_entry_t *reloc_entry = list->head;
list->head = list->head->next;
// find the location where the entry should be reinserted
connection_list_entry_t *prev = find_prev_for_timestamp(list, next_access_timestamp);
if(!prev) {
// the relocated entry should become the lists head (also works if the list is empty!)
reloc_entry->next = list->head;
list->head = reloc_entry;
} else {
// insert after prev
reloc_entry->next = prev->next;
prev->next = reloc_entry;
}
return OK;
}
result_t connection_list_delete_head(connection_list_t *list)
{
if(!list->head) {
return ERR_INVALID_STATE;
}
connection_list_entry_t *new_head = list->head->next;
free(list->head);
list->head = new_head;
return OK;
}

View file

@ -0,0 +1,78 @@
/*
* This file contains functions to manage a list of layer 2 connections for
* scheduling purposes.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Copyright (C) 2024 Thomas Kolb
*/
#ifndef CONNECTION_LIST_H
#define CONNECTION_LIST_H
#include <results.h>
#include <stdbool.h>
#include "connection.h"
typedef struct connection_list_entry_s {
uint64_t next_access_time; //!< When to next activate this connection
connection_ctx_t connection; //!< The actual connection entry
struct connection_list_entry_s *next; //!< pointer to the next list element
} connection_list_entry_t;
typedef struct connection_list_s {
connection_list_entry_t *head; //!< pointer to the first list element
} connection_list_t;
/*!\brief Initialize a connection list.
*
* \param list The list to initialize.
* \returns OK if everything worked or a fitting error code.
*/
result_t connection_list_init(connection_list_t *list);
/*!\brief Destroy the given layer 2 connection list, deleting all internal data.
*/
void connection_list_destroy(connection_list_t *list);
/*!\brief Insert a connection context into the list.
*
* The connection context will be copied internally. The original copy should
* no longer be used after the context was inserted into the list.
*
* \param list Pointer to the list where the entry shall be inserted.
* \param conn Pointer to the connection context.
* \param next_access_time Timestamp when this connection should be activated.
* \returns OK if everything worked or a fitting error code.
*/
result_t connection_list_insert(connection_list_t *list, const connection_ctx_t *conn, uint64_t next_access_time);
/*!\brief Get the head (first entry) of the list.
*
* \param list Pointer to the list to access.
*
* \returns A pointer to the head entry or NULL if the list is empty.
*/
connection_list_entry_t* connection_list_get_head(connection_list_t *list);
/*!\brief Re-schedule the current head entry.
*
* \param list Pointer to the list to access.
* \param next_access_time Timestamp when this connection should be activated again.
*
* \returns OK if everything worked or a fitting error code.
*/
result_t connection_list_reschedule_head(connection_list_t *list, uint64_t next_access_timestamp);
/*!\brief Delete the current head entry.
*
* \param list Pointer to the list to access.
*
* \returns OK if everything worked or a fitting error code.
*/
result_t connection_list_delete_head(connection_list_t *list);
#endif // CONNECTION_LIST_H