Add timed callback dispatcher to security core

Dispatchers are added to a custom linked list while each dispatcher owns a
fibheap of callbacks that are ordered by expiry timestamp. A seperate
fibheap is use for each dispatcher to allow disabling and enabling of
each dispatcher. A new and free function is added that creates a
timed_cd_data object that is used by every public function. A thread is
initialized at object creation instead of using a counter. Add
a protection for the terminate flag.

Signed-off-by: Joao Rebelo <jrebelo@s2e-systems.com>
This commit is contained in:
Joao Rebelo 2019-11-25 13:07:34 +01:00 committed by eboasson
parent a9b9a65e1b
commit 97c3025e54
5 changed files with 1066 additions and 3 deletions

View file

@ -15,6 +15,7 @@ PREPEND(srcs_security_core "${CMAKE_CURRENT_LIST_DIR}/src"
dds_security_plugins.c
shared_secret.c
dds_security_fsm.c
dds_security_timed_cb.c
)
PREPEND(hdrs_public_security_core "${CMAKE_CURRENT_LIST_DIR}/include/security/core"
@ -24,6 +25,7 @@ PREPEND(hdrs_public_security_core "${CMAKE_CURRENT_LIST_DIR}/include/security/co
dds_security_plugins.h
dds_security_fsm.h
shared_secret.h
dds_security_timed_cb.h
)
if(NOT WIN32)
@ -49,10 +51,12 @@ target_include_directories(security_core
)
#target_link_libraries(security_core PRIVATE ddsrt security_api)
if(BUILD_TESTING)
add_subdirectory(tests)
add_subdirectory(tests/plugin_loading)
endif()
install(
DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/include/dds/security/core/"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/dds/security/core/"
COMPONENT dev)
add_subdirectory(tests)
add_subdirectory(tests/plugin_loading)

View file

@ -0,0 +1,183 @@
/*
* Copyright(c) 2006 to 2019 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef DDS_SECURITY_TIMED_CALLBACK_H
#define DDS_SECURITY_TIMED_CALLBACK_H
#include "dds/export.h"
#include "dds/ddsrt/time.h"
#if defined (__cplusplus)
extern "C" {
#endif
/**
* The dispatcher that will trigger the timed callbacks.
*/
struct dds_security_timed_dispatcher_t;
struct dds_security_timed_cb_data_t;
/**
* The callback is triggered by two causes:
* 1. The trigger timeout has been reached.
* 2. The related dispatcher is being deleted.
*/
typedef enum {
DDS_SECURITY_TIMED_CB_KIND_TIMEOUT,
DDS_SECURITY_TIMED_CB_KIND_DELETE
} dds_security_timed_cb_kind;
/**
* Template for the timed callback functions.
* It is NOT allowed to call any t_timed_cb API functions from within this
* callback context.
*
* This will be called when the trigger time of the added callback is reached,
* or if the related dispatcher is deleted. The latter can be used to clean up
* possible callback resources.
*
* @param d Related dispatcher.
* @param kind Triggered by cb timeout or dispatcher deletion.
* @param listener Listener that was provided when enabling the related
* dispatcher (NULL with a deletion trigger).
* @param arg User data, provided when adding a callback to the
* related dispatcher.
*/
typedef void
(*dds_security_timed_cb_t) (
struct dds_security_timed_dispatcher_t *d,
dds_security_timed_cb_kind kind,
void *listener,
void *arg);
DDS_EXPORT struct dds_security_timed_cb_data*
dds_security_timed_cb_new(void);
DDS_EXPORT void
dds_security_timed_cb_free(
struct dds_security_timed_cb_data *dl);
/**
* Create a new dispatcher for timed callbacks.
* The dispatcher is not enabled (see dds_security_timed_dispatcher_enable).
*
* @return New (disabled) timed callbacks dispatcher.
*/
DDS_EXPORT struct dds_security_timed_dispatcher_t*
dds_security_timed_dispatcher_new(
struct dds_security_timed_cb_data *tcb);
/**
* Frees the given dispatcher.
* If the dispatcher contains timed callbacks, then these will be
* triggered with DDS_SECURITY_TIMED_CB_KIND_DELETE and then removed. This
* is done whether the dispatcher is enabled or not.
*
* @param d The dispatcher to free.
*
*/
DDS_EXPORT void
dds_security_timed_dispatcher_free(
struct dds_security_timed_cb_data *tcb,
struct dds_security_timed_dispatcher_t *d);
/**
* Enables a dispatcher for timed callbacks.
*
* Until a dispatcher is enabled, no DDS_SECURITY_TIMED_CB_KIND_TIMEOUT callbacks will
* be triggered.
* As soon as it is enabled, possible stored timed callbacks that are in the
* past will be triggered at that moment.
* Also, from this point on, possible future callbacks will also be triggered
* when the appropriate time has been reached.
*
* A listener argument can be supplied that is returned when the callback
* is triggered. The dispatcher doesn't do anything more with it, so it may
* be NULL.
*
* DDS_SECURITY_TIMED_CB_KIND_DELETE callbacks will always be triggered despite the
* dispatcher being possibly disabled.
*
* @param d The dispatcher to enable.
* @param listener An object that is returned with the callback.
*
*/
DDS_EXPORT void
dds_security_timed_dispatcher_enable(
struct dds_security_timed_cb_data *tcb,
struct dds_security_timed_dispatcher_t *d,
void *listener);
/**
* Disables a dispatcher for timed callbacks.
*
* When a dispatcher is disabled (default after creation), it will not
* trigger any related callbacks. It will still store them, however, so
* that they can be triggered after a (re)enabling.
*
* This is when the callback is actually triggered by a timeout and thus
* its kind is DDS_SECURITY_TIMED_CB_KIND_TIMEOUT. DDS_SECURITY_TIMED_CB_KIND_DELETE callbacks
* will always be triggered despite the dispatcher being possibly disabled.
*
* @param d The dispatcher to disable.
*
*/
DDS_EXPORT void
dds_security_timed_dispatcher_disable(
struct dds_security_timed_cb_data *tcb,
struct dds_security_timed_dispatcher_t *d);
/**
* Adds a timed callback to a dispatcher.
*
* The given callback will be triggered with DDS_SECURITY_TIMED_CB_KIND_TIMEOUT when:
* 1. The dispatcher is enabled and
* 2. The trigger_time has been reached.
*
* If the trigger_time lays in the past, then the callback is still added.
* When the dispatcher is already enabled, it will trigger this 'past'
* callback immediately. Otherwise, the 'past' callback will be triggered
* at the moment that the dispatcher is enabled.
*
* The given callback will be triggered with DDS_SECURITY_TIMED_CB_KIND_DELETE when:
* 1. The related dispatcher is deleted (ignoring enable/disable).
*
* This is done so that possible related callback resources can be freed.
*
* @param d The dispatcher to add the callback to.
* @param cb The actual callback function.
* @param trigger_time A wall-clock time of when to trigger the callback.
* @param arg User data that is provided with the callback.
*
*/
DDS_EXPORT void
dds_security_timed_dispatcher_add(
struct dds_security_timed_cb_data *tcb,
struct dds_security_timed_dispatcher_t *d,
dds_security_timed_cb_t cb,
dds_time_t trigger_time,
void *arg);
#if defined (__cplusplus)
}
#endif
#endif /* DDS_SECURITY_TIMED_CALLBACK_H */

