Outgoing Transfer ID registry

This commit is contained in:
Pavel Kirienko
2014-02-14 15:04:26 +04:00
parent 96f8c9aa09
commit 02cbd60efe
2 changed files with 192 additions and 0 deletions
@@ -0,0 +1,118 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <cassert>
#include <stdint.h>
#include <uavcan/internal/map.hpp>
#include <uavcan/internal/debug.hpp>
#include <uavcan/internal/transport/transfer.hpp>
namespace uavcan
{
#pragma pack(push, 1)
class OutgoingTransferRegistryKey
{
uint16_t data_type_id_;
uint8_t transfer_type_;
uint8_t destination_node_id_; ///< Not applicable for message broadcasting
public:
OutgoingTransferRegistryKey()
: data_type_id_(0xFFFF)
, transfer_type_(0xFF)
, destination_node_id_(NODE_ID_INVALID)
{ }
OutgoingTransferRegistryKey(uint16_t data_type_id, TransferType transfer_type, uint8_t destination_node_id)
: data_type_id_(data_type_id)
, transfer_type_(transfer_type)
, destination_node_id_(destination_node_id)
{
assert(destination_node_id != NODE_ID_INVALID);
assert((transfer_type == TRANSFER_TYPE_MESSAGE_BROADCAST) == (destination_node_id == NODE_ID_BROADCAST));
/* Service response transfers must use the same Transfer ID as matching service request transfer,
* so this registry is not applicable for service response transfers at all.
*/
assert(transfer_type != TRANSFER_TYPE_SERVICE_RESPONSE);
}
bool operator==(const OutgoingTransferRegistryKey& rhs) const
{
return
(data_type_id_ == rhs.data_type_id_) &&
(transfer_type_ == rhs.transfer_type_) &&
(destination_node_id_ == rhs.destination_node_id_);
}
};
#pragma pack(pop)
class IOutgoingTransferRegistry
{
public:
virtual ~IOutgoingTransferRegistry() { }
virtual TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, uint64_t new_monotonic_deadline) = 0;
virtual void cleanup(uint64_t monotonic_deadline) = 0;
};
template <int NUM_STATIC_ENTRIES>
class OutgoingTransferRegistry : public IOutgoingTransferRegistry, Noncopyable
{
#pragma pack(push, 1)
struct Value
{
uint64_t monotonic_deadline;
TransferID tid;
Value() : monotonic_deadline(0) { }
};
#pragma pack(pop)
class DeadlineExpiredPredicate
{
const uint64_t ts_monotonic_;
public:
DeadlineExpiredPredicate(uint64_t ts_monotonic)
: ts_monotonic_(ts_monotonic)
{ }
bool operator()(const OutgoingTransferRegistryKey& key, const Value& value) const
{
(void)key;
assert(value.monotonic_deadline > 0);
return value.monotonic_deadline <= ts_monotonic_;
}
};
Map<OutgoingTransferRegistryKey, Value, NUM_STATIC_ENTRIES> map_;
public:
OutgoingTransferRegistry(IAllocator* allocator)
: map_(allocator)
{ }
TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, uint64_t new_monotonic_deadline)
{
assert(new_monotonic_deadline > 0);
Value* p = map_.access(key);
if (p == NULL)
p = map_.insert(key, Value());
if (p == NULL)
return NULL;
p->monotonic_deadline = new_monotonic_deadline;
return &p->tid;
}
void cleanup(uint64_t ts_monotonic)
{
map_.removeWhere(DeadlineExpiredPredicate(ts_monotonic));
}
};
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#include <algorithm>
#include <gtest/gtest.h>
#include <uavcan/internal/transport/outgoing_transfer_registry.hpp>
TEST(OutgoingTransferRegistry, Basic)
{
using uavcan::OutgoingTransferRegistryKey;
uavcan::PoolManager<1> poolmgr; // Empty
uavcan::OutgoingTransferRegistry<4> otr(&poolmgr);
otr.cleanup(1000);
static const int NUM_KEYS = 5;
const OutgoingTransferRegistryKey keys[NUM_KEYS] =
{
OutgoingTransferRegistryKey(123, uavcan::TRANSFER_TYPE_MESSAGE_UNICAST, 42),
OutgoingTransferRegistryKey(123, uavcan::TRANSFER_TYPE_MESSAGE_BROADCAST, 0),
OutgoingTransferRegistryKey(123, uavcan::TRANSFER_TYPE_SERVICE_REQUEST, 2),
OutgoingTransferRegistryKey(123, uavcan::TRANSFER_TYPE_MESSAGE_UNICAST, 4),
OutgoingTransferRegistryKey(456, uavcan::TRANSFER_TYPE_SERVICE_REQUEST, 2)
};
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
ASSERT_EQ(0, otr.accessOrCreate(keys[2], 1000000)->get());
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
ASSERT_FALSE(otr.accessOrCreate(keys[4], 1000000)); // OOM
/*
* Incrementing a little
*/
otr.accessOrCreate(keys[0], 2000000)->increment();
otr.accessOrCreate(keys[0], 4000000)->increment();
otr.accessOrCreate(keys[0], 3000000)->increment();
ASSERT_EQ(3, otr.accessOrCreate(keys[0], 5000000)->get());
otr.accessOrCreate(keys[2], 2000000)->increment();
otr.accessOrCreate(keys[2], 3000000)->increment();
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 6000000)->get());
otr.accessOrCreate(keys[3], 9000000)->increment();
ASSERT_EQ(1, otr.accessOrCreate(keys[3], 4000000)->get());
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 4000000)->get());
ASSERT_FALSE(otr.accessOrCreate(keys[4], 1000000)); // Still OOM
/*
* Cleaning up
*/
otr.cleanup(4000001); // Kills 1, 3
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
otr.accessOrCreate(keys[1], 5000000)->increment();
otr.accessOrCreate(keys[3], 5000000)->increment();
ASSERT_EQ(3, otr.accessOrCreate(keys[0], 5000000)->get());
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 6000000)->get());
otr.cleanup(5000001); // Kills 1, 3 (He needs a bath, Jud. He stinks of the ground you buried him in.), 0
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 1000000)->get());
otr.cleanup(5000001); // Frees some memory for 4
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
}