From 02cbd60efe8c0fb9a8bf124425d4b39fae3a1796 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 14 Feb 2014 15:04:26 +0400 Subject: [PATCH] Outgoing Transfer ID registry --- .../transport/outgoing_transfer_registry.hpp | 118 ++++++++++++++++++ .../transport/outgoing_transfer_registry.cpp | 74 +++++++++++ 2 files changed, 192 insertions(+) create mode 100644 libuavcan/include/uavcan/internal/transport/outgoing_transfer_registry.hpp create mode 100644 libuavcan/test/transport/outgoing_transfer_registry.cpp diff --git a/libuavcan/include/uavcan/internal/transport/outgoing_transfer_registry.hpp b/libuavcan/include/uavcan/internal/transport/outgoing_transfer_registry.hpp new file mode 100644 index 0000000000..3378355959 --- /dev/null +++ b/libuavcan/include/uavcan/internal/transport/outgoing_transfer_registry.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#pragma once + +#include +#include +#include +#include +#include + +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 +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 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)); + } +}; + +} diff --git a/libuavcan/test/transport/outgoing_transfer_registry.cpp b/libuavcan/test/transport/outgoing_transfer_registry.cpp new file mode 100644 index 0000000000..f33cee0a3e --- /dev/null +++ b/libuavcan/test/transport/outgoing_transfer_registry.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#include +#include +#include + + +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()); +}