View file

@ -0,0 +1,329 @@
/*
* Copyright(c) 2006 to 2019 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "dds/ddsrt/atomics.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/log.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/time.h"
#include "dds/ddsrt/fibheap.h"
#include "dds/security/core/dds_security_timed_cb.h"
struct dds_security_timed_dispatcher_t
{
bool active;
void *listener;
ddsrt_fibheap_t events;
};
struct list_dispatcher_t
{
struct list_dispatcher_t *next;
struct dds_security_timed_dispatcher_t *dispatcher;
};
struct event_t
{
ddsrt_fibheap_node_t heapnode;
dds_security_timed_cb_t callback;
dds_time_t trigger_time;
void *arg;
};
static int compare_timed_cb_trigger_time(const void *va, const void *vb);
static const ddsrt_fibheap_def_t timed_cb_queue_fhdef = DDSRT_FIBHEAPDEF_INITIALIZER(offsetof(struct event_t, heapnode), compare_timed_cb_trigger_time);
static int compare_timed_cb_trigger_time(const void *va, const void *vb)
{
const struct event_t *a = va;
const struct event_t *b = vb;
return (a->trigger_time == b->trigger_time) ? 0 : (a->trigger_time < b->trigger_time) ? -1 : 1;
}
struct dds_security_timed_cb_data {
ddsrt_mutex_t lock;
ddsrt_cond_t cond;
struct list_dispatcher_t *first_dispatcher_node;
ddsrt_thread_t thread;
bool terminate;
};
static uint32_t timed_dispatcher_thread(
void *tcbv)
{
struct list_dispatcher_t *dispatcher_node;
struct event_t *event;
dds_duration_t timeout;
dds_duration_t remain_time;
struct dds_security_timed_cb_data *tcb = (struct dds_security_timed_cb_data *)tcbv;
ddsrt_mutex_lock(&tcb->lock);
do
{
remain_time = DDS_INFINITY;
for (dispatcher_node = tcb->first_dispatcher_node; dispatcher_node != NULL; dispatcher_node = dispatcher_node->next)
{
/* Just some sanity checks. */
assert(dispatcher_node->dispatcher);
if (dispatcher_node->dispatcher->active)
{
do
{
timeout = DDS_INFINITY;
event = ddsrt_fibheap_min(&timed_cb_queue_fhdef, &dispatcher_node->dispatcher->events);
if (event)
{
/* Just some sanity checks. */
assert(event->callback);
/* Determine the trigger timeout of this callback. */
timeout = event->trigger_time - dds_time();
if (timeout <= 0)
{
/* Trigger callback when related dispatcher is active. */
event->callback(dispatcher_node->dispatcher,
DDS_SECURITY_TIMED_CB_KIND_TIMEOUT,
dispatcher_node->dispatcher->listener,
event->arg);
/* Remove handled event from queue, continue with next. */
ddsrt_fibheap_delete(&timed_cb_queue_fhdef, &dispatcher_node->dispatcher->events, event);
ddsrt_free(event);
}
else if (timeout < remain_time)
{
remain_time = timeout;
}
}
}
while (timeout < 0);
}
}
// tcb->cond condition may be triggered before this thread runs and causes
// this waitfor to wait infinity, hence the check of the tcb->terminate
if (((remain_time > 0) || (remain_time == DDS_INFINITY)) && !tcb->terminate)
{
/* Wait for new event, timeout or the end. */
(void)ddsrt_cond_waitfor(&tcb->cond, &tcb->lock, remain_time);
}
} while (!tcb->terminate);
ddsrt_mutex_unlock(&tcb->lock);
return 0;
}
struct dds_security_timed_cb_data*
dds_security_timed_cb_new()
{
struct dds_security_timed_cb_data *tcb = ddsrt_malloc(sizeof(*tcb));
dds_return_t osres;
ddsrt_threadattr_t attr;
ddsrt_mutex_init(&tcb->lock);
ddsrt_cond_init(&tcb->cond);
tcb->first_dispatcher_node = NULL;
tcb->terminate = false;
ddsrt_threadattr_init(&attr);
osres = ddsrt_thread_create(&tcb->thread, "security_dispatcher", &attr, timed_dispatcher_thread, (void*)tcb);
if (osres != DDS_RETCODE_OK)
{
DDS_FATAL("Cannot create thread security_dispatcher");
}
return tcb;
}
void
dds_security_timed_cb_free(
struct dds_security_timed_cb_data *tcb)
{
ddsrt_mutex_lock(&tcb->lock);
tcb->terminate = true;
ddsrt_mutex_unlock(&tcb->lock);
ddsrt_cond_signal(&tcb->cond);
ddsrt_thread_join(tcb->thread, NULL);
ddsrt_cond_destroy(&tcb->cond);
ddsrt_mutex_destroy(&tcb->lock);
ddsrt_free(tcb);
}
struct dds_security_timed_dispatcher_t*
dds_security_timed_dispatcher_new(
struct dds_security_timed_cb_data *tcb)
{
struct dds_security_timed_dispatcher_t *d;
struct list_dispatcher_t *dispatcher_node_new;
struct list_dispatcher_t *dispatcher_node_wrk;
/* New dispatcher. */
d = ddsrt_malloc(sizeof(struct dds_security_timed_dispatcher_t));
memset(d, 0, sizeof(struct dds_security_timed_dispatcher_t));
ddsrt_fibheap_init(&timed_cb_queue_fhdef, &d->events);
dispatcher_node_new = ddsrt_malloc(sizeof(struct list_dispatcher_t));
memset(dispatcher_node_new, 0, sizeof(struct list_dispatcher_t));
dispatcher_node_new->dispatcher = d;
ddsrt_mutex_lock(&tcb->lock);
/* Append to list */
if (tcb->first_dispatcher_node) {
struct list_dispatcher_t *last = NULL;
for (dispatcher_node_wrk = tcb->first_dispatcher_node; dispatcher_node_wrk != NULL; dispatcher_node_wrk = dispatcher_node_wrk->next) {
last = dispatcher_node_wrk;
}
last->next = dispatcher_node_new;
} else {
/* This new event is the first one. */
tcb->first_dispatcher_node = dispatcher_node_new;
}
ddsrt_mutex_unlock(&tcb->lock);
return d;
}
void
dds_security_timed_dispatcher_free(
struct dds_security_timed_cb_data *tcb,
struct dds_security_timed_dispatcher_t *d)
{
struct event_t *event;
struct list_dispatcher_t *dispatcher_node;
struct list_dispatcher_t *dispatcher_node_prev;
assert(d);
/* Remove related events from queue. */
ddsrt_mutex_lock(&tcb->lock);
while((event = ddsrt_fibheap_extract_min(&timed_cb_queue_fhdef, &d->events)) != NULL)
{
event->callback(d, DDS_SECURITY_TIMED_CB_KIND_DELETE, NULL, event->arg);
ddsrt_free(event);
}
/* Remove dispatcher from list */
dispatcher_node_prev = NULL;
for (dispatcher_node = tcb->first_dispatcher_node; dispatcher_node != NULL; dispatcher_node = dispatcher_node->next)
{
if (dispatcher_node->dispatcher == d)
{
/* remove element */
if (dispatcher_node_prev != NULL)
{
dispatcher_node_prev->next = dispatcher_node->next;
}
else
{
tcb->first_dispatcher_node = dispatcher_node->next;
}
ddsrt_free(dispatcher_node);
break;
}
dispatcher_node_prev = dispatcher_node;
}
/* Free this dispatcher. */
ddsrt_free(d);
ddsrt_mutex_unlock(&tcb->lock);
}
void
dds_security_timed_dispatcher_enable(
struct dds_security_timed_cb_data *tcb,
struct dds_security_timed_dispatcher_t *d,
void *listener)
{
assert(d);
assert(!(d->active));
ddsrt_mutex_lock(&tcb->lock);
/* Remember possible listener and activate. */
d->listener = listener;
d->active = true;
/* Start thread when not running, otherwise wake it up to
* trigger callbacks that were (possibly) previously added. */
ddsrt_cond_signal(&tcb->cond);
ddsrt_mutex_unlock(&tcb->lock);
}
void
dds_security_timed_dispatcher_disable(
struct dds_security_timed_cb_data *tcb,
struct dds_security_timed_dispatcher_t *d)
{
assert(d);
assert(d->active);
ddsrt_mutex_lock(&tcb->lock);
/* Forget listener and deactivate. */
d->listener = NULL;
d->active = false;
ddsrt_mutex_unlock(&tcb->lock);
}
void
dds_security_timed_dispatcher_add(
struct dds_security_timed_cb_data *tcb,
struct dds_security_timed_dispatcher_t *d,
dds_security_timed_cb_t cb,
dds_time_t trigger_time,
void *arg)
{
struct event_t *event_new;
assert(d);
assert(cb);
/* Create event. */
event_new = ddsrt_malloc(sizeof(struct event_t));
memset(event_new, 0, sizeof(struct event_t));
event_new->trigger_time = trigger_time;
event_new->callback = cb;
event_new->arg = arg;
/* Insert event based on trigger_time. */
ddsrt_mutex_lock(&tcb->lock);
ddsrt_fibheap_insert(&timed_cb_queue_fhdef, &d->events, event_new);
ddsrt_mutex_unlock(&tcb->lock);
/* Wake up thread (if it's running). */
ddsrt_cond_signal(&tcb->cond);
}

View file

@ -14,6 +14,7 @@ include (CUnit)
set(security_core_test_sources
"tc_fsm.c"
"dds_security_core.c"
)
add_definitions(-DDDSI_INCLUDE_SECURITY)

View file

@ -0,0 +1,546 @@
/*
* Copyright(c) 2019 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include "CUnit/Test.h"
#include "dds/security/core/dds_security_timed_cb.h"
#include "dds/ddsrt/misc.h"
#define SEQ_SIZE (16)
typedef struct {
struct dds_security_timed_dispatcher_t *d;
dds_security_timed_cb_kind kind;
void *listener;
void *arg;
dds_time_t time;
} tc__sequence_data;
static int g_sequence_idx = 0;
static tc__sequence_data g_sequence_array[SEQ_SIZE];
static void simple_callback(struct dds_security_timed_dispatcher_t *d,
dds_security_timed_cb_kind kind,
void *listener,
void *arg)
{
DDSRT_UNUSED_ARG(d);
DDSRT_UNUSED_ARG(kind);
DDSRT_UNUSED_ARG(listener);
if (*((bool *)arg) == false)
{
*((bool *)arg) = true;
}
else
{
*((bool *)arg) = false;
}
}
static int g_order_callback_idx = 0;
static void* g_order_callback[2] = {(void*)NULL, (void*)NULL};
static void order_callback(struct dds_security_timed_dispatcher_t *d,
dds_security_timed_cb_kind kind,
void *listener,
void *arg)
{
DDSRT_UNUSED_ARG(d);
DDSRT_UNUSED_ARG(kind);
DDSRT_UNUSED_ARG(listener);
g_order_callback[g_order_callback_idx] = arg;
g_order_callback_idx++;
}
static void
tc__callback(
struct dds_security_timed_dispatcher_t *d,
dds_security_timed_cb_kind kind,
void *listener,
void *arg)
{
if (g_sequence_idx < SEQ_SIZE) {
g_sequence_array[g_sequence_idx].d = d;
g_sequence_array[g_sequence_idx].arg = arg;
g_sequence_array[g_sequence_idx].kind = kind;
g_sequence_array[g_sequence_idx].listener = listener;
g_sequence_array[g_sequence_idx].time = dds_time();
}
g_sequence_idx++;
}
CU_Test(dds_security_timed_cb, simple_test)
{
struct dds_security_timed_cb_data *tcb = dds_security_timed_cb_new();
struct dds_security_timed_dispatcher_t *d1 = NULL;
static bool test_var = false;
dds_time_t now = dds_time();
dds_time_t future = now + DDS_SECS(2);
d1 = dds_security_timed_dispatcher_new(tcb);
CU_ASSERT_PTR_NOT_NULL_FATAL(d1);
dds_security_timed_dispatcher_add(tcb, d1, simple_callback, future, (void*)&test_var);
dds_security_timed_dispatcher_enable(tcb, d1, (void*)NULL);
CU_ASSERT_FALSE_FATAL(test_var);
dds_sleepfor(DDS_MSECS(500));
CU_ASSERT_FALSE_FATAL(test_var);
dds_sleepfor(DDS_SECS(2));
CU_ASSERT_TRUE_FATAL(test_var);
dds_security_timed_dispatcher_free(tcb, d1);
dds_security_timed_cb_free(tcb);
}
CU_Test(dds_security_timed_cb, simple_order)
{
struct dds_security_timed_cb_data *tcb = dds_security_timed_cb_new();
struct dds_security_timed_dispatcher_t *d1 = NULL;
dds_time_t future;
dds_time_t future2;
d1 = dds_security_timed_dispatcher_new(tcb);
CU_ASSERT_PTR_NOT_NULL_FATAL(d1);
future = dds_time() + DDS_MSECS(20);
future2 = future;
dds_security_timed_dispatcher_add(tcb, d1, order_callback, future, (void*)1);
dds_security_timed_dispatcher_add(tcb, d1, order_callback, future2, (void*)2);
dds_security_timed_dispatcher_enable(tcb, d1, (void*)&g_order_callback);
dds_sleepfor(DDS_MSECS(10));
dds_security_timed_dispatcher_free(tcb, d1);
CU_ASSERT_EQUAL_FATAL(g_order_callback[0], (void*)1);
CU_ASSERT_EQUAL_FATAL(g_order_callback[1], (void*)2);
dds_security_timed_cb_free(tcb);
}
CU_Test(dds_security_timed_cb, test_enabled_and_disabled)
{
struct dds_security_timed_cb_data *tcb = dds_security_timed_cb_new();
struct dds_security_timed_dispatcher_t *d1 = NULL;
static bool test_var = false;
dds_time_t now = dds_time();
dds_time_t future = now + DDS_SECS(2);
d1 = dds_security_timed_dispatcher_new(tcb);
CU_ASSERT_PTR_NOT_NULL_FATAL(d1);
dds_security_timed_dispatcher_add(tcb, d1, simple_callback, future, (void*)&test_var);
dds_security_timed_dispatcher_enable(tcb, d1, (void*)NULL);
CU_ASSERT_FALSE(test_var);
dds_security_timed_dispatcher_disable(tcb, d1);
dds_sleepfor(DDS_MSECS(500));
CU_ASSERT_FALSE(test_var);
dds_sleepfor(DDS_SECS(2));
CU_ASSERT_FALSE(test_var);
dds_security_timed_dispatcher_free(tcb, d1);
dds_security_timed_cb_free(tcb);
}
CU_Test(dds_security_timed_cb, simple_test_with_future)
{
struct dds_security_timed_cb_data *tcb = dds_security_timed_cb_new();
struct dds_security_timed_dispatcher_t *d1 = NULL;
static bool test_var = false;
dds_time_t now = dds_time();
dds_time_t future = now + DDS_SECS(2);
dds_time_t far_future = now + DDS_SECS(10);
d1 = dds_security_timed_dispatcher_new(tcb);
CU_ASSERT_PTR_NOT_NULL_FATAL(d1);
dds_security_timed_dispatcher_enable(tcb, d1, (void*)NULL);
dds_security_timed_dispatcher_add(tcb, d1, simple_callback, future, (void*)&test_var);
dds_security_timed_dispatcher_add(tcb, d1, simple_callback, far_future, (void*)&test_var);
CU_ASSERT_FALSE_FATAL(test_var);
dds_sleepfor(DDS_MSECS(500));
CU_ASSERT_FALSE_FATAL(test_var);
dds_sleepfor(DDS_SECS(2));
CU_ASSERT_TRUE_FATAL(test_var);
dds_security_timed_dispatcher_free(tcb, d1);
dds_security_timed_cb_free(tcb);
}
CU_Test(dds_security_timed_cb, test_multiple_dispatchers)
{
struct dds_security_timed_cb_data *tcb = dds_security_timed_cb_new();
struct dds_security_timed_dispatcher_t *d1 = NULL;
struct dds_security_timed_dispatcher_t *d2 = NULL;
static bool test_var = false;
dds_time_t now = dds_time();
dds_time_t future = now + DDS_SECS(2);
dds_time_t far_future = now + DDS_SECS(10);
d1 = dds_security_timed_dispatcher_new(tcb);
d2 = dds_security_timed_dispatcher_new(tcb);
CU_ASSERT_PTR_NOT_NULL_FATAL(d1);
dds_security_timed_dispatcher_enable(tcb, d1, (void*)NULL);
dds_security_timed_dispatcher_enable(tcb, d2, (void*)NULL);
dds_security_timed_dispatcher_free(tcb, d2);
dds_security_timed_dispatcher_add(tcb, d1, simple_callback, future, (void*)&test_var);
dds_security_timed_dispatcher_add(tcb, d1, simple_callback, far_future, (void*)&test_var);
CU_ASSERT_FALSE_FATAL(test_var);
dds_sleepfor(DDS_MSECS(500));
CU_ASSERT_FALSE_FATAL(test_var);
dds_sleepfor(DDS_SECS(2));
CU_ASSERT_TRUE_FATAL(test_var);
dds_security_timed_dispatcher_free(tcb, d1);
dds_security_timed_cb_free(tcb);
}
CU_Test(dds_security_timed_cb, test_not_enabled_multiple_dispatchers)
{
struct dds_security_timed_cb_data *tcb = dds_security_timed_cb_new();
struct dds_security_timed_dispatcher_t *d1 = NULL;
struct dds_security_timed_dispatcher_t *d2 = NULL;
d1 = dds_security_timed_dispatcher_new(tcb);
d2 = dds_security_timed_dispatcher_new(tcb);
CU_ASSERT_PTR_NOT_NULL_FATAL(d1);
CU_ASSERT_PTR_NOT_NULL_FATAL(d2);
dds_security_timed_dispatcher_free(tcb, d2);
dds_security_timed_dispatcher_free(tcb, d1);
dds_security_timed_cb_free(tcb);
CU_PASS("Timed callbacks enabled and disabled without add");
}
CU_Test(dds_security_timed_cb, test_create_dispatcher)
{
struct dds_security_timed_cb_data *tcb = dds_security_timed_cb_new();
struct dds_security_timed_dispatcher_t *d1 = NULL;
struct dds_security_timed_dispatcher_t *d2 = NULL;
struct dds_security_timed_dispatcher_t *d3 = NULL;
struct dds_security_timed_dispatcher_t *d4 = NULL;
struct dds_security_timed_dispatcher_t *d5 = NULL;
bool ok = false;
dds_time_t now = dds_time();
dds_time_t past = now - DDS_SECS(1);
dds_time_t present = now + DDS_SECS(1);
dds_time_t future = present + DDS_SECS(1);
dds_time_t future2 = future + DDS_SECS(10);
/*************************************************************************
* Check if dispatchers can be created
*************************************************************************/
d1 = dds_security_timed_dispatcher_new(tcb);
d2 = dds_security_timed_dispatcher_new(tcb);
CU_ASSERT_PTR_NOT_NULL_FATAL(d1);
CU_ASSERT_PTR_NOT_NULL_FATAL(d2);
/*************************************************************************
* Check if adding callbacks succeeds
*************************************************************************/
/* The last argument is a sequence number in which
* the callbacks are expected to be called. */
/* We can only really check if it crashes or not... */
dds_security_timed_dispatcher_add(tcb, d1, tc__callback, present, (void*)1);
dds_security_timed_dispatcher_add(tcb, d2, tc__callback, past, (void*)0);
dds_security_timed_dispatcher_add(tcb, d2, tc__callback, present, (void*)2);
dds_security_timed_dispatcher_add(tcb, d1, tc__callback, future, (void*)7);
CU_PASS("Added callbacks")
/*************************************************************************
* Check if dispatchers can be created
*************************************************************************/
d3 = dds_security_timed_dispatcher_new(tcb);
d4 = dds_security_timed_dispatcher_new(tcb);
d5 = dds_security_timed_dispatcher_new(tcb);
CU_ASSERT_PTR_NOT_NULL_FATAL(d3);
CU_ASSERT_PTR_NOT_NULL_FATAL(d4);
CU_ASSERT_PTR_NOT_NULL_FATAL(d5);
/*************************************************************************
* Check if enabling dispatchers succeeds
*************************************************************************/
/* The sleeps are added to get the timing between
* 'present' and 'past' callbacks right. */
/* We can only really check if it crashes or not... */
dds_sleepfor(DDS_MSECS(600));
dds_security_timed_dispatcher_enable(tcb, d1, (void*)NULL);
dds_security_timed_dispatcher_enable(tcb, d2, (void*) d2);
dds_security_timed_dispatcher_enable(tcb, d3, (void*)NULL);
/* Specifically not enabling d4 and d5. */
dds_sleepfor(DDS_MSECS(600));
CU_PASS("Enabled dds_security_timed_dispatchers.");
/*************************************************************************
* Check if adding callbacks succeeds
*************************************************************************/
/* The last argument is a sequence number in which
* the callbacks are expected to be called. */
/* We can only really check if it crashes or not... */
dds_security_timed_dispatcher_add(tcb, d4, tc__callback, past, (void*)99);
dds_security_timed_dispatcher_add(tcb, d2, tc__callback, future, (void*) 8);
dds_security_timed_dispatcher_add(tcb, d3, tc__callback, future2, (void*) 9);
dds_security_timed_dispatcher_add(tcb, d1, tc__callback, past, (void*) 3);
dds_security_timed_dispatcher_add(tcb, d1, tc__callback, future2, (void*)10);
dds_security_timed_dispatcher_add(tcb, d1, tc__callback, present, (void*) 4);
dds_security_timed_dispatcher_add(tcb, d2, tc__callback, present, (void*) 5);
dds_security_timed_dispatcher_add(tcb, d1, tc__callback, future, (void*) 6);
dds_security_timed_dispatcher_add(tcb, d3, tc__callback, future2, (void*)11);
CU_PASS("Added callbacks.");
/*************************************************************************
* Check if timeout callbacks are triggered in the right sequence
*************************************************************************/
int idx;
int timeout = 200; /* 2 seconds */
/* Wait for the callbacks to have been triggered.
* Ignore the ones in the far future. */
while ((g_sequence_idx < 8) && (timeout > 0)) {
dds_sleepfor(DDS_MSECS(10));
timeout--;
}
/* Print and check sequence of triggered callbacks. */
for (idx = 0; (idx < g_sequence_idx) && (idx < SEQ_SIZE); idx++) {
int seq = (int)(long long)(g_sequence_array[idx].arg);
struct dds_security_timed_dispatcher_t *expected_d;
void *expected_l;
/*
* Sequence checks.
*/
if ((seq == 1) || (seq == 6) || (seq == 3) || (seq == 10) || (seq == 4) || (seq == 7)) {
expected_d = d1;
expected_l = NULL;
} else if ((seq == 0) || (seq == 2) || (seq == 8) || (seq == 5)) {
expected_d = d2;
expected_l = d2;
} else if (seq == 9) {
expected_d = d3;
expected_l = NULL;
} else if (seq == 99) {
expected_d = d4;
expected_l = NULL;
CU_FAIL("Unexpected callback on a disabled dispatcher");
ok = false;
} else {
expected_d = NULL;
expected_l = NULL;
CU_FAIL(sprintf("Unknown sequence idx received %d", seq));
ok = false;
}
if (seq != idx) {
/* 6 and 7 order may be mixed since the order is not defined for same time stamp */
if (!((seq == 6 && idx == 7) || (seq == 7 && idx == 6)))
{
printf("Unexpected sequence ordering %d vs %d\n", seq, idx);
CU_FAIL("Unexpected sequence ordering");
ok = false;
}
}
if (seq > 8) {
CU_FAIL(sprintf("Unexpected sequence idx %d of the far future", seq));
ok = false;
}
if (idx > 8) {
CU_FAIL(sprintf("Too many callbacks %d", idx));
ok = false;
}
/*
* Callback contents checks.
*/
if (expected_d != NULL) {
if (g_sequence_array[idx].d != expected_d) {
printf("Unexpected dispatcher %p vs %p\n", g_sequence_array[idx].d, expected_d);
CU_FAIL("Unexpected dispatcher");
ok = false;
}
if (g_sequence_array[idx].listener != expected_l) {
CU_FAIL(sprintf("Unexpected listener %p vs %p", g_sequence_array[idx].listener, expected_l));
ok = false;
}
}
/*
* Callback kind check.
*/
if (g_sequence_array[idx].kind != DDS_SECURITY_TIMED_CB_KIND_TIMEOUT) {
CU_FAIL(sprintf("Unexpected kind %d vs %d", (int)g_sequence_array[idx].kind, (int)DDS_SECURITY_TIMED_CB_KIND_TIMEOUT));
ok = false;
}
}
if (g_sequence_idx < 8) {
CU_FAIL(sprintf("Received %d callbacks, while 9 are expected",
g_sequence_idx + 1));
ok = false;
}
if (ok) {
CU_FAIL(sprintf("Received timeout callbacks."));
}
/* Reset callback index to catch the deletion ones. */
g_sequence_idx = 0;
/*************************************************************************
* Check if deleting succeeds with dispatchers in different states
*************************************************************************/
/* We can only really check if it crashes or not... */
if (d1) {
dds_security_timed_dispatcher_free(tcb, d1);
}
if (d2) {
dds_security_timed_dispatcher_free(tcb, d2);
}
if (d3) {
dds_security_timed_dispatcher_free(tcb, d3);
}
if (d4) {
dds_security_timed_dispatcher_free(tcb, d4);
}
if (d5) {
dds_security_timed_dispatcher_free(tcb, d5);
}
CU_PASS("Deleted dispatchers.");
/*************************************************************************
* Check if deletion callbacks are triggered
*************************************************************************/
if (ok) {
timeout = 200; /* 2 seconds */
/* Wait for the callbacks to have been triggered.
* Ignore the ones in the far future. */
while ((g_sequence_idx < 4) && (timeout > 0)) {
dds_sleepfor(DDS_MSECS(10));
timeout--;
}
/* Print and check sequence of triggered callbacks. */
for (idx = 0; (idx < g_sequence_idx) && (idx < SEQ_SIZE); idx++) {
int seq = (int)(long long)(g_sequence_array[idx].arg);
struct dds_security_timed_dispatcher_t *expected_d;
/*
* Human (somewhat) readable format.
*/
// tc__sequence_data_print(&(g_sequence_array[idx]));
/*
* Sequence checks.
*/
if (seq == 99) {
expected_d = d4;
} else if ((seq == 9) || (seq == 11)) {
expected_d = d3;
} else if (seq == 10) {
expected_d = d1;
} else {
expected_d = NULL;
CU_FAIL(sprintf("Unexpected sequence idx received %d", seq));
ok = false;
}
if (idx > 4) {
CU_FAIL(sprintf("Too many callbacks %d", idx));
ok = false;
}
/*
* Callback contents checks.
*/
if (expected_d != NULL) {
if (g_sequence_array[idx].d != expected_d) {
CU_FAIL(sprintf("Unexpected dispatcher %p vs %p", g_sequence_array[idx].d, expected_d));
ok = false;
}
if (g_sequence_array[idx].listener != NULL) {
CU_FAIL(sprintf("Unexpected listener %p vs NULL", g_sequence_array[idx].listener));
ok = false;
}
}
/*
* Callback kind check.
*/
if (g_sequence_array[idx].kind != DDS_SECURITY_TIMED_CB_KIND_DELETE) {
CU_FAIL(sprintf("Unexpected kind %d vs %d", (int)g_sequence_array[idx].kind, (int)DDS_SECURITY_TIMED_CB_KIND_TIMEOUT));
ok = false;
}
}
if (g_sequence_idx < 4) {
CU_FAIL(sprintf("Received %d callbacks, while 3 are expected",
g_sequence_idx + 1));
ok = false;
}
if (ok) {
CU_PASS("Received deletion callbacks.");
}
}
dds_security_timed_cb_free(tcb);
}