Merge pull request #73 from UAVCAN/footprint_reduction

Footprint reduction
This commit is contained in:
Pavel Kirienko
2015-10-17 20:46:38 +03:00
53 changed files with 1117 additions and 1623 deletions
+16 -18
View File
@@ -167,6 +167,22 @@
# endif
#endif
#ifndef UAVCAN_LIKELY
# if __GNUC__
# define UAVCAN_LIKELY(x) __builtin_expect(!!(x), true)
# else
# define UAVCAN_LIKELY(x) (x)
# endif
#endif
#ifndef UAVCAN_UNLIKELY
# if __GNUC__
# define UAVCAN_UNLIKELY(x) __builtin_expect(!!(x), false)
# else
# define UAVCAN_UNLIKELY(x) (x)
# endif
#endif
namespace uavcan
{
/**
@@ -239,24 +255,6 @@ static const unsigned MaxCanAcceptanceFilters = UAVCAN_MAX_CAN_ACCEPTANCE_FILTER
static const unsigned MaxCanAcceptanceFilters = 32;
#endif
/**
* This constant defines how many receiver objects will be statically pre-allocated by classes that listen to messages
* of type uavcan.protocol.NodeStatus from other nodes. If the number of publishers exceeds this value, extra
* listeners will be allocated in the dynamic memory (one block per listener).
*
* The default value is safe to use in any application (since extra memory can always be retrieved from the pool).
* Applications that are extremely memory sensitive and are not expected to operate in large networks can override
* the default with a lower value.
*
* Note that nodes that aren't using classes that monitor the network (e.g. NodeStatusMonitor, dynamic node ID
* allocation servers) do not depend on this configuration parameter in any way.
*/
#ifdef UAVCAN_MAX_NETWORK_SIZE_HINT
static const unsigned MaxNetworkSizeHint = UAVCAN_MAX_NETWORK_SIZE_HINT;
#else
static const unsigned MaxNetworkSizeHint = 64; ///< Rest will go into the dynamic memory
#endif
}
#endif // UAVCAN_BUILD_CONFIG_HPP_INCLUDED
+53 -16
View File
@@ -26,14 +26,30 @@ public:
virtual void* allocate(std::size_t size) = 0;
virtual void deallocate(const void* ptr) = 0;
virtual uint16_t getNumBlocks() const = 0;
/**
* Returns the maximum number of blocks this allocator can allocate.
*/
virtual uint16_t getBlockCapacity() const = 0;
};
/**
* Classic implementation of a pool allocator (Meyers).
*
* The allocator can be made thread-safe (optional) by means of providing a RAII-lock type via the second template
* argument. The allocator uses the lock only to access the shared state, therefore critical sections are only a few
* cycles long, which implies that it should be acceptable to use hardware IRQ disabling instead of a mutex for
* performance reasons. For example, an IRQ-based RAII-lock type can be implemented as follows:
* struct RaiiSynchronizer
* {
* RaiiSynchronizer() { __disable_irq(); }
* ~RaiiSynchronizer() { __enable_irq(); }
* };
*/
template <std::size_t PoolSize, uint8_t BlockSize>
class UAVCAN_EXPORT PoolAllocator : public IPoolAllocator, Noncopyable
template <std::size_t PoolSize,
uint8_t BlockSize,
typename RaiiSynchronizer = char>
class UAVCAN_EXPORT PoolAllocator : public IPoolAllocator,
Noncopyable
{
union Node
{
@@ -61,18 +77,33 @@ public:
virtual void* allocate(std::size_t size);
virtual void deallocate(const void* ptr);
virtual uint16_t getNumBlocks() const { return NumBlocks; }
virtual uint16_t getBlockCapacity() const { return NumBlocks; }
/**
* Return the number of blocks that are currently allocated/unallocated.
*/
uint16_t getNumUsedBlocks() const { return used_; }
uint16_t getNumFreeBlocks() const { return static_cast<uint16_t>(NumBlocks - used_); }
uint16_t getNumUsedBlocks() const
{
RaiiSynchronizer lock;
(void)lock;
return used_;
}
uint16_t getNumFreeBlocks() const
{
RaiiSynchronizer lock;
(void)lock;
return static_cast<uint16_t>(NumBlocks - used_);
}
/**
* Returns the maximum number of blocks that were ever allocated at the same time.
*/
uint16_t getPeakNumUsedBlocks() const { return max_used_; }
uint16_t getPeakNumUsedBlocks() const
{
RaiiSynchronizer lock;
(void)lock;
return max_used_;
}
};
/**
@@ -96,7 +127,7 @@ public:
virtual void* allocate(std::size_t size);
virtual void deallocate(const void* ptr);
virtual uint16_t getNumBlocks() const;
virtual uint16_t getBlockCapacity() const;
};
// ----------------------------------------------------------------------------
@@ -104,11 +135,11 @@ public:
/*
* PoolAllocator<>
*/
template <std::size_t PoolSize, uint8_t BlockSize>
const uint16_t PoolAllocator<PoolSize, BlockSize>::NumBlocks;
template <std::size_t PoolSize, uint8_t BlockSize, typename RaiiSynchronizer>
const uint16_t PoolAllocator<PoolSize, BlockSize, RaiiSynchronizer>::NumBlocks;
template <std::size_t PoolSize, uint8_t BlockSize>
PoolAllocator<PoolSize, BlockSize>::PoolAllocator() :
template <std::size_t PoolSize, uint8_t BlockSize, typename RaiiSynchronizer>
PoolAllocator<PoolSize, BlockSize, RaiiSynchronizer>::PoolAllocator() :
free_list_(reinterpret_cast<Node*>(pool_.bytes)),
used_(0),
max_used_(0)
@@ -125,14 +156,17 @@ PoolAllocator<PoolSize, BlockSize>::PoolAllocator() :
free_list_[NumBlocks - 1].next = NULL;
}
template <std::size_t PoolSize, uint8_t BlockSize>
void* PoolAllocator<PoolSize, BlockSize>::allocate(std::size_t size)
template <std::size_t PoolSize, uint8_t BlockSize, typename RaiiSynchronizer>
void* PoolAllocator<PoolSize, BlockSize, RaiiSynchronizer>::allocate(std::size_t size)
{
if (free_list_ == NULL || size > BlockSize)
{
return NULL;
}
RaiiSynchronizer lock;
(void)lock;
void* pmem = free_list_;
free_list_ = free_list_->next;
@@ -147,14 +181,17 @@ void* PoolAllocator<PoolSize, BlockSize>::allocate(std::size_t size)
return pmem;
}
template <std::size_t PoolSize, uint8_t BlockSize>
void PoolAllocator<PoolSize, BlockSize>::deallocate(const void* ptr)
template <std::size_t PoolSize, uint8_t BlockSize, typename RaiiSynchronizer>
void PoolAllocator<PoolSize, BlockSize, RaiiSynchronizer>::deallocate(const void* ptr)
{
if (ptr == NULL)
{
return;
}
RaiiSynchronizer lock;
(void)lock;
Node* p = static_cast<Node*>(const_cast<void*>(ptr));
p->next = free_list_;
free_list_ = p;
@@ -0,0 +1,220 @@
/*
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#ifndef UAVCAN_HELPERS_HEAP_BASED_POOL_ALLOCATOR_HPP_INCLUDED
#define UAVCAN_HELPERS_HEAP_BASED_POOL_ALLOCATOR_HPP_INCLUDED
#include <cstdlib>
#include <uavcan/build_config.hpp>
#include <uavcan/debug.hpp>
#include <uavcan/dynamic_memory.hpp>
namespace uavcan
{
/**
* A special-purpose implementation of a pool allocator that keeps the pool in the heap using malloc()/free().
* The pool grows dynamically, ad-hoc, thus using as little memory as possible.
*
* Allocated blocks will not be freed back automatically, but there are two ways to force their deallocation:
* - Call @ref shrink() - this method frees all blocks that are unused at the moment.
* - Destroy the object - the desctructor calls @ref shrink().
*
* The pool can be limited in growth with hard and soft limits.
* The soft limit defines the value that will be reported via @ref IPoolAllocator::getBlockCapacity().
* The hard limit defines the maximum number of blocks that can be allocated from heap.
* Typically, the hard limit should be equal or greater than the soft limit.
*
* The allocator can be made thread-safe (optional) by means of providing a RAII-lock type via the second template
* argument. The allocator uses the lock only to access the shared state, therefore critical sections are only a few
* cycles long, which implies that it should be acceptable to use hardware IRQ disabling instead of a mutex for
* performance reasons. For example, an IRQ-based RAII-lock type can be implemented as follows:
* struct RaiiSynchronizer
* {
* RaiiSynchronizer() { __disable_irq(); }
* ~RaiiSynchronizer() { __enable_irq(); }
* };
*/
template <std::size_t BlockSize,
typename RaiiSynchronizer = char>
class UAVCAN_EXPORT HeapBasedPoolAllocator : public IPoolAllocator,
Noncopyable
{
union Node
{
Node* next;
private:
uint8_t data[BlockSize];
long double _aligner1;
long long _aligner2;
};
const uint16_t capacity_soft_limit_;
const uint16_t capacity_hard_limit_;
uint16_t num_reserved_blocks_;
uint16_t num_allocated_blocks_;
Node* reserve_;
public:
/**
* The allocator initializes with empty reserve, so first allocations will be served from heap.
*
* @param block_capacity_soft_limit Block capacity that will be reported via @ref getBlockCapacity().
*
* @param block_capacity_hard_limit Real block capacity limit; the number of allocated blocks will never
* exceed this value. Hard limit should be higher than soft limit.
* Default value is two times the soft limit.
*/
HeapBasedPoolAllocator(uint16_t block_capacity_soft_limit,
uint16_t block_capacity_hard_limit = 0) :
capacity_soft_limit_(block_capacity_soft_limit),
capacity_hard_limit_((block_capacity_hard_limit > 0) ? block_capacity_hard_limit :
static_cast<uint16_t>(min(static_cast<uint32_t>(block_capacity_soft_limit) * 2U,
static_cast<uint32_t>(NumericTraits<uint16_t>::max())))),
num_reserved_blocks_(0),
num_allocated_blocks_(0),
reserve_(NULL)
{ }
/**
* The destructor de-allocates all blocks that are currently in the reserve.
* BLOCKS THAT ARE CURRENTLY HELD BY THE APPLICATION WILL LEAK.
*/
~HeapBasedPoolAllocator()
{
shrink();
#if UAVCAN_DEBUG
if (num_allocated_blocks_ > 0)
{
UAVCAN_TRACE("HeapBasedPoolAllocator", "%u BLOCKS LEAKED", num_allocated_blocks_);
}
#endif
}
/**
* Takes a block from the reserve, unless it's empty.
* In the latter case, allocates a new block in the heap.
*/
virtual void* allocate(std::size_t size)
{
if (size > BlockSize)
{
return NULL;
}
{
RaiiSynchronizer lock;
(void)lock;
Node* const p = reserve_;
if (UAVCAN_LIKELY(p != NULL))
{
reserve_ = reserve_->next;
num_allocated_blocks_++;
return p;
}
if (num_reserved_blocks_ >= capacity_hard_limit_) // Hard limit reached, no further allocations
{
return NULL;
}
}
// Unlikely branch
void* const m = std::malloc(sizeof(Node));
if (m != NULL)
{
RaiiSynchronizer lock;
(void)lock;
num_reserved_blocks_++;
num_allocated_blocks_++;
}
return m;
}
/**
* Puts the block back to reserve.
* The block will not be free()d automatically; see @ref shrink().
*/
virtual void deallocate(const void* ptr)
{
if (ptr != NULL)
{
RaiiSynchronizer lock;
(void)lock;
Node* const node = static_cast<Node*>(const_cast<void*>(ptr));
node->next = reserve_;
reserve_ = node;
num_allocated_blocks_--;
}
}
/**
* The soft limit.
*/
virtual uint16_t getBlockCapacity() const { return capacity_soft_limit_; }
/**
* The hard limit.
*/
uint16_t getBlockCapacityHardLimit() const { return capacity_hard_limit_; }
/**
* Frees all blocks that are not in use at the moment.
* Post-condition is getNumAllocatedBlocks() == getNumReservedBlocks().
*/
void shrink()
{
Node* p = NULL;
for (;;)
{
{
RaiiSynchronizer lock;
(void)lock;
// Removing from reserve and updating the counter.
p = reserve_;
if (p != NULL)
{
reserve_ = reserve_->next;
num_reserved_blocks_--;
}
else
{
break;
}
}
// Then freeing, having left the critical section.
std::free(p);
}
}
/**
* Number of blocks that are currently in use by the application.
*/
uint16_t getNumAllocatedBlocks() const
{
RaiiSynchronizer lock;
(void)lock;
return num_allocated_blocks_;
}
/**
* Number of blocks that are acquired from the heap.
*/
uint16_t getNumReservedBlocks() const
{
RaiiSynchronizer lock;
(void)lock;
return num_reserved_blocks_;
}
};
}
#endif
@@ -13,8 +13,6 @@
namespace uavcan
{
#if UAVCAN_TINY
/**
* This function implements fast copy of unaligned bit arrays. It isn't part of the library API, so it is not exported.
* @param src_org Source array
@@ -25,16 +23,6 @@ namespace uavcan
*/
void bitarrayCopy(const unsigned char* src_org, unsigned src_offset, unsigned src_len,
unsigned char* dst_org, unsigned dst_offset);
#else
/**
* Special cases of @ref bitarrayCopy() - either source or destination must be aligned.
* These functions aren't part of the library API, so they are not exported.
*/
void bitarrayCopyAlignedToUnaligned(const unsigned char* src_org, unsigned src_len,
unsigned char* dst_org, unsigned dst_offset);
void bitarrayCopyUnalignedToAligned(const unsigned char* src_org, unsigned src_offset, unsigned src_len,
unsigned char* dst_org);
#endif
/**
* This class treats a chunk of memory as an array of bits.
@@ -50,7 +38,6 @@ class UAVCAN_EXPORT BitStream
static inline unsigned bitlenToBytelen(unsigned bits) { return (bits + 7) / 8; }
#if UAVCAN_TINY
static inline void copyBitArrayAlignedToUnaligned(const uint8_t* src_org, unsigned src_len,
uint8_t* dst_org, unsigned dst_offset)
{
@@ -64,21 +51,6 @@ class UAVCAN_EXPORT BitStream
bitarrayCopy(reinterpret_cast<const unsigned char*>(src_org), src_offset, src_len,
reinterpret_cast<unsigned char*>(dst_org), 0);
}
#else
static inline void copyBitArrayAlignedToUnaligned(const uint8_t* src_org, unsigned src_len,
uint8_t* dst_org, unsigned dst_offset)
{
bitarrayCopyAlignedToUnaligned(reinterpret_cast<const unsigned char*>(src_org), src_len,
reinterpret_cast<unsigned char*>(dst_org), dst_offset);
}
static inline void copyBitArrayUnalignedToAligned(const uint8_t* src_org, unsigned src_offset, unsigned src_len,
uint8_t* dst_org)
{
bitarrayCopyUnalignedToAligned(reinterpret_cast<const unsigned char*>(src_org), src_offset, src_len,
reinterpret_cast<unsigned char*>(dst_org));
}
#endif
public:
static const unsigned MaxBitsPerRW = MaxBytesPerRW * 8;
@@ -100,9 +100,9 @@ protected:
~GenericSubscriberBase() { }
int genericStart(TransferListenerBase* listener, bool (Dispatcher::*registration_method)(TransferListenerBase*));
int genericStart(TransferListener* listener, bool (Dispatcher::*registration_method)(TransferListener*));
void stop(TransferListenerBase* listener);
void stop(TransferListener* listener);
public:
/**
@@ -115,31 +115,6 @@ public:
INode& getNode() const { return node_; }
};
/**
* This helper class does some compile-time magic on the transport layer machinery. For authorized personnel only.
*/
template <typename DataStruct_,
unsigned NumStaticReceivers_,
unsigned NumStaticBufs_,
template<unsigned, unsigned, unsigned> class TransferListenerTemplate = TransferListener
>
class UAVCAN_EXPORT TransferListenerInstantiationHelper
{
enum { DataTypeMaxByteLen = BitLenToByteLen<DataStruct_::MaxBitLen>::Result };
enum { NeedsBuffer = int(DataTypeMaxByteLen) > int(GuaranteedPayloadLenPerFrame) };
enum { BufferSize = NeedsBuffer ? DataTypeMaxByteLen : 0 };
#if UAVCAN_TINY
enum { NumStaticBufs = 0 };
enum { NumStaticReceivers = 0 };
#else
enum { NumStaticBufs = NeedsBuffer ? NumStaticBufs_: 0 };
enum { NumStaticReceivers = NumStaticReceivers_ };
#endif
public:
typedef TransferListenerTemplate<BufferSize, NumStaticBufs, NumStaticReceivers> Type;
};
/**
* Please note that the reference passed to the RX callback points to a stack-allocated object, which means
* that it gets invalidated shortly after the callback returns.
@@ -160,9 +135,15 @@ class UAVCAN_EXPORT GenericSubscriber : public GenericSubscriberBase
}
public:
TransferForwarder(SelfType& obj, const DataTypeDescriptor& data_type, IPoolAllocator& allocator)
: TransferListenerType(obj.node_.getDispatcher().getTransferPerfCounter(), data_type, allocator)
, obj_(obj)
TransferForwarder(SelfType& obj,
const DataTypeDescriptor& data_type,
uint16_t max_buffer_size,
IPoolAllocator& allocator) :
TransferListenerType(obj.node_.getDispatcher().getTransferPerfCounter(),
data_type,
max_buffer_size,
allocator),
obj_(obj)
{ }
};
@@ -172,20 +153,19 @@ class UAVCAN_EXPORT GenericSubscriber : public GenericSubscriberBase
void handleIncomingTransfer(IncomingTransfer& transfer);
int genericStart(bool (Dispatcher::*registration_method)(TransferListenerBase*));
int genericStart(bool (Dispatcher::*registration_method)(TransferListener*));
protected:
struct ReceivedDataStructureSpec : public ReceivedDataStructure<DataStruct>
{
ReceivedDataStructureSpec() { }
ReceivedDataStructureSpec(const IncomingTransfer* arg_transfer)
: ReceivedDataStructure<DataStruct>(arg_transfer)
ReceivedDataStructureSpec(const IncomingTransfer* arg_transfer) :
ReceivedDataStructure<DataStruct>(arg_transfer)
{ }
};
explicit GenericSubscriber(INode& node)
: GenericSubscriberBase(node)
explicit GenericSubscriber(INode& node) : GenericSubscriberBase(node)
{ }
virtual ~GenericSubscriber() { stop(); }
@@ -256,8 +236,10 @@ int GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::checkInit()
return -ErrUnknownDataType;
}
forwarder_.template construct<SelfType&, const DataTypeDescriptor&, IPoolAllocator&>
(*this, *descr, node_.getAllocator());
static const uint16_t MaxBufferSize = BitLenToByteLen<DataStruct::MaxBitLen>::Result;
forwarder_.template construct<SelfType&, const DataTypeDescriptor&, uint16_t, IPoolAllocator&>
(*this, *descr, MaxBufferSize, node_.getAllocator());
return 0;
}
@@ -295,7 +277,7 @@ void GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::handleIncomi
template <typename DataSpec, typename DataStruct, typename TransferListenerType>
int GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::
genericStart(bool (Dispatcher::*registration_method)(TransferListenerBase*))
genericStart(bool (Dispatcher::*registration_method)(TransferListener*))
{
const int res = checkInit();
if (res < 0)
+47 -38
View File
@@ -30,38 +30,20 @@ namespace uavcan
* This is the top-level node API.
* A custom node class can be implemented if needed, in which case it shall inherit INode.
*
* @tparam MemPoolSize_ Size of memory pool for this node, in bytes.
* Minimum recommended size is 4K * (number of CAN ifaces + 1).
* For simple nodes this number can be reduced.
* For high-traffic nodes the recommended minimum is
* like 16K * (number of CAN ifaces + 1).
*
* @tparam OutgoingTransferRegistryStaticEntries Number of statically allocated objects
* to track Transfer ID for outgoing transfers.
* Normally it should be equal to expected number of
* publishers and service callers, but it's not necessary.
* Additional objects for Transfer ID tracking will
* be allocated in the memory pool if needed.
* Default value is acceptable for any use case.
* @tparam MemPoolSize Size of memory pool for this node, in bytes.
* Please refer to the documentation for details.
* If this value is zero, the constructor will accept a reference to user-provided allocator.
*/
template <std::size_t MemPoolSize_,
#if UAVCAN_TINY
unsigned OutgoingTransferRegistryStaticEntries = 0
#else
unsigned OutgoingTransferRegistryStaticEntries = 10
#endif
>
template <std::size_t MemPoolSize = 0>
class UAVCAN_EXPORT Node : public INode
{
enum
{
MemPoolSize = (MemPoolSize_ < std::size_t(MemPoolBlockSize)) ? std::size_t(MemPoolBlockSize) : MemPoolSize_
};
typedef PoolAllocator<MemPoolSize, MemPoolBlockSize> Allocator;
typedef typename
Select<(MemPoolSize > 0),
PoolAllocator<MemPoolSize, MemPoolBlockSize>, // If pool size is specified, use default allocator
IPoolAllocator& // Otherwise use reference to user-provided allocator
>::Result Allocator;
Allocator pool_allocator_;
OutgoingTransferRegistry<OutgoingTransferRegistryStaticEntries> outgoing_trans_reg_;
Scheduler scheduler_;
NodeStatusProvider proto_nsp_;
@@ -75,6 +57,12 @@ class UAVCAN_EXPORT Node : public INode
uint64_t internal_failure_cnt_;
bool started_;
void commonInit()
{
internal_failure_cnt_ = 0;
started_ = false;
}
protected:
virtual void registerInternalFailure(const char* msg)
{
@@ -88,21 +76,43 @@ protected:
}
public:
Node(ICanDriver& can_driver, ISystemClock& system_clock)
: outgoing_trans_reg_(pool_allocator_)
, scheduler_(can_driver, pool_allocator_, system_clock, outgoing_trans_reg_)
, proto_nsp_(*this)
/**
* This overload is only valid if MemPoolSize > 0.
*/
Node(ICanDriver& can_driver,
ISystemClock& system_clock) :
scheduler_(can_driver, pool_allocator_, system_clock),
proto_nsp_(*this)
#if !UAVCAN_TINY
, proto_dtp_(*this)
, proto_logger_(*this)
, proto_rrs_(*this)
, proto_tsp_(*this)
#endif
, internal_failure_cnt_(0)
, started_(false)
{ }
{
commonInit();
}
virtual Allocator& getAllocator() { return pool_allocator_; }
/**
* This overload is only valid if MemPoolSize == 0.
*/
Node(ICanDriver& can_driver,
ISystemClock& system_clock,
IPoolAllocator& allocator) :
pool_allocator_(allocator),
scheduler_(can_driver, pool_allocator_, system_clock),
proto_nsp_(*this)
#if !UAVCAN_TINY
, proto_dtp_(*this)
, proto_logger_(*this)
, proto_rrs_(*this)
, proto_tsp_(*this)
#endif
{
commonInit();
}
virtual typename RemoveReference<Allocator>::Type& getAllocator() { return pool_allocator_; }
virtual Scheduler& getScheduler() { return scheduler_; }
virtual const Scheduler& getScheduler() const { return scheduler_; }
@@ -260,9 +270,8 @@ public:
// ----------------------------------------------------------------------------
template <std::size_t MemPoolSize_, unsigned OutgoingTransferRegistryStaticEntries>
int Node<MemPoolSize_, OutgoingTransferRegistryStaticEntries>::start(
const TransferPriority priority)
template <std::size_t MemPoolSize_>
int Node<MemPoolSize_>::start(const TransferPriority priority)
{
if (started_)
{
+2 -2
View File
@@ -92,8 +92,8 @@ class UAVCAN_EXPORT Scheduler : Noncopyable
void pollCleanup(MonotonicTime mono_ts, uint32_t num_frames_processed_with_last_spin);
public:
Scheduler(ICanDriver& can_driver, IPoolAllocator& allocator, ISystemClock& sysclock, IOutgoingTransferRegistry& otr)
: dispatcher_(can_driver, allocator, sysclock, otr)
Scheduler(ICanDriver& can_driver, IPoolAllocator& allocator, ISystemClock& sysclock)
: dispatcher_(can_driver, allocator, sysclock)
, prev_cleanup_ts_(sysclock.getMonotonic())
, deadline_resolution_(MonotonicDuration::fromMSec(DefaultDeadlineResolutionMs))
, cleanup_period_(MonotonicDuration::fromMSec(DefaultCleanupPeriodMs))
@@ -20,17 +20,6 @@
namespace uavcan
{
template <typename ServiceDataType, unsigned NumStaticReceiversAndBuffers>
class UAVCAN_EXPORT ServiceResponseTransferListenerInstantiationHelper
{
public: // so much templating it hurts
typedef typename TransferListenerInstantiationHelper<typename ServiceDataType::Response,
NumStaticReceiversAndBuffers,
NumStaticReceiversAndBuffers,
TransferListenerWithFilter>::Type Type;
};
/**
* This struct describes a pending service call.
* Refer to @ref ServiceClient to learn more about service calls.
@@ -217,24 +206,17 @@ public:
* In C++11 mode this type defaults to std::function<>.
* In C++03 mode this type defaults to a plain function pointer; use binder to
* call member functions as callbacks.
*
* @tparam NumStaticCalls_ Number of concurrent calls that the class will be able to handle without using the
* memory pool. Note that this is NOT the maximum possible number of concurrent calls,
* there's no such limit. Defaults to one.
*/
template <typename DataType_,
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>,
typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>
#else
typename Callback_ = void (*)(const ServiceCallResult<DataType_>&),
typename Callback_ = void (*)(const ServiceCallResult<DataType_>&)
#endif
unsigned NumStaticCalls_ = 1
>
class UAVCAN_EXPORT ServiceClient
: public GenericSubscriber<DataType_,
typename DataType_::Response,
typename ServiceResponseTransferListenerInstantiationHelper<DataType_,
NumStaticCalls_>::Type>
typename DataType_::Response, TransferListenerWithFilter>
, public ServiceClientBase
{
public:
@@ -244,16 +226,12 @@ public:
typedef ServiceCallResult<DataType> ServiceCallResultType;
typedef Callback_ Callback;
enum { NumStaticCalls = NumStaticCalls_ };
private:
typedef ServiceClient<DataType, Callback> SelfType;
typedef GenericPublisher<DataType, RequestType> PublisherType;
typedef typename ServiceResponseTransferListenerInstantiationHelper<DataType, NumStaticCalls>::Type
TransferListenerType;
typedef GenericSubscriber<DataType, ResponseType, TransferListenerType> SubscriberType;
typedef GenericSubscriber<DataType, ResponseType, TransferListenerWithFilter> SubscriberType;
typedef Multiset<CallState, NumStaticCalls> CallRegistry;
typedef Multiset<CallState> CallRegistry;
struct TimeoutCallbackCaller
{
@@ -424,8 +402,8 @@ public:
// ----------------------------------------------------------------------------
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::invokeCallback(ServiceCallResultType& result)
template <typename DataType_, typename Callback_>
void ServiceClient<DataType_, Callback_>::invokeCallback(ServiceCallResultType& result)
{
if (coerceOrFallback<bool>(callback_, true))
{
@@ -437,8 +415,8 @@ void ServiceClient<DataType_, Callback_, NumStaticCalls_>::invokeCallback(Servic
}
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::shouldAcceptFrame(const RxFrame& frame) const
template <typename DataType_, typename Callback_>
bool ServiceClient<DataType_, Callback_>::shouldAcceptFrame(const RxFrame& frame) const
{
UAVCAN_ASSERT(frame.getTransferType() == TransferTypeServiceResponse); // Other types filtered out by dispatcher
@@ -447,9 +425,8 @@ bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::shouldAcceptFrame(con
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::
handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
template <typename DataType_, typename Callback_>
void ServiceClient<DataType_, Callback_>::handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
{
UAVCAN_ASSERT(response.getTransferType() == TransferTypeServiceResponse);
@@ -460,8 +437,8 @@ handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::handleDeadline(MonotonicTime)
template <typename DataType_, typename Callback_>
void ServiceClient<DataType_, Callback_>::handleDeadline(MonotonicTime)
{
UAVCAN_TRACE("ServiceClient", "Shared deadline event received");
/*
@@ -484,8 +461,8 @@ void ServiceClient<DataType_, Callback_, NumStaticCalls_>::handleDeadline(Monoto
}
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::addCallState(ServiceCallID call_id)
template <typename DataType_, typename Callback_>
int ServiceClient<DataType_, Callback_>::addCallState(ServiceCallID call_id)
{
if (call_registry_.isEmpty())
{
@@ -507,16 +484,16 @@ int ServiceClient<DataType_, Callback_, NumStaticCalls_>::addCallState(ServiceCa
return 0;
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id, const RequestType& request)
template <typename DataType_, typename Callback_>
int ServiceClient<DataType_, Callback_>::call(NodeID server_node_id, const RequestType& request)
{
ServiceCallID dummy;
return call(server_node_id, request, dummy);
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id, const RequestType& request,
ServiceCallID& out_call_id)
template <typename DataType_, typename Callback_>
int ServiceClient<DataType_, Callback_>::call(NodeID server_node_id, const RequestType& request,
ServiceCallID& out_call_id)
{
if (!coerceOrFallback<bool>(callback_, true))
{
@@ -549,7 +526,7 @@ int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_nod
* Configuring the listener so it will accept only the matching responses
* TODO move to init(), but this requires to somewhat refactor GenericSubscriber<> (remove TransferForwarder)
*/
TransferListenerType* const tl = SubscriberType::getTransferListener();
TransferListenerWithFilter* const tl = SubscriberType::getTransferListener();
if (tl == NULL)
{
UAVCAN_ASSERT(0); // Must have been created
@@ -573,8 +550,8 @@ int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_nod
return publisher_res;
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelCall(ServiceCallID call_id)
template <typename DataType_, typename Callback_>
void ServiceClient<DataType_, Callback_>::cancelCall(ServiceCallID call_id)
{
call_registry_.removeFirstWhere(CallStateMatchingPredicate(call_id));
if (call_registry_.isEmpty())
@@ -583,21 +560,21 @@ void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelCall(ServiceCal
}
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelAllCalls()
template <typename DataType_, typename Callback_>
void ServiceClient<DataType_, Callback_>::cancelAllCalls()
{
call_registry_.clear();
SubscriberType::stop();
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::hasPendingCallToServer(NodeID server_node_id) const
template <typename DataType_, typename Callback_>
bool ServiceClient<DataType_, Callback_>::hasPendingCallToServer(NodeID server_node_id) const
{
return NULL != call_registry_.find(ServerSearchPredicate(server_node_id));
}
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
ServiceCallID ServiceClient<DataType_, Callback_, NumStaticCalls_>::getCallIDByIndex(unsigned index) const
template <typename DataType_, typename Callback_>
ServiceCallID ServiceClient<DataType_, Callback_>::getCallIDByIndex(unsigned index) const
{
const CallState* const id = call_registry_.getByIndex(index);
return (id == NULL) ? ServiceCallID() : id->getCallID();
@@ -77,33 +77,18 @@ public:
* In C++11 mode this type defaults to std::function<>.
* In C++03 mode this type defaults to a plain function pointer; use binder to
* call member functions as callbacks.
*
* @tparam NumStaticReceivers Number of statically allocated receiver objects. If there's more service
* clients for this service, extra receivers will be allocated in the memory pool.
*
* @tparam NumStaticBufs Number of statically allocated receiver buffers. If there's more concurrent
* incoming transfers, extra buffers will be allocated in the memory pool.
*/
template <typename DataType_,
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
typename Callback_ = std::function<void (const ReceivedDataStructure<typename DataType_::Request>&,
ServiceResponseDataStructure<typename DataType_::Response>&)>,
ServiceResponseDataStructure<typename DataType_::Response>&)>
#else
typename Callback_ = void (*)(const ReceivedDataStructure<typename DataType_::Request>&,
ServiceResponseDataStructure<typename DataType_::Response>&),
#endif
#if UAVCAN_TINY
unsigned NumStaticReceivers = 0,
unsigned NumStaticBufs = 0
#else
unsigned NumStaticReceivers = 2,
unsigned NumStaticBufs = 1
ServiceResponseDataStructure<typename DataType_::Response>&)
#endif
>
class UAVCAN_EXPORT ServiceServer
: public GenericSubscriber<DataType_, typename DataType_::Request,
typename TransferListenerInstantiationHelper<typename DataType_::Request,
NumStaticReceivers, NumStaticBufs>::Type>
: public GenericSubscriber<DataType_, typename DataType_::Request, TransferListener>
{
public:
typedef DataType_ DataType;
@@ -112,9 +97,7 @@ public:
typedef Callback_ Callback;
private:
typedef typename TransferListenerInstantiationHelper<RequestType, NumStaticReceivers, NumStaticBufs>::Type
TransferListenerType;
typedef GenericSubscriber<DataType, RequestType, TransferListenerType> SubscriberType;
typedef GenericSubscriber<DataType, RequestType, TransferListener> SubscriberType;
typedef GenericPublisher<DataType, ResponseType> PublisherType;
PublisherType publisher_;
+24 -14
View File
@@ -20,20 +20,16 @@ namespace uavcan
* Please refer to the @ref Node<> for documentation concerning the template arguments; refer to the tutorials
* to lean how to use libuavcan in multiprocess applications.
*/
template <std::size_t MemPoolSize_,
unsigned OutgoingTransferRegistryStaticEntries = 10
>
template <std::size_t MemPoolSize = 0>
class UAVCAN_EXPORT SubNode : public INode
{
enum
{
MemPoolSize = (MemPoolSize_ < std::size_t(MemPoolBlockSize)) ? std::size_t(MemPoolBlockSize) : MemPoolSize_
};
typedef PoolAllocator<MemPoolSize, MemPoolBlockSize> Allocator;
typedef typename
Select<(MemPoolSize > 0),
PoolAllocator<MemPoolSize, MemPoolBlockSize>, // If pool size is specified, use default allocator
IPoolAllocator& // Otherwise use reference to user-provided allocator
>::Result Allocator;
Allocator pool_allocator_;
OutgoingTransferRegistry<OutgoingTransferRegistryStaticEntries> outgoing_trans_reg_;
Scheduler scheduler_;
uint64_t internal_failure_cnt_;
@@ -47,13 +43,27 @@ protected:
}
public:
SubNode(ICanDriver& can_driver, ISystemClock& system_clock) :
outgoing_trans_reg_(pool_allocator_),
scheduler_(can_driver, pool_allocator_, system_clock, outgoing_trans_reg_),
/**
* This overload is only valid if MemPoolSize > 0.
*/
SubNode(ICanDriver& can_driver,
ISystemClock& system_clock) :
scheduler_(can_driver, pool_allocator_, system_clock),
internal_failure_cnt_(0)
{ }
virtual Allocator& getAllocator() { return pool_allocator_; }
/**
* This overload is only valid if MemPoolSize == 0.
*/
SubNode(ICanDriver& can_driver,
ISystemClock& system_clock,
IPoolAllocator& allocator) :
pool_allocator_(allocator),
scheduler_(can_driver, pool_allocator_, system_clock),
internal_failure_cnt_(0)
{ }
virtual typename RemoveReference<Allocator>::Type& getAllocator() { return pool_allocator_; }
virtual Scheduler& getScheduler() { return scheduler_; }
virtual const Scheduler& getScheduler() const { return scheduler_; }
+4 -21
View File
@@ -34,39 +34,22 @@ namespace uavcan
* In C++11 mode this type defaults to std::function<>.
* In C++03 mode this type defaults to a plain function pointer; use binder to
* call member functions as callbacks.
*
* @tparam NumStaticReceivers Number of statically allocated receiver objects. If there's more publishers
* of this message, extra receivers will be allocated in the memory pool.
*
* @tparam NumStaticBufs Number of statically allocated receiver buffers. If there's more concurrent
* incoming transfers, extra buffers will be allocated in the memory pool.
*/
template <typename DataType_,
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
typename Callback_ = std::function<void (const ReceivedDataStructure<DataType_>&)>,
typename Callback_ = std::function<void (const ReceivedDataStructure<DataType_>&)>
#else
typename Callback_ = void (*)(const ReceivedDataStructure<DataType_>&),
#endif
#if UAVCAN_TINY
unsigned NumStaticReceivers = 0,
unsigned NumStaticBufs = 0
#else
unsigned NumStaticReceivers = 2,
unsigned NumStaticBufs = 1
typename Callback_ = void (*)(const ReceivedDataStructure<DataType_>&)
#endif
>
class UAVCAN_EXPORT Subscriber
: public GenericSubscriber<DataType_, DataType_,
typename TransferListenerInstantiationHelper<DataType_, NumStaticReceivers,
NumStaticBufs>::Type>
: public GenericSubscriber<DataType_, DataType_, TransferListener>
{
public:
typedef Callback_ Callback;
private:
typedef typename TransferListenerInstantiationHelper<DataType_, NumStaticReceivers, NumStaticBufs>::Type
TransferListenerType;
typedef GenericSubscriber<DataType_, DataType_, TransferListenerType> BaseType;
typedef GenericSubscriber<DataType_, DataType_, TransferListener> BaseType;
Callback callback_;
@@ -126,8 +126,8 @@ private:
*/
ServiceServer<AppendEntries, AppendEntriesCallback> append_entries_srv_;
ServiceClient<AppendEntries, AppendEntriesResponseCallback> append_entries_client_;
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
ServiceClient<RequestVote, RequestVoteResponseCallback, MaxNumFollowers> request_vote_client_;
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
ServiceClient<RequestVote, RequestVoteResponseCallback> request_vote_client_;
/*
* Methods
@@ -84,7 +84,7 @@ class NodeDiscoverer : TimerBase
{ }
};
typedef Map<NodeID, NodeData, 10> NodeMap;
typedef Map<NodeID, NodeData> NodeMap;
/**
* When this number of attempts has been made, the discoverer will give up and assume that the node
@@ -101,10 +101,10 @@ class NodeDiscoverer : TimerBase
IEventTracer& tracer_;
BitSet<NodeID::Max + 1> committed_node_mask_; ///< Nodes that are marked will not be queried
NodeMap node_map_; ///< Will not work in UAVCAN_TINY
NodeMap node_map_;
ServiceClient<protocol::GetNodeInfo, GetNodeInfoResponseCallback> get_node_info_client_;
Subscriber<protocol::NodeStatus, NodeStatusCallback, MaxNetworkSizeHint, 0> node_status_sub_;
Subscriber<protocol::NodeStatus, NodeStatusCallback> node_status_sub_;
/*
* Methods
@@ -31,8 +31,7 @@ class UAVCAN_EXPORT GlobalTimeSyncSlave : Noncopyable
void (GlobalTimeSyncSlave::*)(const ReceivedDataStructure<protocol::GlobalTimeSync>&)>
GlobalTimeSyncCallback;
// Static buffers are explicitly disabled because time should never be unicasted.
Subscriber<protocol::GlobalTimeSync, GlobalTimeSyncCallback, 2, 0> sub_;
Subscriber<protocol::GlobalTimeSync, GlobalTimeSyncCallback> sub_;
UtcTime prev_ts_utc_;
MonotonicTime prev_ts_mono_;
@@ -156,7 +156,6 @@ private:
}
};
enum { NumStaticCalls = 2 };
enum { DefaultNumRequestAttempts = 16 };
enum { DefaultTimerIntervalMSec = 40 }; ///< Read explanation in the class documentation
@@ -165,9 +164,9 @@ private:
*/
Entry entries_[NodeID::Max]; // [1, NodeID::Max]
Multiset<INodeInfoListener*, 2> listeners_;
Multiset<INodeInfoListener*> listeners_;
ServiceClient<protocol::GetNodeInfo, GetNodeInfoResponseCallback, NumStaticCalls> get_node_info_client_;
ServiceClient<protocol::GetNodeInfo, GetNodeInfoResponseCallback> get_node_info_client_;
MonotonicDuration request_interval_;
@@ -75,7 +75,7 @@ private:
typedef MethodBinder<NodeStatusMonitor*, void (NodeStatusMonitor::*)(const TimerEvent&)> TimerCallback;
Subscriber<protocol::NodeStatus, NodeStatusCallback, MaxNetworkSizeHint, 0> sub_;
Subscriber<protocol::NodeStatus, NodeStatusCallback> sub_;
TimerEventForwarder<TimerCallback> timer_;
@@ -65,7 +65,7 @@ private:
static const unsigned DefaultAnonMsgMask = 0xFF;
static const unsigned DefaultAnonMsgID = 0x0;
typedef uavcan::Multiset<CanFilterConfig, 1> MultisetConfigContainer;
typedef uavcan::Multiset<CanFilterConfig> MultisetConfigContainer;
static CanFilterConfig mergeFilters(CanFilterConfig &a_, CanFilterConfig &b_);
static uint8_t countBits(uint32_t n_);
@@ -81,19 +81,19 @@ class UAVCAN_EXPORT Dispatcher : Noncopyable
{
CanIOManager canio_;
ISystemClock& sysclock_;
IOutgoingTransferRegistry& outgoing_transfer_reg_;
OutgoingTransferRegistry outgoing_transfer_reg_;
TransferPerfCounter perf_;
class ListenerRegistry
{
LinkedListRoot<TransferListenerBase> list_;
LinkedListRoot<TransferListener> list_;
class DataTypeIDInsertionComparator
{
const DataTypeID id_;
public:
explicit DataTypeIDInsertionComparator(DataTypeID id) : id_(id) { }
bool operator()(const TransferListenerBase* listener) const
bool operator()(const TransferListener* listener) const
{
UAVCAN_ASSERT(listener);
return id_ > listener->getDataTypeDescriptor().getID();
@@ -103,15 +103,15 @@ class UAVCAN_EXPORT Dispatcher : Noncopyable
public:
enum Mode { UniqueListener, ManyListeners };
bool add(TransferListenerBase* listener, Mode mode);
void remove(TransferListenerBase* listener);
bool add(TransferListener* listener, Mode mode);
void remove(TransferListener* listener);
bool exists(DataTypeID dtid) const;
void cleanup(MonotonicTime ts);
void handleFrame(const RxFrame& frame);
unsigned getNumEntries() const { return list_.getLength(); }
const LinkedListRoot<TransferListenerBase>& getList() const { return list_; }
const LinkedListRoot<TransferListener>& getList() const { return list_; }
};
ListenerRegistry lmsg_;
@@ -133,10 +133,10 @@ class UAVCAN_EXPORT Dispatcher : Noncopyable
void notifyRxFrameListener(const CanRxFrame& can_frame, CanIOFlags flags);
public:
Dispatcher(ICanDriver& driver, IPoolAllocator& allocator, ISystemClock& sysclock, IOutgoingTransferRegistry& otr)
Dispatcher(ICanDriver& driver, IPoolAllocator& allocator, ISystemClock& sysclock)
: canio_(driver, allocator, sysclock)
, sysclock_(sysclock)
, outgoing_transfer_reg_(otr)
, outgoing_transfer_reg_(allocator)
#if !UAVCAN_TINY
, rx_listener_(NULL)
#endif
@@ -162,13 +162,13 @@ public:
void cleanup(MonotonicTime ts);
bool registerMessageListener(TransferListenerBase* listener);
bool registerServiceRequestListener(TransferListenerBase* listener);
bool registerServiceResponseListener(TransferListenerBase* listener);
bool registerMessageListener(TransferListener* listener);
bool registerServiceRequestListener(TransferListener* listener);
bool registerServiceResponseListener(TransferListener* listener);
void unregisterMessageListener(TransferListenerBase* listener);
void unregisterServiceRequestListener(TransferListenerBase* listener);
void unregisterServiceResponseListener(TransferListenerBase* listener);
void unregisterMessageListener(TransferListener* listener);
void unregisterServiceRequestListener(TransferListener* listener);
void unregisterServiceResponseListener(TransferListener* listener);
bool hasSubscriber(DataTypeID dtid) const;
bool hasPublisher(DataTypeID dtid) const;
@@ -185,15 +185,15 @@ public:
* removed from this list as soon as the corresponding service call is complete.
* @{
*/
const LinkedListRoot<TransferListenerBase>& getListOfMessageListeners() const
const LinkedListRoot<TransferListener>& getListOfMessageListeners() const
{
return lmsg_.getList();
}
const LinkedListRoot<TransferListenerBase>& getListOfServiceRequestListeners() const
const LinkedListRoot<TransferListener>& getListOfServiceRequestListeners() const
{
return lsrv_req_.getList();
}
const LinkedListRoot<TransferListenerBase>& getListOfServiceResponseListeners() const
const LinkedListRoot<TransferListener>& getListOfServiceResponseListeners() const
{
return lsrv_resp_.getList();
}
@@ -201,7 +201,7 @@ public:
* @}
*/
IOutgoingTransferRegistry& getOutgoingTransferRegistry() { return outgoing_transfer_reg_; }
OutgoingTransferRegistry& getOutgoingTransferRegistry() { return outgoing_transfer_reg_; }
#if !UAVCAN_TINY
LoopbackFrameListenerRegistry& getLoopbackFrameListenerRegistry() { return loopback_listeners_; }
@@ -61,21 +61,7 @@ public:
* If a local transfer sender was inactive for a sufficiently long time, the outgoing transfer registry will
* remove the respective Transfer ID tracking object.
*/
class UAVCAN_EXPORT IOutgoingTransferRegistry
{
public:
static const MonotonicDuration MinEntryLifetime;
virtual ~IOutgoingTransferRegistry() { }
virtual TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, MonotonicTime new_deadline) = 0;
virtual bool exists(DataTypeID dtid, TransferType tt) const = 0;
virtual void cleanup(MonotonicTime deadline) = 0;
};
template <int NumStaticEntries>
class UAVCAN_EXPORT OutgoingTransferRegistry : public IOutgoingTransferRegistry, Noncopyable
class UAVCAN_EXPORT OutgoingTransferRegistry : Noncopyable
{
struct Value
{
@@ -123,56 +109,22 @@ class UAVCAN_EXPORT OutgoingTransferRegistry : public IOutgoingTransferRegistry,
}
};
Map<OutgoingTransferRegistryKey, Value, NumStaticEntries> map_;
Map<OutgoingTransferRegistryKey, Value> map_;
public:
static const MonotonicDuration MinEntryLifetime;
explicit OutgoingTransferRegistry(IPoolAllocator& allocator)
: map_(allocator)
{ }
virtual TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, MonotonicTime new_deadline);
TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, MonotonicTime new_deadline);
virtual bool exists(DataTypeID dtid, TransferType tt) const;
bool exists(DataTypeID dtid, TransferType tt) const;
virtual void cleanup(MonotonicTime ts);
void cleanup(MonotonicTime ts);
};
// ----------------------------------------------------------------------------
/*
* OutgoingTransferRegistry<>
*/
template <int NumStaticEntries>
TransferID* OutgoingTransferRegistry<NumStaticEntries>::accessOrCreate(const OutgoingTransferRegistryKey& key,
MonotonicTime new_deadline)
{
UAVCAN_ASSERT(!new_deadline.isZero());
Value* p = map_.access(key);
if (p == NULL)
{
p = map_.insert(key, Value());
if (p == NULL)
{
return NULL;
}
UAVCAN_TRACE("OutgoingTransferRegistry", "Created %s", key.toString().c_str());
}
p->deadline = new_deadline;
return &p->tid;
}
template <int NumStaticEntries>
bool OutgoingTransferRegistry<NumStaticEntries>::exists(DataTypeID dtid, TransferType tt) const
{
return NULL != map_.find(ExistenceCheckingPredicate(dtid, tt));
}
template <int NumStaticEntries>
void OutgoingTransferRegistry<NumStaticEntries>::cleanup(MonotonicTime ts)
{
map_.removeAllWhere(DeadlineExpiredPredicate(ts));
}
}
#endif // UAVCAN_TRANSPORT_OUTGOING_TRANSFER_REGISTRY_HPP_INCLUDED
@@ -16,6 +16,47 @@
namespace uavcan
{
/**
* Standalone static buffer
*/
class StaticTransferBufferImpl : public ITransferBuffer
{
uint8_t* const data_;
const uint16_t size_;
uint16_t max_write_pos_;
public:
StaticTransferBufferImpl(uint8_t* buf, uint16_t buf_size) :
data_(buf),
size_(buf_size),
max_write_pos_(0)
{ }
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
void reset();
uint16_t getSize() const { return size_; }
uint8_t* getRawPtr() { return data_; }
const uint8_t* getRawPtr() const { return data_; }
uint16_t getMaxWritePos() const { return max_write_pos_; }
void setMaxWritePos(uint16_t value) { max_write_pos_ = value; }
};
template <uint16_t Size>
class UAVCAN_EXPORT StaticTransferBuffer : public StaticTransferBufferImpl
{
uint8_t buffer_[Size];
public:
StaticTransferBuffer() : StaticTransferBufferImpl(buffer_, Size)
{
StaticAssert<(Size > 0)>::check();
}
};
/**
* Internal for TransferBufferManager
*/
@@ -53,41 +94,13 @@ public:
#endif
};
/**
* Internal for TransferBufferManager
*/
class UAVCAN_EXPORT TransferBufferManagerEntry : public ITransferBuffer, Noncopyable
{
TransferBufferManagerKey key_;
protected:
virtual void resetImpl() = 0;
public:
TransferBufferManagerEntry() { }
explicit TransferBufferManagerEntry(const TransferBufferManagerKey& key)
: key_(key)
{ }
const TransferBufferManagerKey& getKey() const { return key_; }
bool isEmpty() const { return key_.isEmpty(); }
void reset(const TransferBufferManagerKey& key = TransferBufferManagerKey())
{
key_ = key;
resetImpl();
}
};
/**
* Resizable gather/scatter storage.
* reset() call releases all memory blocks.
* Supports unordered write operations - from higher to lower offsets
*/
class UAVCAN_EXPORT DynamicTransferBufferManagerEntry
: public TransferBufferManagerEntry
, public LinkedListNode<DynamicTransferBufferManagerEntry>
class UAVCAN_EXPORT TransferBufferManagerEntry : public ITransferBuffer
, public LinkedListNode<TransferBufferManagerEntry>
{
struct Block : LinkedListNode<Block>
{
@@ -107,118 +120,58 @@ class UAVCAN_EXPORT DynamicTransferBufferManagerEntry
LinkedListRoot<Block> blocks_; // Blocks are ordered from lower to higher buffer offset
uint16_t max_write_pos_;
const uint16_t max_size_;
/// Reset functionality must be implemented in a non-virtual method to call it safely from the destructor.
void doReset();
virtual void resetImpl();
TransferBufferManagerKey key_;
public:
DynamicTransferBufferManagerEntry(IPoolAllocator& allocator, uint16_t max_size)
: allocator_(allocator)
, max_write_pos_(0)
, max_size_(max_size)
TransferBufferManagerEntry(IPoolAllocator& allocator, uint16_t max_size) :
allocator_(allocator),
max_write_pos_(0),
max_size_(max_size)
{
StaticAssert<(Block::Size > 8)>::check();
IsDynamicallyAllocatable<Block>::check();
IsDynamicallyAllocatable<DynamicTransferBufferManagerEntry>::check();
IsDynamicallyAllocatable<TransferBufferManagerEntry>::check();
}
virtual ~DynamicTransferBufferManagerEntry()
{
doReset();
}
virtual ~TransferBufferManagerEntry() { reset(); }
static DynamicTransferBufferManagerEntry* instantiate(IPoolAllocator& allocator, uint16_t max_size);
static void destroy(DynamicTransferBufferManagerEntry*& obj, IPoolAllocator& allocator);
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
};
/**
* Standalone static buffer
*/
class StaticTransferBufferImpl : public ITransferBuffer
{
uint8_t* const data_;
const uint16_t size_;
uint16_t max_write_pos_;
public:
StaticTransferBufferImpl(uint8_t* buf, uint16_t buf_size)
: data_(buf)
, size_(buf_size)
, max_write_pos_(0)
{ }
static TransferBufferManagerEntry* instantiate(IPoolAllocator& allocator, uint16_t max_size);
static void destroy(TransferBufferManagerEntry*& obj, IPoolAllocator& allocator);
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
void reset();
void reset(const TransferBufferManagerKey& key = TransferBufferManagerKey());
uint16_t getSize() const { return size_; }
uint8_t* getRawPtr() { return data_; }
const uint8_t* getRawPtr() const { return data_; }
uint16_t getMaxWritePos() const { return max_write_pos_; }
void setMaxWritePos(uint16_t value) { max_write_pos_ = value; }
};
template <uint16_t Size>
class UAVCAN_EXPORT StaticTransferBuffer : public StaticTransferBufferImpl
{
uint8_t buffer_[Size];
public:
StaticTransferBuffer()
: StaticTransferBufferImpl(buffer_, Size)
{
StaticAssert<(Size > 0)>::check();
}
const TransferBufferManagerKey& getKey() const { return key_; }
bool isEmpty() const { return key_.isEmpty(); }
};
/**
* Statically allocated storage for the buffer manager
* Buffer manager implementation.
*/
class StaticTransferBufferManagerEntryImpl : public TransferBufferManagerEntry
class TransferBufferManager : public Noncopyable
{
StaticTransferBufferImpl buf_;
LinkedListRoot<TransferBufferManagerEntry> buffers_;
IPoolAllocator& allocator_;
const uint16_t max_buf_size_;
virtual void resetImpl();
TransferBufferManagerEntry* findFirst(const TransferBufferManagerKey& key);
public:
StaticTransferBufferManagerEntryImpl(uint8_t* buf, uint16_t buf_size)
: buf_(buf, buf_size)
TransferBufferManager(uint16_t max_buf_size, IPoolAllocator& allocator) :
allocator_(allocator),
max_buf_size_(max_buf_size)
{ }
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
~TransferBufferManager();
bool migrateFrom(const TransferBufferManagerEntry* tbme);
};
ITransferBuffer* access(const TransferBufferManagerKey& key);
ITransferBuffer* create(const TransferBufferManagerKey& key);
void remove(const TransferBufferManagerKey& key);
bool isEmpty() const;
template <uint16_t Size>
class UAVCAN_EXPORT StaticTransferBufferManagerEntry : public StaticTransferBufferManagerEntryImpl
{
uint8_t buffer_[Size];
public:
StaticTransferBufferManagerEntry()
: StaticTransferBufferManagerEntryImpl(buffer_, Size)
{ }
};
/**
* Manages different storage types (static/dynamic) for transfer reception logic.
*/
class UAVCAN_EXPORT ITransferBufferManager
{
public:
virtual ~ITransferBufferManager() { }
virtual ITransferBuffer* access(const TransferBufferManagerKey& key) = 0;
virtual ITransferBuffer* create(const TransferBufferManagerKey& key) = 0;
virtual void remove(const TransferBufferManagerKey& key) = 0;
virtual bool isEmpty() const = 0;
unsigned getNumBuffers() const;
};
/**
@@ -226,13 +179,13 @@ public:
*/
class UAVCAN_EXPORT TransferBufferAccessor
{
ITransferBufferManager& bufmgr_;
TransferBufferManager& bufmgr_;
const TransferBufferManagerKey key_;
public:
TransferBufferAccessor(ITransferBufferManager& bufmgr, TransferBufferManagerKey key)
: bufmgr_(bufmgr)
, key_(key)
TransferBufferAccessor(TransferBufferManager& bufmgr, TransferBufferManagerKey key) :
bufmgr_(bufmgr),
key_(key)
{
UAVCAN_ASSERT(!key.isEmpty());
}
@@ -241,87 +194,6 @@ public:
void remove() { bufmgr_.remove(key_); }
};
/**
* Buffer manager implementation.
*/
class TransferBufferManagerImpl : public ITransferBufferManager, Noncopyable
{
LinkedListRoot<DynamicTransferBufferManagerEntry> dynamic_buffers_;
IPoolAllocator& allocator_;
const uint16_t max_buf_size_;
virtual StaticTransferBufferManagerEntryImpl* getStaticByIndex(uint16_t index) const = 0;
StaticTransferBufferManagerEntryImpl* findFirstStatic(const TransferBufferManagerKey& key);
DynamicTransferBufferManagerEntry* findFirstDynamic(const TransferBufferManagerKey& key);
void optimizeStorage();
public:
TransferBufferManagerImpl(uint16_t max_buf_size, IPoolAllocator& allocator)
: allocator_(allocator)
, max_buf_size_(max_buf_size)
{ }
virtual ~TransferBufferManagerImpl();
virtual ITransferBuffer* access(const TransferBufferManagerKey& key);
virtual ITransferBuffer* create(const TransferBufferManagerKey& key);
virtual void remove(const TransferBufferManagerKey& key);
virtual bool isEmpty() const;
unsigned getNumDynamicBuffers() const;
unsigned getNumStaticBuffers() const;
};
template <uint16_t MaxBufSize, uint8_t NumStaticBufs>
class UAVCAN_EXPORT TransferBufferManager : public TransferBufferManagerImpl
{
mutable StaticTransferBufferManagerEntry<MaxBufSize> static_buffers_[NumStaticBufs];
virtual StaticTransferBufferManagerEntry<MaxBufSize>* getStaticByIndex(uint16_t index) const
{
return (index < NumStaticBufs) ? &static_buffers_[index] : NULL;
}
public:
explicit TransferBufferManager(IPoolAllocator& allocator)
: TransferBufferManagerImpl(MaxBufSize, allocator)
{
#if UAVCAN_TINY
StaticAssert<(NumStaticBufs == 0)>::check(); // Static buffers in UAVCAN_TINY mode are not allowed
#endif
StaticAssert<(MaxBufSize > 0)>::check();
}
};
template <uint16_t MaxBufSize>
class UAVCAN_EXPORT TransferBufferManager<MaxBufSize, 0> : public TransferBufferManagerImpl
{
virtual StaticTransferBufferManagerEntry<MaxBufSize>* getStaticByIndex(uint16_t) const
{
return NULL;
}
public:
explicit TransferBufferManager(IPoolAllocator& allocator)
: TransferBufferManagerImpl(MaxBufSize, allocator)
{
StaticAssert<(MaxBufSize > 0)>::check();
}
};
template <>
class UAVCAN_EXPORT TransferBufferManager<0, 0> : public ITransferBufferManager
{
public:
TransferBufferManager() { }
TransferBufferManager(IPoolAllocator&) { }
virtual ITransferBuffer* access(const TransferBufferManagerKey&) { return NULL; }
virtual ITransferBuffer* create(const TransferBufferManagerKey&) { return NULL; }
virtual void remove(const TransferBufferManagerKey&) { }
virtual bool isEmpty() const { return true; }
};
}
#endif // UAVCAN_TRANSPORT_TRANSFER_BUFFER_HPP_INCLUDED
@@ -96,11 +96,11 @@ public:
/**
* Internal, refer to the transport dispatcher class.
*/
class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListenerBase>, Noncopyable
class UAVCAN_EXPORT TransferListener : public LinkedListNode<TransferListener>, Noncopyable
{
const DataTypeDescriptor& data_type_;
MapBase<TransferBufferManagerKey, TransferReceiver>& receivers_;
ITransferBufferManager& bufmgr_;
TransferBufferManager bufmgr_;
Map<TransferBufferManagerKey, TransferReceiver> receivers_;
TransferPerfCounter& perf_;
const TransferCRC crc_base_; ///< Pre-initialized with data type hash, thus constant
bool allow_anonymous_transfers_;
@@ -108,10 +108,10 @@ class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListene
class TimedOutReceiverPredicate
{
const MonotonicTime ts_;
ITransferBufferManager& parent_bufmgr_;
TransferBufferManager& parent_bufmgr_;
public:
TimedOutReceiverPredicate(MonotonicTime arg_ts, ITransferBufferManager& arg_bufmgr)
TimedOutReceiverPredicate(MonotonicTime arg_ts, TransferBufferManager& arg_bufmgr)
: ts_(arg_ts)
, parent_bufmgr_(arg_bufmgr)
{ }
@@ -122,25 +122,24 @@ class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListene
bool checkPayloadCrc(const uint16_t compare_with, const ITransferBuffer& tbb) const;
protected:
TransferListenerBase(TransferPerfCounter& perf, const DataTypeDescriptor& data_type,
MapBase<TransferBufferManagerKey, TransferReceiver>& receivers,
ITransferBufferManager& bufmgr)
: data_type_(data_type)
, receivers_(receivers)
, bufmgr_(bufmgr)
, perf_(perf)
, crc_base_(data_type.getSignature().toTransferCRC())
, allow_anonymous_transfers_(false)
{ }
virtual ~TransferListenerBase() { }
void handleReception(TransferReceiver& receiver, const RxFrame& frame, TransferBufferAccessor& tba);
void handleAnonymousTransferReception(const RxFrame& frame);
virtual void handleIncomingTransfer(IncomingTransfer& transfer) = 0;
public:
TransferListener(TransferPerfCounter& perf, const DataTypeDescriptor& data_type,
uint16_t max_buffer_size, IPoolAllocator& allocator)
: data_type_(data_type)
, bufmgr_(max_buffer_size, allocator)
, receivers_(allocator)
, perf_(perf)
, crc_base_(data_type.getSignature().toTransferCRC())
, allow_anonymous_transfers_(false)
{ }
virtual ~TransferListener();
const DataTypeDescriptor& getDataTypeDescriptor() const { return data_type_; }
/**
@@ -154,35 +153,6 @@ public:
virtual void handleFrame(const RxFrame& frame);
};
/**
* This class should be derived by transfer receivers (subscribers, servers).
*/
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
class UAVCAN_EXPORT TransferListener : public TransferListenerBase
{
TransferBufferManager<MaxBufSize, NumStaticBufs> bufmgr_;
Map<TransferBufferManagerKey, TransferReceiver, NumStaticReceivers> receivers_;
public:
TransferListener(TransferPerfCounter& perf, const DataTypeDescriptor& data_type, IPoolAllocator& allocator)
: TransferListenerBase(perf, data_type, receivers_, bufmgr_)
, bufmgr_(allocator)
, receivers_(allocator)
{
#if UAVCAN_TINY
StaticAssert<NumStaticBufs == 0>::check();
StaticAssert<NumStaticReceivers == 0>::check();
#endif
StaticAssert<(NumStaticReceivers >= NumStaticBufs)>::check(); // Otherwise it would be meaningless
}
virtual ~TransferListener()
{
// Map must be cleared before bufmgr is destroyed
receivers_.clear();
}
};
/**
* This class is used by transfer listener to decide if the frame should be accepted or ignored.
*/
@@ -200,19 +170,16 @@ public:
/**
* This class should be derived by callers.
*/
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
class UAVCAN_EXPORT TransferListenerWithFilter : public TransferListener<MaxBufSize, NumStaticBufs, NumStaticReceivers>
class UAVCAN_EXPORT TransferListenerWithFilter : public TransferListener
{
const ITransferAcceptanceFilter* filter_;
virtual void handleFrame(const RxFrame& frame);
public:
typedef TransferListener<MaxBufSize, NumStaticBufs, NumStaticReceivers> BaseType;
TransferListenerWithFilter(TransferPerfCounter& perf, const DataTypeDescriptor& data_type,
IPoolAllocator& allocator)
: BaseType(perf, data_type, allocator)
uint16_t max_buffer_size, IPoolAllocator& allocator)
: TransferListener(perf, data_type, max_buffer_size, allocator)
, filter_(NULL)
{ }
@@ -222,27 +189,6 @@ public:
}
};
// ----------------------------------------------------------------------------
/*
* TransferListenerWithFilter<>
*/
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
void TransferListenerWithFilter<MaxBufSize, NumStaticBufs, NumStaticReceivers>::handleFrame(const RxFrame& frame)
{
if (filter_ != NULL)
{
if (filter_->shouldAcceptFrame(frame))
{
BaseType::handleFrame(frame);
}
}
else
{
UAVCAN_ASSERT(0);
}
}
}
#endif // UAVCAN_TRANSPORT_TRANSFER_LISTENER_HPP_INCLUDED
+28 -226
View File
@@ -18,9 +18,7 @@ namespace uavcan
/**
* Slow but memory efficient KV container.
*
* KV pairs can be allocated in a static buffer or in the node's memory pool if the static buffer is exhausted.
* When a KV pair is deleted from the static buffer, one pair from the memory pool will be moved in the free
* slot of the static buffer, so the use of the memory pool is minimized.
* KV pairs will be allocated in the node's memory pool.
*
* Please be aware that this container does not perform any speed optimizations to minimize memory footprint,
* so the complexity of most operations is O(N).
@@ -32,10 +30,8 @@ namespace uavcan
* Size of Key + Value + padding must not exceed MemPoolBlockSize.
*/
template <typename Key, typename Value>
class UAVCAN_EXPORT MapBase : Noncopyable
class UAVCAN_EXPORT Map : Noncopyable
{
template <typename, typename, unsigned> friend class Map;
public:
struct KVPair
{
@@ -102,16 +98,9 @@ private:
LinkedListRoot<KVGroup> list_;
IPoolAllocator& allocator_;
#if !UAVCAN_TINY
KVPair* const static_;
const unsigned num_static_entries_;
#endif
KVPair* findKey(const Key& key);
#if !UAVCAN_TINY
void optimizeStorage();
#endif
void compact();
struct YesPredicate
@@ -119,30 +108,18 @@ private:
bool operator()(const Key&, const Value&) const { return true; }
};
protected:
#if UAVCAN_TINY
MapBase(IPoolAllocator& allocator)
: allocator_(allocator)
{
UAVCAN_ASSERT(Key() == Key());
}
#else
MapBase(KVPair* static_buf, unsigned num_static_entries, IPoolAllocator& allocator)
: allocator_(allocator)
, static_(static_buf)
, num_static_entries_(num_static_entries)
{
UAVCAN_ASSERT(Key() == Key());
}
#endif
/// Derived class destructor must call clear();
~MapBase()
{
UAVCAN_ASSERT(getSize() == 0);
}
public:
Map(IPoolAllocator& allocator) :
allocator_(allocator)
{
UAVCAN_ASSERT(Key() == Key());
}
~Map()
{
clear();
}
/**
* Returns null pointer if there's no such entry.
*/
@@ -192,69 +169,20 @@ public:
*/
bool isEmpty() const { return find(YesPredicate()) == NULL; }
unsigned getSize() const;
/**
* For testing, do not use directly.
* Complexity is O(N).
*/
unsigned getNumStaticPairs() const;
unsigned getNumDynamicPairs() const;
};
template <typename Key, typename Value, unsigned NumStaticEntries = 0>
class UAVCAN_EXPORT Map : public MapBase<Key, Value>
{
typename MapBase<Key, Value>::KVPair static_[NumStaticEntries];
public:
#if !UAVCAN_TINY
// This instantiation will not be valid in UAVCAN_TINY mode
explicit Map(IPoolAllocator& allocator)
: MapBase<Key, Value>(static_, NumStaticEntries, allocator)
{ }
~Map() { this->clear(); }
#endif // !UAVCAN_TINY
};
template <typename Key, typename Value>
class UAVCAN_EXPORT Map<Key, Value, 0> : public MapBase<Key, Value>
{
public:
explicit Map(IPoolAllocator& allocator)
#if UAVCAN_TINY
: MapBase<Key, Value>(allocator)
#else
: MapBase<Key, Value>(NULL, 0, allocator)
#endif
{ }
~Map() { this->clear(); }
unsigned getSize() const;
};
// ----------------------------------------------------------------------------
/*
* MapBase<>
* Map<>
*/
template <typename Key, typename Value>
typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::findKey(const Key& key)
typename Map<Key, Value>::KVPair* Map<Key, Value>::findKey(const Key& key)
{
#if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (static_[i].match(key))
{
return static_ + i;
}
}
#endif
KVGroup* p = list_.get();
while (p)
{
@@ -268,64 +196,8 @@ typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::findKey(const Key& ke
return NULL;
}
#if !UAVCAN_TINY
template <typename Key, typename Value>
void MapBase<Key, Value>::optimizeStorage()
{
while (true)
{
// Looking for first EMPTY static entry
KVPair* stat = NULL;
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (static_[i].match(Key()))
{
stat = static_ + i;
break;
}
}
if (stat == NULL)
{
break;
}
// Looking for the first NON-EMPTY dynamic entry, erasing immediately
KVGroup* p = list_.get();
KVPair dyn;
while (p)
{
bool stop = false;
for (int i = 0; i < KVGroup::NumKV; i++)
{
if (!p->kvs[i].match(Key())) // Non empty
{
dyn = p->kvs[i]; // Copy by value
p->kvs[i] = KVPair(); // Erase immediately
stop = true;
break;
}
}
if (stop)
{
break;
}
p = p->getNextListNode();
}
if (dyn.match(Key()))
{
break;
}
// Migrating
*stat = dyn;
}
}
#endif // !UAVCAN_TINY
template <typename Key, typename Value>
void MapBase<Key, Value>::compact()
void Map<Key, Value>::compact()
{
KVGroup* p = list_.get();
while (p)
@@ -350,7 +222,7 @@ void MapBase<Key, Value>::compact()
}
template <typename Key, typename Value>
Value* MapBase<Key, Value>::access(const Key& key)
Value* Map<Key, Value>::access(const Key& key)
{
UAVCAN_ASSERT(!(key == Key()));
KVPair* const kv = findKey(key);
@@ -358,7 +230,7 @@ Value* MapBase<Key, Value>::access(const Key& key)
}
template <typename Key, typename Value>
Value* MapBase<Key, Value>::insert(const Key& key, const Value& value)
Value* Map<Key, Value>::insert(const Key& key, const Value& value)
{
UAVCAN_ASSERT(!(key == Key()));
remove(key);
@@ -381,40 +253,23 @@ Value* MapBase<Key, Value>::insert(const Key& key, const Value& value)
}
template <typename Key, typename Value>
void MapBase<Key, Value>::remove(const Key& key)
void Map<Key, Value>::remove(const Key& key)
{
UAVCAN_ASSERT(!(key == Key()));
KVPair* const kv = findKey(key);
if (kv)
{
*kv = KVPair();
#if !UAVCAN_TINY
optimizeStorage();
#endif
compact();
}
}
template <typename Key, typename Value>
template <typename Predicate>
void MapBase<Key, Value>::removeAllWhere(Predicate predicate)
void Map<Key, Value>::removeAllWhere(Predicate predicate)
{
unsigned num_removed = 0;
#if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (!static_[i].match(Key()))
{
if (predicate(static_[i].key, static_[i].value))
{
num_removed++;
static_[i] = KVPair();
}
}
}
#endif
KVGroup* p = list_.get();
while (p != NULL)
{
@@ -438,30 +293,14 @@ void MapBase<Key, Value>::removeAllWhere(Predicate predicate)
if (num_removed > 0)
{
#if !UAVCAN_TINY
optimizeStorage();
#endif
compact();
}
}
template <typename Key, typename Value>
template <typename Predicate>
const Key* MapBase<Key, Value>::find(Predicate predicate) const
const Key* Map<Key, Value>::find(Predicate predicate) const
{
#if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (!static_[i].match(Key()))
{
if (predicate(static_[i].key, static_[i].value))
{
return &static_[i].key;
}
}
}
#endif
KVGroup* p = list_.get();
while (p != NULL)
{
@@ -485,29 +324,14 @@ const Key* MapBase<Key, Value>::find(Predicate predicate) const
}
template <typename Key, typename Value>
void MapBase<Key, Value>::clear()
void Map<Key, Value>::clear()
{
removeAllWhere(YesPredicate());
}
template <typename Key, typename Value>
typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned index)
typename Map<Key, Value>::KVPair* Map<Key, Value>::getByIndex(unsigned index)
{
#if !UAVCAN_TINY
// Checking the static storage
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (!static_[i].match(Key()))
{
if (index == 0)
{
return static_ + i;
}
index--;
}
}
#endif
// Slowly crawling through the dynamic storage
KVGroup* p = list_.get();
while (p != NULL)
@@ -534,35 +358,13 @@ typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned i
}
template <typename Key, typename Value>
const typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned index) const
const typename Map<Key, Value>::KVPair* Map<Key, Value>::getByIndex(unsigned index) const
{
return const_cast<MapBase<Key, Value>*>(this)->getByIndex(index);
return const_cast<Map<Key, Value>*>(this)->getByIndex(index);
}
template <typename Key, typename Value>
unsigned MapBase<Key, Value>::getSize() const
{
return getNumStaticPairs() + getNumDynamicPairs();
}
template <typename Key, typename Value>
unsigned MapBase<Key, Value>::getNumStaticPairs() const
{
unsigned num = 0;
#if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (!static_[i].match(Key()))
{
num++;
}
}
#endif
return num;
}
template <typename Key, typename Value>
unsigned MapBase<Key, Value>::getNumDynamicPairs() const
unsigned Map<Key, Value>::getSize() const
{
unsigned num = 0;
KVGroup* p = list_.get();
+15 -79
View File
@@ -23,11 +23,9 @@ namespace uavcan
* Slow but memory efficient unordered multiset. Unlike Map<>, this container does not move objects, so
* they don't have to be copyable.
*
* Items can be allocated in a static buffer or in the node's memory pool if the static buffer is exhausted.
*
* Number of static entries must not be less than 1.
* Items will be allocated in the node's memory pool.
*/
template <typename T, unsigned NumStaticEntries>
template <typename T>
class UAVCAN_EXPORT Multiset : Noncopyable
{
struct Item : ::uavcan::Noncopyable
@@ -122,7 +120,6 @@ private:
*/
LinkedListRoot<Chunk> list_;
IPoolAllocator& allocator_;
Item static_[NumStaticEntries];
/*
* Methods
@@ -333,13 +330,7 @@ public:
* Counts number of items stored.
* Best case complexity is O(N).
*/
unsigned getSize() const { return getNumStaticItems() + getNumDynamicItems(); }
/**
* For testing, do not use directly.
*/
unsigned getNumStaticItems() const;
unsigned getNumDynamicItems() const;
unsigned getSize() const;
};
// ----------------------------------------------------------------------------
@@ -347,21 +338,10 @@ public:
/*
* Multiset<>
*/
template <typename T, unsigned NumStaticEntries>
typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::findOrCreateFreeSlot()
template <typename T>
typename Multiset<T>::Item* Multiset<T>::findOrCreateFreeSlot()
{
#if !UAVCAN_TINY
// Search in static pool
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (!static_[i].isConstructed())
{
return &static_[i];
}
}
#endif
// Search in dynamic pool
// Search
{
Chunk* p = list_.get();
while (p)
@@ -375,7 +355,7 @@ typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::fin
}
}
// Create new dynamic chunk
// Create new chunk
Chunk* const chunk = Chunk::instantiate(allocator_);
if (chunk == NULL)
{
@@ -385,8 +365,8 @@ typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::fin
return &chunk->items[0];
}
template <typename T, unsigned NumStaticEntries>
void Multiset<T, NumStaticEntries>::compact()
template <typename T>
void Multiset<T>::compact()
{
Chunk* p = list_.get();
while (p)
@@ -410,30 +390,12 @@ void Multiset<T, NumStaticEntries>::compact()
}
}
template <typename T, unsigned NumStaticEntries>
template <typename T>
template <typename Predicate>
void Multiset<T, NumStaticEntries>::removeWhere(Predicate predicate, const RemoveStrategy strategy)
void Multiset<T>::removeWhere(Predicate predicate, const RemoveStrategy strategy)
{
unsigned num_removed = 0;
#if !UAVCAN_TINY
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (static_[i].isConstructed())
{
if (predicate(*static_[i].ptr))
{
num_removed++;
static_[i].destroy();
if (strategy == RemoveOne)
{
break;
}
}
}
}
#endif
Chunk* p = list_.get();
while (p != NULL)
{
@@ -470,23 +432,10 @@ void Multiset<T, NumStaticEntries>::removeWhere(Predicate predicate, const Remov
}
}
template <typename T, unsigned NumStaticEntries>
template <typename T>
template <typename Predicate>
T* Multiset<T, NumStaticEntries>::find(Predicate predicate)
T* Multiset<T>::find(Predicate predicate)
{
#if !UAVCAN_TINY
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (static_[i].isConstructed())
{
if (predicate(*static_[i].ptr))
{
return static_[i].ptr;
}
}
}
#endif
Chunk* p = list_.get();
while (p != NULL)
{
@@ -508,21 +457,8 @@ T* Multiset<T, NumStaticEntries>::find(Predicate predicate)
return NULL;
}
template <typename T, unsigned NumStaticEntries>
unsigned Multiset<T, NumStaticEntries>::getNumStaticItems() const
{
unsigned num = 0;
#if !UAVCAN_TINY
for (unsigned i = 0; i < NumStaticEntries; i++)
{
num += static_[i].isConstructed() ? 1U : 0U;
}
#endif
return num;
}
template <typename T, unsigned NumStaticEntries>
unsigned Multiset<T, NumStaticEntries>::getNumDynamicItems() const
template <typename T>
unsigned Multiset<T>::getSize() const
{
unsigned num = 0;
Chunk* p = list_.get();
-148
View File
@@ -14,8 +14,6 @@ namespace uavcan
static const unsigned char reverse_mask[] = { 0x00U, 0x80U, 0xC0U, 0xE0U, 0xF0U, 0xF8U, 0xFCU, 0xFEU, 0xFFU };
static const unsigned char reverse_mask_xor[] = { 0xFFU, 0x7FU, 0x3FU, 0x1FU, 0x0FU, 0x07U, 0x03U, 0x01U, 0x00U };
#if UAVCAN_TINY
#define PREPARE_FIRST_COPY() \
do { \
if (src_len >= (CHAR_BIT - dst_offset_modulo)) { \
@@ -120,150 +118,4 @@ void bitarrayCopy(const unsigned char* src_org, unsigned src_offset, unsigned sr
}
}
#else
/*
* Functions below were manually optimized in the most horrible way.
*/
void bitarrayCopyAlignedToUnaligned(const unsigned char* src_org, unsigned src_len,
unsigned char* dst_org, unsigned dst_offset)
{
if (src_len > 0U)
{
unsigned char* dst = dst_org + (dst_offset / CHAR_BIT);
const unsigned dst_offset_modulo = dst_offset % CHAR_BIT;
if (0U == dst_offset_modulo)
{
const unsigned byte_len = src_len / CHAR_BIT;
const unsigned src_len_modulo = src_len % CHAR_BIT;
if (byte_len > 0U)
{
(void)std::memcpy(dst, src_org, byte_len);
src_org += byte_len;
dst += byte_len;
}
if (src_len_modulo > 0U)
{
*dst &= reverse_mask_xor[src_len_modulo];
*dst |= reverse_mask[src_len_modulo] & *src_org;
}
}
else
{
const unsigned bit_diff_ls = CHAR_BIT - dst_offset_modulo;
unsigned char c =
static_cast<unsigned char>(*src_org >> dst_offset_modulo & reverse_mask_xor[dst_offset_modulo]);
if (src_len >= (CHAR_BIT - dst_offset_modulo))
{
*dst &= reverse_mask[dst_offset_modulo];
src_len -= CHAR_BIT - dst_offset_modulo;
}
else
{
*dst &= reverse_mask[dst_offset_modulo] | reverse_mask_xor[dst_offset_modulo + src_len + 1];
c &= reverse_mask[dst_offset_modulo + src_len];
src_len = 0;
}
*dst++ |= c;
int byte_len = int(src_len / CHAR_BIT);
while (--byte_len >= 0)
{
c = static_cast<unsigned char>(*src_org++ << bit_diff_ls);
c = static_cast<unsigned char>(c | (*src_org >> dst_offset_modulo));
*dst++ = c;
}
const unsigned src_len_modulo = src_len % CHAR_BIT;
if (src_len_modulo > 0U)
{
c = static_cast<unsigned char>(*src_org++ << bit_diff_ls);
c = static_cast<unsigned char>(c | (*src_org >> dst_offset_modulo));
c &= reverse_mask[src_len_modulo];
*dst &= reverse_mask_xor[src_len_modulo];
*dst |= c;
}
}
}
}
void bitarrayCopyUnalignedToAligned(const unsigned char* src_org, unsigned src_offset, unsigned src_len,
unsigned char* dst_org)
{
if (src_len > 0U)
{
const unsigned char* src = src_org + (src_offset / CHAR_BIT);
const unsigned src_offset_modulo = src_offset % CHAR_BIT;
if (src_offset_modulo == 0U)
{
const unsigned byte_len = src_len / CHAR_BIT;
const unsigned src_len_modulo = src_len % CHAR_BIT;
if (byte_len > 0U)
{
(void)std::memcpy(dst_org, src, byte_len);
src += byte_len;
dst_org += byte_len;
}
if (src_len_modulo > 0U)
{
*dst_org &= reverse_mask_xor[src_len_modulo];
*dst_org |= reverse_mask[src_len_modulo] & *src;
}
}
else
{
const unsigned bit_diff_rs = CHAR_BIT - src_offset_modulo;
unsigned char c = static_cast<unsigned char>(*src++ << src_offset_modulo);
c = static_cast<unsigned char>(c | (*src >> bit_diff_rs));
if (src_len >= CHAR_BIT)
{
*dst_org &= 0x55U;
src_len -= CHAR_BIT;
}
else
{
*dst_org &= 0x55U | reverse_mask_xor[src_len + 1];
c &= reverse_mask[src_len];
src_len = 0;
}
*dst_org++ |= c;
int byte_len = int(src_len / CHAR_BIT);
while (--byte_len >= 0)
{
c = static_cast<unsigned char>(*src++ << src_offset_modulo);
c = static_cast<unsigned char>(c | (*src >> bit_diff_rs));
*dst_org++ = c;
}
const unsigned src_len_modulo = src_len % CHAR_BIT;
if (src_len_modulo > 0U)
{
c = static_cast<unsigned char>(*src++ << src_offset_modulo);
c = static_cast<unsigned char>(c | (*src >> bit_diff_rs));
c &= reverse_mask[src_len_modulo];
*dst_org &= reverse_mask_xor[src_len_modulo];
*dst_org |= c;
}
}
}
}
#endif
}
+3 -3
View File
@@ -7,8 +7,8 @@
namespace uavcan
{
int GenericSubscriberBase::genericStart(TransferListenerBase* listener,
bool (Dispatcher::*registration_method)(TransferListenerBase*))
int GenericSubscriberBase::genericStart(TransferListener* listener,
bool (Dispatcher::*registration_method)(TransferListener*))
{
if (listener == NULL)
{
@@ -24,7 +24,7 @@ int GenericSubscriberBase::genericStart(TransferListenerBase* listener,
return 0;
}
void GenericSubscriberBase::stop(TransferListenerBase* listener)
void GenericSubscriberBase::stop(TransferListener* listener)
{
if (listener != NULL)
{
@@ -38,8 +38,8 @@ int16_t CanAcceptanceFilterConfigurator::loadInputConfiguration(AnonymousMessage
return -ErrMemory;
}
const TransferListenerBase* p = node_.getDispatcher().getListOfMessageListeners().get();
while (p)
const TransferListener* p = node_.getDispatcher().getListOfMessageListeners().get();
while (p != NULL)
{
CanFilterConfig cfg;
cfg.id = static_cast<uint32_t>(p->getDataTypeDescriptor().getID().get()) << 8;
+2 -2
View File
@@ -295,10 +295,10 @@ CanIOManager::CanIOManager(ICanDriver& driver, IPoolAllocator& allocator, ISyste
if (mem_blocks_per_iface == 0)
{
mem_blocks_per_iface = allocator.getNumBlocks() / (num_ifaces_ + 1U) + 1U;
mem_blocks_per_iface = allocator.getBlockCapacity() / (num_ifaces_ + 1U) + 1U;
}
UAVCAN_TRACE("CanIOManager", "Memory blocks per iface: %u, total: %u",
unsigned(mem_blocks_per_iface), unsigned(allocator.getNumBlocks()));
unsigned(mem_blocks_per_iface), unsigned(allocator.getBlockCapacity()));
for (int i = 0; i < num_ifaces_; i++)
{
+14 -14
View File
@@ -72,11 +72,11 @@ void LoopbackFrameListenerRegistry::invokeListeners(RxFrame& frame)
/*
* Dispatcher::ListenerRegister
*/
bool Dispatcher::ListenerRegistry::add(TransferListenerBase* listener, Mode mode)
bool Dispatcher::ListenerRegistry::add(TransferListener* listener, Mode mode)
{
if (mode == UniqueListener)
{
TransferListenerBase* p = list_.get();
TransferListener* p = list_.get();
while (p)
{
if (p->getDataTypeDescriptor().getID() == listener->getDataTypeDescriptor().getID())
@@ -91,14 +91,14 @@ bool Dispatcher::ListenerRegistry::add(TransferListenerBase* listener, Mode mode
return true;
}
void Dispatcher::ListenerRegistry::remove(TransferListenerBase* listener)
void Dispatcher::ListenerRegistry::remove(TransferListener* listener)
{
list_.remove(listener);
}
bool Dispatcher::ListenerRegistry::exists(DataTypeID dtid) const
{
TransferListenerBase* p = list_.get();
TransferListener* p = list_.get();
while (p)
{
if (p->getDataTypeDescriptor().getID() == dtid)
@@ -112,10 +112,10 @@ bool Dispatcher::ListenerRegistry::exists(DataTypeID dtid) const
void Dispatcher::ListenerRegistry::cleanup(MonotonicTime ts)
{
TransferListenerBase* p = list_.get();
TransferListener* p = list_.get();
while (p)
{
TransferListenerBase* const next = p->getNextListNode();
TransferListener* const next = p->getNextListNode();
p->cleanup(ts); // p may be modified
p = next;
}
@@ -123,10 +123,10 @@ void Dispatcher::ListenerRegistry::cleanup(MonotonicTime ts)
void Dispatcher::ListenerRegistry::handleFrame(const RxFrame& frame)
{
TransferListenerBase* p = list_.get();
TransferListener* p = list_.get();
while (p)
{
TransferListenerBase* const next = p->getNextListNode();
TransferListener* const next = p->getNextListNode();
if (p->getDataTypeDescriptor().getID() == frame.getDataTypeID())
{
p->handleFrame(frame); // p may be modified
@@ -311,7 +311,7 @@ void Dispatcher::cleanup(MonotonicTime ts)
lsrv_resp_.cleanup(ts);
}
bool Dispatcher::registerMessageListener(TransferListenerBase* listener)
bool Dispatcher::registerMessageListener(TransferListener* listener)
{
if (listener->getDataTypeDescriptor().getKind() != DataTypeKindMessage)
{
@@ -321,7 +321,7 @@ bool Dispatcher::registerMessageListener(TransferListenerBase* listener)
return lmsg_.add(listener, ListenerRegistry::ManyListeners); // Multiple subscribers are OK
}
bool Dispatcher::registerServiceRequestListener(TransferListenerBase* listener)
bool Dispatcher::registerServiceRequestListener(TransferListener* listener)
{
if (listener->getDataTypeDescriptor().getKind() != DataTypeKindService)
{
@@ -331,7 +331,7 @@ bool Dispatcher::registerServiceRequestListener(TransferListenerBase* listener)
return lsrv_req_.add(listener, ListenerRegistry::UniqueListener); // Only one server per data type
}
bool Dispatcher::registerServiceResponseListener(TransferListenerBase* listener)
bool Dispatcher::registerServiceResponseListener(TransferListener* listener)
{
if (listener->getDataTypeDescriptor().getKind() != DataTypeKindService)
{
@@ -341,17 +341,17 @@ bool Dispatcher::registerServiceResponseListener(TransferListenerBase* listener)
return lsrv_resp_.add(listener, ListenerRegistry::ManyListeners); // Multiple callers may call same srv
}
void Dispatcher::unregisterMessageListener(TransferListenerBase* listener)
void Dispatcher::unregisterMessageListener(TransferListener* listener)
{
lmsg_.remove(listener);
}
void Dispatcher::unregisterServiceRequestListener(TransferListenerBase* listener)
void Dispatcher::unregisterServiceRequestListener(TransferListener* listener)
{
lsrv_req_.remove(listener);
}
void Dispatcher::unregisterServiceResponseListener(TransferListenerBase* listener)
void Dispatcher::unregisterServiceResponseListener(TransferListener* listener)
{
lsrv_resp_.remove(listener);
}
@@ -20,8 +20,36 @@ std::string OutgoingTransferRegistryKey::toString() const
#endif
/*
* IOutgoingTransferRegistry
* OutgoingTransferRegistry
*/
const MonotonicDuration IOutgoingTransferRegistry::MinEntryLifetime = MonotonicDuration::fromMSec(2000);
const MonotonicDuration OutgoingTransferRegistry::MinEntryLifetime = MonotonicDuration::fromMSec(2000);
TransferID* OutgoingTransferRegistry::accessOrCreate(const OutgoingTransferRegistryKey& key,
MonotonicTime new_deadline)
{
UAVCAN_ASSERT(!new_deadline.isZero());
Value* p = map_.access(key);
if (p == NULL)
{
p = map_.insert(key, Value());
if (p == NULL)
{
return NULL;
}
UAVCAN_TRACE("OutgoingTransferRegistry", "Created %s", key.toString().c_str());
}
p->deadline = new_deadline;
return &p->tid;
}
bool OutgoingTransferRegistry::exists(DataTypeID dtid, TransferType tt) const
{
return NULL != map_.find(ExistenceCheckingPredicate(dtid, tt));
}
void OutgoingTransferRegistry::cleanup(MonotonicTime ts)
{
map_.removeAllWhere(DeadlineExpiredPredicate(ts));
}
}
+103 -250
View File
@@ -8,6 +8,58 @@
namespace uavcan
{
/*
* StaticTransferBufferImpl
*/
int StaticTransferBufferImpl::read(unsigned offset, uint8_t* data, unsigned len) const
{
if (!data)
{
UAVCAN_ASSERT(0);
return -ErrInvalidParam;
}
if (offset >= max_write_pos_)
{
return 0;
}
if ((offset + len) > max_write_pos_)
{
len = max_write_pos_ - offset;
}
UAVCAN_ASSERT((offset + len) <= max_write_pos_);
(void)copy(data_ + offset, data_ + offset + len, data);
return int(len);
}
int StaticTransferBufferImpl::write(unsigned offset, const uint8_t* data, unsigned len)
{
if (!data)
{
UAVCAN_ASSERT(0);
return -ErrInvalidParam;
}
if (offset >= size_)
{
return 0;
}
if ((offset + len) > size_)
{
len = size_ - offset;
}
UAVCAN_ASSERT((offset + len) <= size_);
(void)copy(data, data + len, data_ + offset);
max_write_pos_ = max(uint16_t(offset + len), uint16_t(max_write_pos_));
return int(len);
}
void StaticTransferBufferImpl::reset()
{
max_write_pos_ = 0;
#if UAVCAN_DEBUG
fill(data_, data_ + size_, uint8_t(0));
#endif
}
/*
* TransferBufferManagerKey
*/
@@ -23,8 +75,8 @@ std::string TransferBufferManagerKey::toString() const
/*
* DynamicTransferBuffer::Block
*/
DynamicTransferBufferManagerEntry::Block*
DynamicTransferBufferManagerEntry::Block::instantiate(IPoolAllocator& allocator)
TransferBufferManagerEntry::Block*
TransferBufferManagerEntry::Block::instantiate(IPoolAllocator& allocator)
{
void* const praw = allocator.allocate(sizeof(Block));
if (praw == NULL)
@@ -34,7 +86,7 @@ DynamicTransferBufferManagerEntry::Block::instantiate(IPoolAllocator& allocator)
return new (praw) Block;
}
void DynamicTransferBufferManagerEntry::Block::destroy(Block*& obj, IPoolAllocator& allocator)
void TransferBufferManagerEntry::Block::destroy(Block*& obj, IPoolAllocator& allocator)
{
if (obj != NULL)
{
@@ -44,8 +96,8 @@ void DynamicTransferBufferManagerEntry::Block::destroy(Block*& obj, IPoolAllocat
}
}
void DynamicTransferBufferManagerEntry::Block::read(uint8_t*& outptr, unsigned target_offset,
unsigned& total_offset, unsigned& left_to_read)
void TransferBufferManagerEntry::Block::read(uint8_t*& outptr, unsigned target_offset,
unsigned& total_offset, unsigned& left_to_read)
{
UAVCAN_ASSERT(outptr);
for (unsigned i = 0; (i < Block::Size) && (left_to_read > 0); i++, total_offset++)
@@ -58,7 +110,7 @@ void DynamicTransferBufferManagerEntry::Block::read(uint8_t*& outptr, unsigned t
}
}
void DynamicTransferBufferManagerEntry::Block::write(const uint8_t*& inptr, unsigned target_offset,
void TransferBufferManagerEntry::Block::write(const uint8_t*& inptr, unsigned target_offset,
unsigned& total_offset, unsigned& left_to_write)
{
UAVCAN_ASSERT(inptr);
@@ -75,46 +127,28 @@ void DynamicTransferBufferManagerEntry::Block::write(const uint8_t*& inptr, unsi
/*
* DynamicTransferBuffer
*/
DynamicTransferBufferManagerEntry* DynamicTransferBufferManagerEntry::instantiate(IPoolAllocator& allocator,
TransferBufferManagerEntry* TransferBufferManagerEntry::instantiate(IPoolAllocator& allocator,
uint16_t max_size)
{
void* const praw = allocator.allocate(sizeof(DynamicTransferBufferManagerEntry));
void* const praw = allocator.allocate(sizeof(TransferBufferManagerEntry));
if (praw == NULL)
{
return NULL;
}
return new (praw) DynamicTransferBufferManagerEntry(allocator, max_size);
return new (praw) TransferBufferManagerEntry(allocator, max_size);
}
void DynamicTransferBufferManagerEntry::destroy(DynamicTransferBufferManagerEntry*& obj, IPoolAllocator& allocator)
void TransferBufferManagerEntry::destroy(TransferBufferManagerEntry*& obj, IPoolAllocator& allocator)
{
if (obj != NULL)
{
obj->~DynamicTransferBufferManagerEntry();
obj->~TransferBufferManagerEntry();
allocator.deallocate(obj);
obj = NULL;
}
}
void DynamicTransferBufferManagerEntry::doReset()
{
max_write_pos_ = 0;
Block* p = blocks_.get();
while (p)
{
Block* const next = p->getNextListNode();
blocks_.remove(p);
Block::destroy(p, allocator_);
p = next;
}
}
void DynamicTransferBufferManagerEntry::resetImpl()
{
doReset();
}
int DynamicTransferBufferManagerEntry::read(unsigned offset, uint8_t* data, unsigned len) const
int TransferBufferManagerEntry::read(unsigned offset, uint8_t* data, unsigned len) const
{
if (!data)
{
@@ -150,7 +184,7 @@ int DynamicTransferBufferManagerEntry::read(unsigned offset, uint8_t* data, unsi
return int(len);
}
int DynamicTransferBufferManagerEntry::write(unsigned offset, const uint8_t* data, unsigned len)
int TransferBufferManagerEntry::write(unsigned offset, const uint8_t* data, unsigned len)
{
if (!data)
{
@@ -221,131 +255,26 @@ int DynamicTransferBufferManagerEntry::write(unsigned offset, const uint8_t* dat
return int(actually_written);
}
/*
* StaticTransferBufferImpl
*/
int StaticTransferBufferImpl::read(unsigned offset, uint8_t* data, unsigned len) const
{
if (!data)
{
UAVCAN_ASSERT(0);
return -ErrInvalidParam;
}
if (offset >= max_write_pos_)
{
return 0;
}
if ((offset + len) > max_write_pos_)
{
len = max_write_pos_ - offset;
}
UAVCAN_ASSERT((offset + len) <= max_write_pos_);
(void)copy(data_ + offset, data_ + offset + len, data);
return int(len);
}
int StaticTransferBufferImpl::write(unsigned offset, const uint8_t* data, unsigned len)
{
if (!data)
{
UAVCAN_ASSERT(0);
return -ErrInvalidParam;
}
if (offset >= size_)
{
return 0;
}
if ((offset + len) > size_)
{
len = size_ - offset;
}
UAVCAN_ASSERT((offset + len) <= size_);
(void)copy(data, data + len, data_ + offset);
max_write_pos_ = max(uint16_t(offset + len), uint16_t(max_write_pos_));
return int(len);
}
void StaticTransferBufferImpl::reset()
void TransferBufferManagerEntry::reset(const TransferBufferManagerKey& key)
{
key_ = key;
max_write_pos_ = 0;
#if UAVCAN_DEBUG
fill(data_, data_ + size_, uint8_t(0));
#endif
Block* p = blocks_.get();
while (p)
{
Block* const next = p->getNextListNode();
blocks_.remove(p);
Block::destroy(p, allocator_);
p = next;
}
}
/*
* StaticTransferBufferManagerEntryImpl
* TransferBufferManager
*/
void StaticTransferBufferManagerEntryImpl::resetImpl()
TransferBufferManagerEntry* TransferBufferManager::findFirst(const TransferBufferManagerKey& key)
{
buf_.reset();
}
int StaticTransferBufferManagerEntryImpl::read(unsigned offset, uint8_t* data, unsigned len) const
{
return buf_.read(offset, data, len);
}
int StaticTransferBufferManagerEntryImpl::write(unsigned offset, const uint8_t* data, unsigned len)
{
return buf_.write(offset, data, len);
}
bool StaticTransferBufferManagerEntryImpl::migrateFrom(const TransferBufferManagerEntry* tbme)
{
if (tbme == NULL || tbme->isEmpty())
{
UAVCAN_ASSERT(0);
return false;
}
// Resetting self and moving all data from the source
TransferBufferManagerEntry::reset(tbme->getKey());
const int res = tbme->read(0, buf_.getRawPtr(), buf_.getSize());
if (res < 0)
{
TransferBufferManagerEntry::reset();
return false;
}
buf_.setMaxWritePos(uint16_t(res));
if (res < int(buf_.getSize()))
{
return true;
}
// Now we need to make sure that all data can fit the storage
uint8_t dummy = 0;
if (tbme->read(buf_.getSize(), &dummy, 1) > 0)
{
TransferBufferManagerEntry::reset(); // Damn, the buffer was too large
return false;
}
return true;
}
/*
* TransferBufferManagerImpl
*/
StaticTransferBufferManagerEntryImpl* TransferBufferManagerImpl::findFirstStatic(const TransferBufferManagerKey& key)
{
for (unsigned i = 0; true; i++)
{
StaticTransferBufferManagerEntryImpl* const sb = getStaticByIndex(uint16_t(i));
if (sb == NULL)
{
break;
}
if (sb->getKey() == key)
{
return sb;
}
}
return NULL;
}
DynamicTransferBufferManagerEntry* TransferBufferManagerImpl::findFirstDynamic(const TransferBufferManagerKey& key)
{
DynamicTransferBufferManagerEntry* dyn = dynamic_buffers_.get();
TransferBufferManagerEntry* dyn = buffers_.get();
while (dyn)
{
UAVCAN_ASSERT(!dyn->isEmpty());
@@ -358,68 +287,29 @@ DynamicTransferBufferManagerEntry* TransferBufferManagerImpl::findFirstDynamic(c
return NULL;
}
void TransferBufferManagerImpl::optimizeStorage()
TransferBufferManager::~TransferBufferManager()
{
while (!dynamic_buffers_.isEmpty())
{
StaticTransferBufferManagerEntryImpl* const sb = findFirstStatic(TransferBufferManagerKey());
if (sb == NULL)
{
break;
}
DynamicTransferBufferManagerEntry* dyn = dynamic_buffers_.get();
UAVCAN_ASSERT(dyn);
UAVCAN_ASSERT(!dyn->isEmpty());
if (sb->migrateFrom(dyn))
{
UAVCAN_ASSERT(!dyn->isEmpty());
UAVCAN_TRACE("TransferBufferManager", "Storage optimization: Migrated %s",
dyn->getKey().toString().c_str());
dynamic_buffers_.remove(dyn);
DynamicTransferBufferManagerEntry::destroy(dyn, allocator_);
}
else
{
/* Migration can fail if a dynamic buffer contains more data than a static buffer can accomodate.
* This should never happen during normal operation because dynamic buffers are limited in growth.
*/
UAVCAN_TRACE("TransferBufferManager", "Storage optimization: MIGRATION FAILURE %s MAXSIZE %u",
dyn->getKey().toString().c_str(), max_buf_size_);
UAVCAN_ASSERT(0);
sb->reset();
break;
}
}
}
TransferBufferManagerImpl::~TransferBufferManagerImpl()
{
DynamicTransferBufferManagerEntry* dyn = dynamic_buffers_.get();
TransferBufferManagerEntry* dyn = buffers_.get();
while (dyn)
{
DynamicTransferBufferManagerEntry* const next = dyn->getNextListNode();
dynamic_buffers_.remove(dyn);
DynamicTransferBufferManagerEntry::destroy(dyn, allocator_);
TransferBufferManagerEntry* const next = dyn->getNextListNode();
buffers_.remove(dyn);
TransferBufferManagerEntry::destroy(dyn, allocator_);
dyn = next;
}
}
ITransferBuffer* TransferBufferManagerImpl::access(const TransferBufferManagerKey& key)
ITransferBuffer* TransferBufferManager::access(const TransferBufferManagerKey& key)
{
if (key.isEmpty())
{
UAVCAN_ASSERT(0);
return NULL;
}
TransferBufferManagerEntry* tbme = findFirstStatic(key);
if (tbme)
{
return tbme;
}
return findFirstDynamic(key);
return findFirst(key);
}
ITransferBuffer* TransferBufferManagerImpl::create(const TransferBufferManagerKey& key)
ITransferBuffer* TransferBufferManager::create(const TransferBufferManagerKey& key)
{
if (key.isEmpty())
{
@@ -428,27 +318,17 @@ ITransferBuffer* TransferBufferManagerImpl::create(const TransferBufferManagerKe
}
remove(key);
TransferBufferManagerEntry* tbme = findFirstStatic(TransferBufferManagerKey());
TransferBufferManagerEntry* tbme = TransferBufferManagerEntry::instantiate(allocator_, max_buf_size_);
if (tbme == NULL)
{
DynamicTransferBufferManagerEntry* dyn =
DynamicTransferBufferManagerEntry::instantiate(allocator_, max_buf_size_);
tbme = dyn;
if (dyn == NULL)
{
return NULL; // Epic fail.
}
dynamic_buffers_.insert(dyn);
UAVCAN_TRACE("TransferBufferManager", "Dynamic buffer created [st=%u, dyn=%u], %s",
getNumStaticBuffers(), getNumDynamicBuffers(), key.toString().c_str());
}
else
{
UAVCAN_TRACE("TransferBufferManager", "Static buffer created [st=%u, dyn=%u], %s",
getNumStaticBuffers(), getNumDynamicBuffers(), key.toString().c_str());
return NULL; // Epic fail.
}
if (tbme)
buffers_.insert(tbme);
UAVCAN_TRACE("TransferBufferManager", "Buffer created [num=%u], %s", getNumBuffers(), key.toString().c_str());
if (tbme != NULL)
{
UAVCAN_ASSERT(tbme->isEmpty());
tbme->reset(key);
@@ -456,54 +336,27 @@ ITransferBuffer* TransferBufferManagerImpl::create(const TransferBufferManagerKe
return tbme;
}
void TransferBufferManagerImpl::remove(const TransferBufferManagerKey& key)
void TransferBufferManager::remove(const TransferBufferManagerKey& key)
{
UAVCAN_ASSERT(!key.isEmpty());
TransferBufferManagerEntry* const tbme = findFirstStatic(key);
if (tbme)
TransferBufferManagerEntry* dyn = findFirst(key);
if (dyn != NULL)
{
UAVCAN_TRACE("TransferBufferManager", "Static buffer deleted, %s", key.toString().c_str());
tbme->reset();
optimizeStorage();
return;
}
DynamicTransferBufferManagerEntry* dyn = findFirstDynamic(key);
if (dyn)
{
UAVCAN_TRACE("TransferBufferManager", "Dynamic buffer deleted, %s", key.toString().c_str());
dynamic_buffers_.remove(dyn);
DynamicTransferBufferManagerEntry::destroy(dyn, allocator_);
UAVCAN_TRACE("TransferBufferManager", "Buffer deleted, %s", key.toString().c_str());
buffers_.remove(dyn);
TransferBufferManagerEntry::destroy(dyn, allocator_);
}
}
bool TransferBufferManagerImpl::isEmpty() const
bool TransferBufferManager::isEmpty() const
{
return (getNumStaticBuffers() == 0) && (getNumDynamicBuffers() == 0);
return getNumBuffers() == 0;
}
unsigned TransferBufferManagerImpl::getNumDynamicBuffers() const
unsigned TransferBufferManager::getNumBuffers() const
{
return dynamic_buffers_.getLength();
}
unsigned TransferBufferManagerImpl::getNumStaticBuffers() const
{
unsigned res = 0;
for (unsigned i = 0; true; i++)
{
StaticTransferBufferManagerEntryImpl* const sb = getStaticByIndex(uint16_t(i));
if (sb == NULL)
{
break;
}
if (!sb->isEmpty())
{
res++;
}
}
return res;
return buffers_.getLength();
}
}
@@ -80,10 +80,10 @@ int MultiFrameIncomingTransfer::read(unsigned offset, uint8_t* data, unsigned le
}
/*
* TransferListenerBase::TimedOutReceiverPredicate
* TransferListener::TimedOutReceiverPredicate
*/
bool TransferListenerBase::TimedOutReceiverPredicate::operator()(const TransferBufferManagerKey& key,
const TransferReceiver& value) const
bool TransferListener::TimedOutReceiverPredicate::operator()(const TransferBufferManagerKey& key,
const TransferReceiver& value) const
{
if (value.isTimedOut(ts_))
{
@@ -101,9 +101,9 @@ bool TransferListenerBase::TimedOutReceiverPredicate::operator()(const TransferB
}
/*
* TransferListenerBase
* TransferListener
*/
bool TransferListenerBase::checkPayloadCrc(const uint16_t compare_with, const ITransferBuffer& tbb) const
bool TransferListener::checkPayloadCrc(const uint16_t compare_with, const ITransferBuffer& tbb) const
{
TransferCRC crc = crc_base_;
unsigned offset = 0;
@@ -113,7 +113,7 @@ bool TransferListenerBase::checkPayloadCrc(const uint16_t compare_with, const IT
const int res = tbb.read(offset, buf, sizeof(buf));
if (res < 0)
{
UAVCAN_TRACE("TransferListenerBase", "Failed to check CRC: Buffer read failure %i", res);
UAVCAN_TRACE("TransferListener", "Failed to check CRC: Buffer read failure %i", res);
return false;
}
if (res == 0)
@@ -125,14 +125,14 @@ bool TransferListenerBase::checkPayloadCrc(const uint16_t compare_with, const IT
}
if (crc.get() != compare_with)
{
UAVCAN_TRACE("TransferListenerBase", "CRC mismatch, expected=0x%04x, got=0x%04x",
UAVCAN_TRACE("TransferListener", "CRC mismatch, expected=0x%04x, got=0x%04x",
int(compare_with), int(crc.get()));
return false;
}
return true;
}
void TransferListenerBase::handleReception(TransferReceiver& receiver, const RxFrame& frame,
void TransferListener::handleReception(TransferReceiver& receiver, const RxFrame& frame,
TransferBufferAccessor& tba)
{
switch (receiver.addFrame(frame, tba))
@@ -155,12 +155,12 @@ void TransferListenerBase::handleReception(TransferReceiver& receiver, const RxF
const ITransferBuffer* tbb = tba.access();
if (tbb == NULL)
{
UAVCAN_TRACE("TransferListenerBase", "Buffer access failure, last frame: %s", frame.toString().c_str());
UAVCAN_TRACE("TransferListener", "Buffer access failure, last frame: %s", frame.toString().c_str());
break;
}
if (!checkPayloadCrc(receiver.getLastTransferCrc(), *tbb))
{
UAVCAN_TRACE("TransferListenerBase", "CRC error, last frame: %s", frame.toString().c_str());
UAVCAN_TRACE("TransferListener", "CRC error, last frame: %s", frame.toString().c_str());
break;
}
MultiFrameIncomingTransfer it(receiver.getLastTransferTimestampMonotonic(),
@@ -177,7 +177,7 @@ void TransferListenerBase::handleReception(TransferReceiver& receiver, const RxF
}
}
void TransferListenerBase::handleAnonymousTransferReception(const RxFrame& frame)
void TransferListener::handleAnonymousTransferReception(const RxFrame& frame)
{
if (allow_anonymous_transfers_)
{
@@ -187,13 +187,19 @@ void TransferListenerBase::handleAnonymousTransferReception(const RxFrame& frame
}
}
void TransferListenerBase::cleanup(MonotonicTime ts)
TransferListener::~TransferListener()
{
// Map must be cleared before bufmgr is destroyed
receivers_.clear();
}
void TransferListener::cleanup(MonotonicTime ts)
{
receivers_.removeAllWhere(TimedOutReceiverPredicate(ts, bufmgr_));
UAVCAN_ASSERT(receivers_.isEmpty() ? bufmgr_.isEmpty() : 1);
}
void TransferListenerBase::handleFrame(const RxFrame& frame)
void TransferListener::handleFrame(const RxFrame& frame)
{
if (frame.getSrcNodeID().isUnicast()) // Normal transfer
{
@@ -227,7 +233,25 @@ void TransferListenerBase::handleFrame(const RxFrame& frame)
}
else
{
UAVCAN_TRACE("TransferListenerBase", "Invalid frame: %s", frame.toString().c_str()); // Invalid frame
UAVCAN_TRACE("TransferListener", "Invalid frame: %s", frame.toString().c_str()); // Invalid frame
}
}
/*
* TransferListenerWithFilter
*/
void TransferListenerWithFilter::handleFrame(const RxFrame& frame)
{
if (filter_ != NULL)
{
if (filter_->shouldAcceptFrame(frame))
{
TransferListener::handleFrame(frame);
}
}
else
{
UAVCAN_ASSERT(0);
}
}
@@ -153,7 +153,7 @@ int TransferSender::send(const uint8_t* payload, unsigned payload_len, Monotonic
UAVCAN_ASSERT(!tx_deadline.isZero());
const MonotonicTime otr_deadline = tx_deadline + max(max_transfer_interval_ * 2,
IOutgoingTransferRegistry::MinEntryLifetime);
OutgoingTransferRegistry::MinEntryLifetime);
TransferID* const tid = dispatcher_.getOutgoingTransferRegistry().accessOrCreate(otr_key, otr_deadline);
if (tid == NULL)
+2 -2
View File
@@ -33,9 +33,9 @@ void LimitedPoolAllocator::deallocate(const void* ptr)
}
}
uint16_t LimitedPoolAllocator::getNumBlocks() const
uint16_t LimitedPoolAllocator::getBlockCapacity() const
{
return min(max_blocks_, allocator_.getNumBlocks());
return min(max_blocks_, allocator_.getBlockCapacity());
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ TEST(DynamicMemory, LimitedPoolAllocator)
uavcan::PoolAllocator<128, 32> pool32;
uavcan::LimitedPoolAllocator lim(pool32, 2);
EXPECT_EQ(2, lim.getNumBlocks());
EXPECT_EQ(2, lim.getBlockCapacity());
EXPECT_EQ(0, pool32.getPeakNumUsedBlocks());
const void* ptr1 = lim.allocate(1);
@@ -0,0 +1,179 @@
/*
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#include <gtest/gtest.h>
#include <uavcan/helpers/heap_based_pool_allocator.hpp>
#include <malloc.h>
TEST(HeapBasedPoolAllocator, Basic)
{
std::cout << ">>> HEAP BEFORE:" << std::endl;
malloc_stats();
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize> al(0xEEEE);
ASSERT_EQ(0, al.getNumReservedBlocks());
ASSERT_EQ(0, al.getNumAllocatedBlocks());
ASSERT_EQ(0xEEEE, al.getBlockCapacity());
ASSERT_EQ(0xFFFF, al.getBlockCapacityHardLimit());
void* a = al.allocate(10);
void* b = al.allocate(10);
void* c = al.allocate(10);
void* d = al.allocate(10);
ASSERT_EQ(4, al.getNumReservedBlocks());
ASSERT_EQ(4, al.getNumAllocatedBlocks());
al.deallocate(a);
ASSERT_EQ(4, al.getNumReservedBlocks());
ASSERT_EQ(3, al.getNumAllocatedBlocks());
al.deallocate(b);
ASSERT_EQ(4, al.getNumReservedBlocks());
ASSERT_EQ(2, al.getNumAllocatedBlocks());
al.deallocate(c);
ASSERT_EQ(4, al.getNumReservedBlocks());
ASSERT_EQ(1, al.getNumAllocatedBlocks());
a = al.allocate(10);
ASSERT_EQ(4, al.getNumReservedBlocks());
ASSERT_EQ(2, al.getNumAllocatedBlocks());
ASSERT_EQ(c, a);
al.deallocate(a);
ASSERT_EQ(4, al.getNumReservedBlocks());
ASSERT_EQ(1, al.getNumAllocatedBlocks());
al.shrink();
ASSERT_EQ(1, al.getNumReservedBlocks());
ASSERT_EQ(1, al.getNumAllocatedBlocks());
al.deallocate(d);
ASSERT_EQ(1, al.getNumReservedBlocks());
ASSERT_EQ(0, al.getNumAllocatedBlocks());
al.shrink();
ASSERT_EQ(0, al.getNumReservedBlocks());
ASSERT_EQ(0, al.getNumAllocatedBlocks());
std::cout << ">>> HEAP AFTER:" << std::endl;
malloc_stats();
}
TEST(HeapBasedPoolAllocator, Limits)
{
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize> al(2);
ASSERT_EQ(2, al.getBlockCapacity());
ASSERT_EQ(4, al.getBlockCapacityHardLimit());
ASSERT_EQ(0, al.getNumReservedBlocks());
ASSERT_EQ(0, al.getNumAllocatedBlocks());
void* a = al.allocate(10);
void* b = al.allocate(10);
void* c = al.allocate(10);
void* d = al.allocate(10);
ASSERT_TRUE(a);
ASSERT_TRUE(b);
ASSERT_TRUE(c);
ASSERT_TRUE(d);
ASSERT_FALSE(al.allocate(10));
ASSERT_EQ(4, al.getNumReservedBlocks());
ASSERT_EQ(4, al.getNumAllocatedBlocks());
al.deallocate(a);
al.deallocate(b);
al.deallocate(c);
al.deallocate(d);
ASSERT_EQ(4, al.getNumReservedBlocks());
ASSERT_EQ(0, al.getNumAllocatedBlocks());
}
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
#include <thread>
#include <mutex>
struct RaiiSynchronizer
{
static std::mutex mutex;
std::lock_guard<std::mutex> guard{mutex};
};
std::mutex RaiiSynchronizer::mutex;
TEST(HeapBasedPoolAllocator, Concurrency)
{
std::cout << ">>> HEAP BEFORE:" << std::endl;
malloc_stats();
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, RaiiSynchronizer> al(1000);
ASSERT_EQ(1000, al.getBlockCapacity());
ASSERT_EQ(2000, al.getBlockCapacityHardLimit());
volatile bool terminate = false;
/*
* Starting the testing threads
*/
std::thread threads[3];
for (auto& x : threads)
{
x = std::thread([&al, &terminate]()
{
while (!terminate)
{
auto a = al.allocate(1);
auto b = al.allocate(1);
auto c = al.allocate(1);
al.deallocate(al.allocate(1));
al.deallocate(a);
al.deallocate(b);
al.deallocate(c);
}
});
}
/*
* Running the threads for some time, then terminating
*/
std::this_thread::sleep_for(std::chrono::seconds(1));
terminate = true;
std::cout << "Terminating workers..." << std::endl;
for (auto& x : threads)
{
x.join();
}
std::cout << "All workers joined" << std::endl;
/*
* Now, there must not be any leaked memory, because the worker threads deallocate everything before completion.
*/
std::cout << "Allocated: " << al.getNumAllocatedBlocks() << std::endl;
std::cout << "Reserved: " << al.getNumReservedBlocks() << std::endl;
std::cout << ">>> HEAP BEFORE SHRINK:" << std::endl;
malloc_stats();
al.shrink();
std::cout << ">>> HEAP AFTER SHRINK:" << std::endl;
malloc_stats();
}
#endif
+4 -3
View File
@@ -31,11 +31,12 @@ TEST(Node, Basic)
swver.minor = 1;
swver.vcs_commit = 0xDEADBEEF;
std::cout << "sizeof(uavcan::Node<0>): " << sizeof(uavcan::Node<0>) << std::endl;
/*
* uavcan::Node
*/
uavcan::Node<0> node1(nodes.can_a, nodes.clock_a);
std::cout << "sizeof(uavcan::Node<0>): " << sizeof(uavcan::Node<0>) << std::endl;
uavcan::Node<1024> node1(nodes.can_a, nodes.clock_a);
node1.setName("com.example");
node1.setNodeID(1);
node1.setSoftwareVersion(swver);
@@ -43,7 +44,7 @@ TEST(Node, Basic)
/*
* Companion test node
*/
uavcan::Node<0> node2(nodes.can_b, nodes.clock_b);
uavcan::Node<1024> node2(nodes.can_b, nodes.clock_b);
node2.setName("foobar");
node2.setNodeID(2);
node2.setSoftwareVersion(swver);
+4 -3
View File
@@ -31,10 +31,12 @@ TEST(SubNode, Basic)
swver.minor = 1;
swver.vcs_commit = 0xDEADBEEF;
std::cout << "sizeof(uavcan::SubNode<0>): " << sizeof(uavcan::SubNode<0>) << std::endl;
/*
* uavcan::Node
*/
uavcan::Node<0> node1(nodes.can_a, nodes.clock_a);
uavcan::Node<1024> node1(nodes.can_a, nodes.clock_a);
node1.setName("com.example");
node1.setNodeID(1);
node1.setSoftwareVersion(swver);
@@ -42,8 +44,7 @@ TEST(SubNode, Basic)
/*
* uavcan::SubNode
*/
uavcan::SubNode<0> node2(nodes.can_b, nodes.clock_b);
std::cout << "sizeof(uavcan::SubNode<0>): " << sizeof(uavcan::SubNode<0>) << std::endl;
uavcan::SubNode<1024> node2(nodes.can_b, nodes.clock_b);
BackgroundSpinner bgspinner(node2, node1);
bgspinner.startPeriodic(uavcan::MonotonicDuration::fromMSec(10));
+12 -6
View File
@@ -10,6 +10,7 @@
#endif
#include <uavcan/node/abstract_node.hpp>
#include <uavcan/helpers/heap_based_pool_allocator.hpp>
#include <memory>
#include <set>
#include <queue>
@@ -19,15 +20,20 @@
struct TestNode : public uavcan::INode
{
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> pool;
uavcan::OutgoingTransferRegistry<8> otr;
/*
* This class used to use the simple pool allocator instead:
* uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 1024, uavcan::MemPoolBlockSize> pool;
* It has been replaced because unlike the simple allocator, heap-based one is not tested as extensively.
* Moreover, heap based allocator prints and error message upon destruction if some memory has not been freed.
*/
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize> pool;
uavcan::Scheduler scheduler;
uint64_t internal_failure_count;
TestNode(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock_driver, uavcan::NodeID self_node_id)
: otr(pool)
, scheduler(can_driver, pool, clock_driver, otr)
, internal_failure_count(0)
TestNode(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock_driver, uavcan::NodeID self_node_id) :
pool(1024),
scheduler(can_driver, pool, clock_driver),
internal_failure_count(0)
{
setNodeID(self_node_id);
}
@@ -190,6 +190,5 @@ TEST(dynamic_node_id_server, ObjectSizes)
std::cout << "ServiceServer<AppendEntries>: " << sizeof(ServiceServer<AppendEntries>) << std::endl;
std::cout << "ServiceClient<AppendEntries>: " << sizeof(ServiceClient<AppendEntries>) << std::endl;
std::cout << "ServiceServer<RequestVote>: " << sizeof(ServiceServer<RequestVote>) << std::endl;
std::cout << "ServiceClient<RequestVote,~,5>:"
<< sizeof(ServiceClient<RequestVote, void (*)(const ServiceCallResult<RequestVote>&), 5>) << std::endl;
std::cout << "ServiceClient<RequestVote>: " << sizeof(ServiceClient<RequestVote>) << std::endl;
}
@@ -281,7 +281,5 @@ TEST(dynamic_node_id_server_NodeDiscoverer, Sizes)
std::cout << "BitSet<NodeID::Max + 1>: " << sizeof(BitSet<NodeID::Max + 1>) << std::endl;
std::cout << "ServiceClient<protocol::GetNodeInfo>: " << sizeof(ServiceClient<protocol::GetNodeInfo>) << std::endl;
std::cout << "protocol::GetNodeInfo::Response: " << sizeof(protocol::GetNodeInfo::Response) << std::endl;
std::cout << "Subscriber<protocol::NodeStatus, ~, 64, 0>: "
<< sizeof(Subscriber<protocol::NodeStatus,
void (*)(const ReceivedDataStructure<protocol::NodeStatus>&), 64, 0>) << std::endl;
std::cout << "Subscriber<protocol::NodeStatus>: " << sizeof(Subscriber<protocol::NodeStatus>) << std::endl;
}
@@ -145,10 +145,10 @@ TEST(CanAcceptanceFilter, Basic_test)
std::cout << "config.MK [" << i << "]= " << configure_array.getByIndex(i)->mask << std::endl;
}
ASSERT_EQ(configure_array.getByIndex(0)->id, 0);
ASSERT_EQ(configure_array.getByIndex(0)->mask, 255);
ASSERT_EQ(configure_array.getByIndex(1)->id, 256000);
ASSERT_EQ(configure_array.getByIndex(1)->mask, 16771968);
ASSERT_EQ(configure_array.getByIndex(0)->id, 256000);
ASSERT_EQ(configure_array.getByIndex(0)->mask, 16771968);
ASSERT_EQ(configure_array.getByIndex(1)->id, 0);
ASSERT_EQ(configure_array.getByIndex(1)->mask, 255);
ASSERT_EQ(configure_array.getByIndex(2)->id, 6272);
ASSERT_EQ(configure_array.getByIndex(2)->mask, 32640);
ASSERT_EQ(configure_array.getByIndex(3)->id, 262144);
@@ -170,16 +170,16 @@ TEST(CanAcceptanceFilter, Basic_test)
for (uint16_t i = 0; i<configure_array_size; i++)
{
std::cout << "config.ID [" << i << "]= " << configure_array.getByIndex(i)->id << std::endl;
std::cout << "config.MK [" << i << "]= " << configure_array.getByIndex(i)->mask << std::endl;
std::cout << "config.ID [" << i << "]= " << configure_array_2.getByIndex(i)->id << std::endl;
std::cout << "config.MK [" << i << "]= " << configure_array_2.getByIndex(i)->mask << std::endl;
}
ASSERT_EQ(configure_array_2.getByIndex(0)->id, 6272);
ASSERT_EQ(configure_array_2.getByIndex(0)->mask, 32640);
ASSERT_EQ(configure_array_2.getByIndex(0)->id, 256000);
ASSERT_EQ(configure_array_2.getByIndex(0)->mask, 16771968);
ASSERT_EQ(configure_array_2.getByIndex(1)->id, 262144);
ASSERT_EQ(configure_array_2.getByIndex(1)->mask, 16776320);
ASSERT_EQ(configure_array_2.getByIndex(2)->id, 256000);
ASSERT_EQ(configure_array_2.getByIndex(2)->mask, 16771968);
ASSERT_EQ(configure_array_2.getByIndex(2)->id, 6272);
ASSERT_EQ(configure_array_2.getByIndex(2)->mask, 32640);
ASSERT_EQ(configure_array_2.getByIndex(3)->id, 262144);
ASSERT_EQ(configure_array_2.getByIndex(3)->mask, 16771968);
}
+21 -28
View File
@@ -52,14 +52,12 @@ static const uavcan::NodeID SELF_NODE_ID(64);
TEST(Dispatcher, Reception)
{
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> pool;
SystemClockMock clockmock(100);
CanDriverMock driver(2, clockmock);
uavcan::OutgoingTransferRegistry<8> out_trans_reg(pool);
uavcan::Dispatcher dispatcher(driver, pool, clockmock, out_trans_reg);
uavcan::Dispatcher dispatcher(driver, pool, clockmock);
ASSERT_TRUE(dispatcher.setNodeID(SELF_NODE_ID)); // Can be set only once
ASSERT_FALSE(dispatcher.setNodeID(SELF_NODE_ID));
ASSERT_EQ(SELF_NODE_ID, dispatcher.getNodeID());
@@ -86,17 +84,17 @@ TEST(Dispatcher, Reception)
makeDataType(uavcan::DataTypeKindService, 1)
};
typedef TestListener<512, 2, 2> Subscriber;
typedef std::auto_ptr<Subscriber> SubscriberPtr;
static const int NUM_SUBSCRIBERS = 6;
SubscriberPtr subscribers[NUM_SUBSCRIBERS] =
typedef std::auto_ptr<TestListener> TestListenerPtr;
static const int MaxBufSize = 512;
static const int NumSubscribers = 6;
TestListenerPtr subscribers[NumSubscribers] =
{
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[0], pool)), // msg
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[0], pool)), // msg // Two similar
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[1], pool)), // msg
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[2], pool)), // srv
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[3], pool)), // srv
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[3], pool)) // srv // Repeat again
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[0], MaxBufSize, pool)), // msg
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[0], MaxBufSize, pool)), // msg // Two similar
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[1], MaxBufSize, pool)), // msg
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[2], MaxBufSize, pool)), // srv
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[3], MaxBufSize, pool)), // srv
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[3], MaxBufSize, pool)) // srv // Repeat again
};
static const std::string DATA[6] =
@@ -139,7 +137,7 @@ TEST(Dispatcher, Reception)
/*
* Registration
*/
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
for (int i = 0; i < NumSubscribers; i++)
{
ASSERT_FALSE(dispatcher.hasSubscriber(subscribers[i]->getDataTypeDescriptor().getID()));
ASSERT_FALSE(dispatcher.hasPublisher(subscribers[i]->getDataTypeDescriptor().getID()));
@@ -153,7 +151,7 @@ TEST(Dispatcher, Reception)
ASSERT_TRUE(dispatcher.registerServiceResponseListener(subscribers[4].get()));
ASSERT_TRUE(dispatcher.registerServiceResponseListener(subscribers[5].get()));
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
for (int i = 0; i < NumSubscribers; i++)
{
ASSERT_FALSE(dispatcher.hasPublisher(subscribers[i]->getDataTypeDescriptor().getID()));
}
@@ -177,7 +175,7 @@ TEST(Dispatcher, Reception)
ASSERT_EQ(1, dispatcher.getNumServiceRequestListeners());
ASSERT_EQ(2, dispatcher.getNumServiceResponseListeners());
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
for (int i = 0; i < NumSubscribers; i++)
{
ASSERT_TRUE(subscribers[i]->isEmpty());
}
@@ -214,7 +212,7 @@ TEST(Dispatcher, Reception)
ASSERT_TRUE(subscribers[5]->matchAndPop(transfers[3]));
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
for (int i = 0; i < NumSubscribers; i++)
{
ASSERT_TRUE(subscribers[i]->isEmpty());
}
@@ -255,9 +253,7 @@ TEST(Dispatcher, Transmission)
SystemClockMock clockmock(100);
CanDriverMock driver(2, clockmock);
uavcan::OutgoingTransferRegistry<8> out_trans_reg(pool);
uavcan::Dispatcher dispatcher(driver, pool, clockmock, out_trans_reg);
uavcan::Dispatcher dispatcher(driver, pool, clockmock);
ASSERT_TRUE(dispatcher.setNodeID(SELF_NODE_ID)); // Can be set only once
ASSERT_FALSE(dispatcher.setNodeID(SELF_NODE_ID));
@@ -280,7 +276,8 @@ TEST(Dispatcher, Transmission)
ASSERT_FALSE(dispatcher.hasPublisher(123));
ASSERT_FALSE(dispatcher.hasPublisher(456));
const uavcan::OutgoingTransferRegistryKey otr_key(123, uavcan::TransferTypeMessageBroadcast, 0);
ASSERT_TRUE(out_trans_reg.accessOrCreate(otr_key, uavcan::MonotonicTime::fromMSec(1000000)));
ASSERT_TRUE(dispatcher.getOutgoingTransferRegistry().accessOrCreate(otr_key,
uavcan::MonotonicTime::fromMSec(1000000)));
ASSERT_TRUE(dispatcher.hasPublisher(123));
ASSERT_FALSE(dispatcher.hasPublisher(456));
@@ -319,9 +316,7 @@ TEST(Dispatcher, Spin)
SystemClockMock clockmock(100);
CanDriverMock driver(2, clockmock);
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg);
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock);
ASSERT_TRUE(dispatcher.setNodeID(SELF_NODE_ID)); // Can be set only once
ASSERT_FALSE(dispatcher.setNodeID(SELF_NODE_ID));
@@ -365,9 +360,7 @@ TEST(Dispatcher, Loopback)
SystemClockMock clockmock(100);
CanDriverMock driver(2, clockmock);
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg);
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock);
ASSERT_TRUE(dispatcher.setNodeID(SELF_NODE_ID));
{
@@ -72,8 +72,8 @@ TEST(MultiFrameIncomingTransfer, Basic)
using uavcan::RxFrame;
using uavcan::MultiFrameIncomingTransfer;
NullAllocator poolmgr; // We don't need dynamic memory
uavcan::TransferBufferManager<256, 1> bufmgr(poolmgr);
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> poolmgr;
uavcan::TransferBufferManager bufmgr(256, poolmgr);
const RxFrame frame = makeFrame();
uavcan::TransferBufferManagerKey bufmgr_key(frame.getSrcNodeID(), frame.getTransferType());
@@ -12,8 +12,8 @@
TEST(OutgoingTransferRegistry, Basic)
{
using uavcan::OutgoingTransferRegistryKey;
NullAllocator poolmgr; // Empty
uavcan::OutgoingTransferRegistry<4> otr(poolmgr);
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 2, uavcan::MemPoolBlockSize> poolmgr;
uavcan::OutgoingTransferRegistry otr(poolmgr);
otr.cleanup(tsMono(1000));
+15 -43
View File
@@ -83,8 +83,8 @@ static const int TEST_BUFFER_SIZE = 200;
TEST(StaticTransferBuffer, Basic)
{
using uavcan::StaticTransferBufferManagerEntry;
StaticTransferBufferManagerEntry<TEST_BUFFER_SIZE> buf;
using uavcan::StaticTransferBuffer;
StaticTransferBuffer<TEST_BUFFER_SIZE> buf;
uint8_t local_buffer[TEST_BUFFER_SIZE * 2];
const uint8_t* const test_data_ptr = reinterpret_cast<const uint8_t*>(TEST_DATA.c_str());
@@ -126,15 +126,15 @@ TEST(StaticTransferBuffer, Basic)
}
TEST(DynamicTransferBufferManagerEntry, Basic)
TEST(TransferBufferManagerEntry, Basic)
{
using uavcan::DynamicTransferBufferManagerEntry;
using uavcan::TransferBufferManagerEntry;
static const int MAX_SIZE = TEST_BUFFER_SIZE;
static const int POOL_BLOCKS = 8;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
DynamicTransferBufferManagerEntry buf(pool, MAX_SIZE);
TransferBufferManagerEntry buf(pool, MAX_SIZE);
uint8_t local_buffer[TEST_BUFFER_SIZE * 2];
const uint8_t* const test_data_ptr = reinterpret_cast<const uint8_t*>(TEST_DATA.c_str());
@@ -186,7 +186,7 @@ TEST(DynamicTransferBufferManagerEntry, Basic)
// Destroying the object; memory should be released
ASSERT_LT(0, pool.getNumUsedBlocks());
buf.~DynamicTransferBufferManagerEntry();
buf.~TransferBufferManagerEntry();
ASSERT_EQ(0, pool.getNumUsedBlocks());
}
@@ -229,11 +229,10 @@ TEST(TransferBufferManager, Basic)
using uavcan::TransferBufferManagerKey;
using uavcan::ITransferBuffer;
static const int POOL_BLOCKS = 6;
static const int POOL_BLOCKS = 100;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
typedef TransferBufferManager<MGR_MAX_BUFFER_SIZE, 2> TransferBufferManagerType;
std::auto_ptr<TransferBufferManagerType> mgr(new TransferBufferManagerType(pool));
std::auto_ptr<TransferBufferManager> mgr(new TransferBufferManager(MGR_MAX_BUFFER_SIZE, pool));
// Empty
ASSERT_FALSE(mgr->access(TransferBufferManagerKey(0, uavcan::TransferTypeMessageBroadcast)));
@@ -250,40 +249,25 @@ TEST(TransferBufferManager, Basic)
TransferBufferManagerKey(64, uavcan::TransferTypeMessageBroadcast)
};
// Static 0
ASSERT_TRUE((tbb = mgr->create(keys[0])));
ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[0], tbb));
ASSERT_EQ(1, mgr->getNumStaticBuffers());
ASSERT_EQ(1, mgr->getNumBuffers());
// Static 1
ASSERT_TRUE((tbb = mgr->create(keys[1])));
ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[1], tbb));
ASSERT_EQ(2, mgr->getNumStaticBuffers());
ASSERT_EQ(0, mgr->getNumDynamicBuffers());
ASSERT_EQ(0, pool.getNumUsedBlocks());
ASSERT_EQ(2, mgr->getNumBuffers());
ASSERT_LT(2, pool.getNumUsedBlocks());
// Dynamic 0
ASSERT_TRUE((tbb = mgr->create(keys[2])));
ASSERT_EQ(1, pool.getNumUsedBlocks()); // Empty dynamic buffer occupies one block
ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[2], tbb));
ASSERT_EQ(2, mgr->getNumStaticBuffers());
ASSERT_EQ(1, mgr->getNumDynamicBuffers());
ASSERT_LT(1, pool.getNumUsedBlocks());
ASSERT_EQ(3, mgr->getNumBuffers());
std::cout << "TransferBufferManager - Basic: Pool usage: " << pool.getNumUsedBlocks() << std::endl;
// Dynamic 2
ASSERT_TRUE((tbb = mgr->create(keys[3])));
ASSERT_LT(0, pool.getNumUsedBlocks());
ASSERT_LT(0, fillTestData(MGR_TEST_DATA[3], tbb));
ASSERT_EQ(2, mgr->getNumStaticBuffers());
ASSERT_EQ(2, mgr->getNumDynamicBuffers());
// Dynamic 3 - will fail due to OOM
ASSERT_FALSE((tbb = mgr->create(keys[4])));
ASSERT_EQ(2, mgr->getNumStaticBuffers());
ASSERT_EQ(2, mgr->getNumDynamicBuffers());
ASSERT_EQ(4, mgr->getNumBuffers());
// Making sure all buffers contain proper data
ASSERT_TRUE((tbb = mgr->access(keys[0])));
@@ -298,18 +282,14 @@ TEST(TransferBufferManager, Basic)
ASSERT_TRUE((tbb = mgr->access(keys[3])));
ASSERT_TRUE(matchAgainst(MGR_TEST_DATA[3], *tbb));
// Freeing one static buffer; one dynamic must migrate
mgr->remove(keys[1]);
ASSERT_FALSE(mgr->access(keys[1]));
ASSERT_EQ(2, mgr->getNumStaticBuffers());
ASSERT_EQ(1, mgr->getNumDynamicBuffers()); // One migrated to the static
ASSERT_EQ(3, mgr->getNumBuffers());
ASSERT_LT(0, pool.getNumFreeBlocks());
// Removing NodeID 0; one dynamic must migrate
mgr->remove(keys[0]);
ASSERT_FALSE(mgr->access(keys[0]));
ASSERT_EQ(2, mgr->getNumStaticBuffers());
ASSERT_EQ(0, mgr->getNumDynamicBuffers());
ASSERT_EQ(2, mgr->getNumBuffers());
// At this time we have the following NodeID: 2, 127
ASSERT_TRUE((tbb = mgr->access(keys[2])));
@@ -332,11 +312,3 @@ TEST(TransferBufferManager, Basic)
mgr.reset();
ASSERT_EQ(0, pool.getNumUsedBlocks());
}
TEST(TransferBufferManager, EmptySpecialization)
{
uavcan::TransferBufferManager<0, 0> mgr;
(void)mgr;
ASSERT_GE(sizeof(void*), sizeof(mgr));
}
+20 -17
View File
@@ -9,11 +9,11 @@
class TransferListenerEmulator : public IncomingTransferEmulatorBase
{
uavcan::TransferListenerBase& target_;
uavcan::TransferListener& target_;
const uavcan::DataTypeDescriptor data_type_;
public:
TransferListenerEmulator(uavcan::TransferListenerBase& target, const uavcan::DataTypeDescriptor& type,
TransferListenerEmulator(uavcan::TransferListener& target, const uavcan::DataTypeDescriptor& type,
uavcan::NodeID dst_node_id = 127)
: IncomingTransferEmulatorBase(dst_node_id)
, target_(target)
@@ -34,11 +34,11 @@ TEST(TransferListener, BasicMFT)
{
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
static const int NUM_POOL_BLOCKS = 12; // This number is just enough to pass the test
static const int NUM_POOL_BLOCKS = 100;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
uavcan::TransferPerfCounter perf;
TestListener<256, 1, 1> subscriber(perf, type, pool);
TestListener subscriber(perf, type, 256, pool);
/*
* Test data
@@ -93,9 +93,10 @@ TEST(TransferListener, CrcFailure)
{
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
NullAllocator poolmgr; // No dynamic memory
static const int NUM_POOL_BLOCKS = 100;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> poolmgr;
uavcan::TransferPerfCounter perf;
TestListener<256, 2, 2> subscriber(perf, type, poolmgr); // Static buffer only, 2 entries
TestListener subscriber(perf, type, 256, poolmgr); // Static buffer only, 2 entries
/*
* Generating transfers with damaged payload (CRC is not valid)
@@ -136,9 +137,10 @@ TEST(TransferListener, BasicSFT)
{
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
NullAllocator poolmgr; // No dynamic memory. At all.
static const int NUM_POOL_BLOCKS = 100;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> poolmgr;
uavcan::TransferPerfCounter perf;
TestListener<0, 0, 5> subscriber(perf, type, poolmgr); // Max buf size is 0, i.e. SFT-only
TestListener subscriber(perf, type, 0, poolmgr); // Max buf size is 0, i.e. SFT-only
TransferListenerEmulator emulator(subscriber, type);
const Transfer transfers[] =
@@ -148,7 +150,6 @@ TEST(TransferListener, BasicSFT)
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 2, ""),
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 3, "abc"),
emulator.makeTransfer(16, uavcan::TransferTypeServiceResponse, 4, ""),
emulator.makeTransfer(16, uavcan::TransferTypeServiceResponse, 2, ""), // New TT, ignored due to OOM
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 2, "foo"), // Same as 2, not ignored
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 2, "123456789abc"), // Same as 2, not SFT - ignore
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 2, "bar"), // Same as 2, not ignored
@@ -161,8 +162,8 @@ TEST(TransferListener, BasicSFT)
ASSERT_TRUE(subscriber.matchAndPop(transfers[2]));
ASSERT_TRUE(subscriber.matchAndPop(transfers[3]));
ASSERT_TRUE(subscriber.matchAndPop(transfers[4]));
ASSERT_TRUE(subscriber.matchAndPop(transfers[6]));
ASSERT_TRUE(subscriber.matchAndPop(transfers[8]));
ASSERT_TRUE(subscriber.matchAndPop(transfers[5]));
ASSERT_TRUE(subscriber.matchAndPop(transfers[7]));
ASSERT_TRUE(subscriber.isEmpty());
}
@@ -172,9 +173,10 @@ TEST(TransferListener, Cleanup)
{
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
NullAllocator poolmgr; // No dynamic memory
static const int NUM_POOL_BLOCKS = 100;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> poolmgr;
uavcan::TransferPerfCounter perf;
TestListener<256, 1, 2> subscriber(perf, type, poolmgr); // Static buffer only, 1 entry
TestListener subscriber(perf, type, 256, poolmgr);
/*
* Generating transfers
@@ -206,7 +208,7 @@ TEST(TransferListener, Cleanup)
/*
* Cleanup with huge timestamp value will remove all entries
*/
static_cast<uavcan::TransferListenerBase&>(subscriber).cleanup(tsMono(100000000));
static_cast<uavcan::TransferListener&>(subscriber).cleanup(tsMono(100000000));
/*
* Sending the same transfers again - they will be accepted since registres were cleared
@@ -227,9 +229,10 @@ TEST(TransferListener, AnonymousTransfers)
{
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
NullAllocator poolmgr;
static const int NUM_POOL_BLOCKS = 100;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> poolmgr;
uavcan::TransferPerfCounter perf;
TestListener<0, 0, 0> subscriber(perf, type, poolmgr);
TestListener subscriber(perf, type, 0, poolmgr);
TransferListenerEmulator emulator(subscriber, type);
const Transfer transfers[] =
@@ -261,5 +264,5 @@ TEST(TransferListener, Sizes)
{
using namespace uavcan;
std::cout << "sizeof(TransferListener<64, 1, 2>): " << sizeof(TransferListener<64, 1, 2>) << std::endl;
std::cout << "sizeof(TransferListener): " << sizeof(TransferListener) << std::endl;
}
+12 -14
View File
@@ -74,15 +74,13 @@ const uavcan::TransferBufferManagerKey RxFrameGenerator::DEFAULT_KEY(42, uavcan:
template <unsigned BufSize>
struct Context
{
NullAllocator pool; // We don't need dynamic memory for this test
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> pool;
uavcan::TransferReceiver receiver; // Must be default constructible and copyable
uavcan::TransferBufferManager<BufSize, 1> bufmgr;
uavcan::TransferBufferManager bufmgr;
Context() :
bufmgr(pool)
{
assert(pool.allocate(1) == NULL);
}
bufmgr(BufSize, pool)
{ }
~Context()
{
@@ -124,7 +122,7 @@ TEST(TransferReceiver, Basic)
Context<32> context;
RxFrameGenerator gen(789);
uavcan::TransferReceiver& rcv = context.receiver;
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
std::cout << "sizeof(TransferReceiver): " << sizeof(TransferReceiver) << std::endl;
@@ -263,7 +261,7 @@ TEST(TransferReceiver, OutOfBufferSpace_32bytes)
Context<32> context;
RxFrameGenerator gen(789);
uavcan::TransferReceiver& rcv = context.receiver;
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
/*
@@ -301,7 +299,7 @@ TEST(TransferReceiver, OutOfOrderFrames)
Context<32> context;
RxFrameGenerator gen(789);
uavcan::TransferReceiver& rcv = context.receiver;
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "1234567", SET100, 7, 100000000), bk));
@@ -325,7 +323,7 @@ TEST(TransferReceiver, IntervalMeasurement)
Context<32> context;
RxFrameGenerator gen(789);
uavcan::TransferReceiver& rcv = context.receiver;
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
static const int INTERVAL = 1000;
@@ -355,7 +353,7 @@ TEST(TransferReceiver, Restart)
Context<32> context;
RxFrameGenerator gen(789);
uavcan::TransferReceiver& rcv = context.receiver;
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
/*
@@ -403,7 +401,7 @@ TEST(TransferReceiver, UtcTransferTimestamping)
Context<32> context;
RxFrameGenerator gen(789);
uavcan::TransferReceiver& rcv = context.receiver;
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
/*
@@ -462,7 +460,7 @@ TEST(TransferReceiver, HeaderParsing)
Context<32> context;
RxFrameGenerator gen(123);
uavcan::TransferReceiver& rcv = context.receiver;
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
/*
* MFT, message broadcasting
@@ -509,7 +507,7 @@ TEST(TransferReceiver, HeaderParsing)
Context<32> context;
RxFrameGenerator gen(123);
uavcan::TransferReceiver& rcv = context.receiver;
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
static const uavcan::TransferType ADDRESSED_TRANSFER_TYPES[2] =
{
+10 -16
View File
@@ -29,17 +29,15 @@ static int sendOne(uavcan::TransferSender& sender, const std::string& data,
TEST(TransferSender, Basic)
{
NullAllocator poolmgr;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> poolmgr;
SystemClockMock clockmock(100);
CanDriverMock driver(2, clockmock);
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
static const uavcan::NodeID TX_NODE_ID(64);
static const uavcan::NodeID RX_NODE_ID(65);
uavcan::Dispatcher dispatcher_tx(driver, poolmgr, clockmock, out_trans_reg);
uavcan::Dispatcher dispatcher_rx(driver, poolmgr, clockmock, out_trans_reg);
uavcan::Dispatcher dispatcher_tx(driver, poolmgr, clockmock);
uavcan::Dispatcher dispatcher_rx(driver, poolmgr, clockmock);
ASSERT_TRUE(dispatcher_tx.setNodeID(TX_NODE_ID));
ASSERT_TRUE(dispatcher_rx.setNodeID(RX_NODE_ID));
@@ -123,9 +121,9 @@ TEST(TransferSender, Basic)
}
}
TestListener<512, 2, 2> sub_msg(dispatcher_rx.getTransferPerfCounter(), TYPES[0], poolmgr);
TestListener<512, 2, 2> sub_srv_req(dispatcher_rx.getTransferPerfCounter(), TYPES[1], poolmgr);
TestListener<512, 2, 2> sub_srv_resp(dispatcher_rx.getTransferPerfCounter(), TYPES[1], poolmgr);
TestListener sub_msg(dispatcher_rx.getTransferPerfCounter(), TYPES[0], 512, poolmgr);
TestListener sub_srv_req(dispatcher_rx.getTransferPerfCounter(), TYPES[1], 512, poolmgr);
TestListener sub_srv_resp(dispatcher_rx.getTransferPerfCounter(), TYPES[1], 512, poolmgr);
dispatcher_rx.registerMessageListener(&sub_msg);
dispatcher_rx.registerServiceRequestListener(&sub_srv_req);
@@ -190,15 +188,13 @@ struct TransferSenderTestLoopbackFrameListener : public uavcan::LoopbackFrameLis
TEST(TransferSender, Loopback)
{
NullAllocator poolmgr;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> poolmgr;
SystemClockMock clockmock(100);
CanDriverMock driver(2, clockmock);
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
static const uavcan::NodeID TX_NODE_ID(64);
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg);
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock);
ASSERT_TRUE(dispatcher.setNodeID(TX_NODE_ID));
uavcan::DataTypeDescriptor desc = makeDataType(uavcan::DataTypeKindMessage, 1, "Foobar");
@@ -231,14 +227,12 @@ TEST(TransferSender, Loopback)
TEST(TransferSender, PassiveMode)
{
NullAllocator poolmgr;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> poolmgr;
SystemClockMock clockmock(100);
CanDriverMock driver(2, clockmock);
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg);
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock);
uavcan::TransferSender sender(dispatcher, makeDataType(uavcan::DataTypeKindMessage, 123),
uavcan::CanTxQueue::Volatile);
@@ -11,7 +11,7 @@ TEST(TransferTestHelpers, Transfer)
{
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
uavcan::TransferBufferManager<128, 1> mgr(pool);
uavcan::TransferBufferManager mgr(128, pool);
uavcan::TransferBufferAccessor tba(mgr, uavcan::TransferBufferManagerKey(0, uavcan::TransferTypeMessageBroadcast));
uavcan::RxFrame frame(uavcan::Frame(123, uavcan::TransferTypeMessageBroadcast, 1, 0, 0),
@@ -117,17 +117,16 @@ struct Transfer
* In reality, uavcan::TransferListener should accept only specific transfer types
* which are dispatched/filtered by uavcan::Dispatcher.
*/
template <unsigned MAX_BUF_SIZE, unsigned NUM_STATIC_BUFS, unsigned NUM_STATIC_RECEIVERS>
class TestListener : public uavcan::TransferListener<MAX_BUF_SIZE, NUM_STATIC_BUFS, NUM_STATIC_RECEIVERS>
class TestListener : public uavcan::TransferListener
{
typedef uavcan::TransferListener<MAX_BUF_SIZE, NUM_STATIC_BUFS, NUM_STATIC_RECEIVERS> Base;
typedef uavcan::TransferListener Base;
std::queue<Transfer> transfers_;
public:
TestListener(uavcan::TransferPerfCounter& perf, const uavcan::DataTypeDescriptor& data_type,
uavcan::IPoolAllocator& allocator)
: Base(perf, data_type, allocator)
uavcan::uint16_t max_buffer_size, uavcan::IPoolAllocator& allocator)
: Base(perf, data_type, max_buffer_size, allocator)
{ }
void handleIncomingTransfer(uavcan::IncomingTransfer& transfer)
@@ -166,7 +165,7 @@ public:
return res;
}
int getNumReceivedTransfers() const { return transfers_.size(); }
unsigned getNumReceivedTransfers() const { return static_cast<unsigned>(transfers_.size()); }
bool isEmpty() const { return transfers_.empty(); }
};
@@ -317,5 +316,5 @@ class NullAllocator : public uavcan::IPoolAllocator
public:
virtual void* allocate(std::size_t) { return NULL; }
virtual void deallocate(const void*) { }
virtual uint16_t getNumBlocks() const { return 0; }
virtual uint16_t getBlockCapacity() const { return 0; }
};
+20 -69
View File
@@ -2,6 +2,11 @@
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#if __GNUC__
// We need auto_ptr for compatibility reasons
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include <string>
#include <cstdio>
#include <memory>
@@ -46,7 +51,7 @@ TEST(Map, Basic)
static const int POOL_BLOCKS = 3;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
typedef Map<std::string, std::string, 2> MapType;
typedef Map<std::string, std::string> MapType;
std::auto_ptr<MapType> map(new MapType(pool));
// Empty
@@ -57,25 +62,23 @@ TEST(Map, Basic)
ASSERT_FALSE(map->getByIndex(1));
ASSERT_FALSE(map->getByIndex(10000));
// Static insertion
// Insertion
ASSERT_EQ("a", *map->insert("1", "a"));
ASSERT_EQ("b", *map->insert("2", "b"));
ASSERT_EQ(0, pool.getNumUsedBlocks());
ASSERT_EQ(2, map->getNumStaticPairs());
ASSERT_EQ(0, map->getNumDynamicPairs());
ASSERT_EQ(1, pool.getNumUsedBlocks());
ASSERT_EQ(2, map->getSize());
// Ordering
ASSERT_TRUE(map->getByIndex(0)->match("1"));
ASSERT_TRUE(map->getByIndex(1)->match("2"));
// Dynamic insertion
// Insertion
ASSERT_EQ("c", *map->insert("3", "c"));
ASSERT_EQ(1, pool.getNumUsedBlocks());
ASSERT_EQ("d", *map->insert("4", "d"));
ASSERT_EQ(1, pool.getNumUsedBlocks()); // Assuming that at least 2 items fit one block
ASSERT_EQ(2, map->getNumStaticPairs());
ASSERT_EQ(2, map->getNumDynamicPairs());
ASSERT_EQ(2, pool.getNumUsedBlocks()); // Assuming that at least 2 items fit one block
ASSERT_EQ(4, map->getSize());
// Making sure everything is here
ASSERT_EQ("a", *map->access("1"));
@@ -84,14 +87,6 @@ TEST(Map, Basic)
ASSERT_EQ("d", *map->access("4"));
ASSERT_FALSE(map->access("hi"));
// Ordering
ASSERT_TRUE(map->getByIndex(0)->match("1"));
ASSERT_TRUE(map->getByIndex(1)->match("2"));
ASSERT_TRUE(map->getByIndex(2)->match("3"));
ASSERT_TRUE(map->getByIndex(3)->match("4"));
ASSERT_FALSE(map->getByIndex(4));
ASSERT_FALSE(map->getByIndex(1000));
// Modifying existing entries
*map->access("1") = "A";
*map->access("2") = "B";
@@ -116,35 +111,28 @@ TEST(Map, Basic)
ASSERT_EQ("4", *map->find(ValueFindPredicate("D")));
ASSERT_FALSE(map->find(KeyFindPredicate("nonexistent_value")));
// Removing one static
// Removing one
map->remove("1"); // One of dynamics now migrates to the static storage
map->remove("foo"); // There's no such thing anyway
ASSERT_EQ(1, pool.getNumUsedBlocks());
ASSERT_EQ(2, map->getNumStaticPairs());
ASSERT_EQ(1, map->getNumDynamicPairs());
ASSERT_EQ(2, pool.getNumUsedBlocks());
ASSERT_EQ(3, map->getSize());
ASSERT_FALSE(map->access("1"));
ASSERT_EQ("B", *map->access("2"));
ASSERT_EQ("C", *map->access("3"));
ASSERT_EQ("D", *map->access("4"));
// Ordering has not changed - first dynamic entry has moved to the first static slot
ASSERT_TRUE(map->getByIndex(0)->match("3"));
ASSERT_TRUE(map->getByIndex(1)->match("2"));
ASSERT_TRUE(map->getByIndex(2)->match("4"));
// Removing another static
// Removing another
map->remove("2");
ASSERT_EQ(2, map->getNumStaticPairs());
ASSERT_EQ(0, map->getNumDynamicPairs());
ASSERT_EQ(0, pool.getNumUsedBlocks()); // No dynamic entries left
ASSERT_EQ(2, map->getSize());
ASSERT_EQ(2, pool.getNumUsedBlocks());
ASSERT_FALSE(map->access("1"));
ASSERT_FALSE(map->access("2"));
ASSERT_EQ("C", *map->access("3"));
ASSERT_EQ("D", *map->access("4"));
// Adding some new dynamics
// Adding some new
unsigned max_key_integer = 0;
for (int i = 0; i < 100; i++)
{
@@ -172,13 +160,7 @@ TEST(Map, Basic)
ASSERT_FALSE(map->access("value"));
// Removing odd values - nearly half of them
ASSERT_EQ(2, map->getNumStaticPairs());
const unsigned num_dynamics_old = map->getNumDynamicPairs();
map->removeAllWhere(oddValuePredicate);
ASSERT_EQ(2, map->getNumStaticPairs());
const unsigned num_dynamics_new = map->getNumDynamicPairs();
std::cout << "Num of dynamic pairs reduced from " << num_dynamics_old << " to " << num_dynamics_new << std::endl;
ASSERT_LT(num_dynamics_new, num_dynamics_old);
// Making sure there's no odd values left
for (unsigned kv_int = 0; kv_int <= max_key_integer; kv_int++)
@@ -200,37 +182,6 @@ TEST(Map, Basic)
}
TEST(Map, NoStatic)
{
using uavcan::Map;
static const int POOL_BLOCKS = 3;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
typedef Map<std::string, std::string> MapType;
std::auto_ptr<MapType> map(new MapType(pool));
// Empty
ASSERT_FALSE(map->access("hi"));
map->remove("foo");
ASSERT_EQ(0, pool.getNumUsedBlocks());
ASSERT_FALSE(map->getByIndex(0));
// Insertion
ASSERT_EQ("a", *map->insert("1", "a"));
ASSERT_EQ("b", *map->insert("2", "b"));
ASSERT_EQ(1, pool.getNumUsedBlocks());
ASSERT_EQ(0, map->getNumStaticPairs());
ASSERT_EQ(2, map->getNumDynamicPairs());
// Ordering
ASSERT_TRUE(map->getByIndex(0)->match("1"));
ASSERT_TRUE(map->getByIndex(1)->match("2"));
ASSERT_FALSE(map->getByIndex(3));
ASSERT_FALSE(map->getByIndex(1000));
}
TEST(Map, PrimitiveKey)
{
using uavcan::Map;
@@ -238,7 +189,7 @@ TEST(Map, PrimitiveKey)
static const int POOL_BLOCKS = 3;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
typedef Map<short, short, 2> MapType;
typedef Map<short, short> MapType;
std::auto_ptr<MapType> map(new MapType(pool));
// Empty
+8 -42
View File
@@ -71,7 +71,7 @@ TEST(Multiset, Basic)
static const int POOL_BLOCKS = 3;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
typedef Multiset<std::string, 2> MultisetType;
typedef Multiset<std::string> MultisetType;
std::auto_ptr<MultisetType> mset(new MultisetType(pool));
typedef SummationOperator<std::string> StringConcatenationOperator;
@@ -86,9 +86,8 @@ TEST(Multiset, Basic)
// Static addion
ASSERT_EQ("1", *mset->emplace("1"));
ASSERT_EQ("2", *mset->emplace("2"));
ASSERT_EQ(0, pool.getNumUsedBlocks());
ASSERT_EQ(2, mset->getNumStaticItems());
ASSERT_EQ(0, mset->getNumDynamicItems());
ASSERT_LE(1, pool.getNumUsedBlocks()); // One or more
ASSERT_EQ(2, mset->getSize());
// Ordering
ASSERT_TRUE(*mset->getByIndex(0) == "1");
@@ -103,23 +102,15 @@ TEST(Multiset, Basic)
// Dynamic addition
ASSERT_EQ("3", *mset->emplace("3"));
ASSERT_EQ("3", *mset->getByIndex(2));
ASSERT_EQ(1, pool.getNumUsedBlocks());
ASSERT_LE(1, pool.getNumUsedBlocks()); // One or more
ASSERT_EQ("4", *mset->emplace("4"));
ASSERT_LE(1, pool.getNumUsedBlocks()); // One or more
ASSERT_EQ(2, mset->getNumStaticItems());
ASSERT_EQ(2, mset->getNumDynamicItems());
ASSERT_EQ(4, mset->getSize());
// Making sure everything is here
ASSERT_EQ("1", *mset->getByIndex(0));
ASSERT_EQ("2", *mset->getByIndex(1));
// 2 and 3 are not tested because their placement depends on number of items per dynamic block
ASSERT_FALSE(mset->getByIndex(100));
ASSERT_FALSE(mset->getByIndex(4));
const std::string data_at_pos2 = *mset->getByIndex(2);
const std::string data_at_pos3 = *mset->getByIndex(3);
// Finding some items
ASSERT_EQ("1", *mset->find(FindPredicate("1")));
ASSERT_EQ("2", *mset->find(FindPredicate("2")));
@@ -134,23 +125,10 @@ TEST(Multiset, Basic)
ASSERT_EQ(4, op.accumulator.size());
}
// Removing one static; ordering will be preserved
// Removing some
mset->removeFirst("1");
mset->removeFirst("foo"); // There's no such thing anyway
ASSERT_LE(1, pool.getNumUsedBlocks());
ASSERT_EQ(1, mset->getNumStaticItems());
ASSERT_EQ(2, mset->getNumDynamicItems()); // This container does not move items
// Ordering has not changed
ASSERT_EQ("2", *mset->getByIndex(0)); // Entry "1" was here
ASSERT_EQ(data_at_pos2, *mset->getByIndex(1));
ASSERT_EQ(data_at_pos3, *mset->getByIndex(2));
// Removing another static
mset->removeFirst("2");
ASSERT_EQ(0, mset->getNumStaticItems());
ASSERT_EQ(2, mset->getNumDynamicItems());
ASSERT_LE(1, pool.getNumUsedBlocks());
// Adding some new items
unsigned max_value_integer = 0;
@@ -219,7 +197,7 @@ TEST(Multiset, PrimitiveKey)
static const int POOL_BLOCKS = 3;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
typedef Multiset<int, 2> MultisetType;
typedef Multiset<int> MultisetType;
std::auto_ptr<MultisetType> mset(new MultisetType(pool));
// Empty
@@ -237,16 +215,6 @@ TEST(Multiset, PrimitiveKey)
ASSERT_EQ(4, *mset->emplace(4));
ASSERT_EQ(4, mset->getSize());
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
// Only C++11 because C++03 uses one entry per pool block which breaks ordering
ASSERT_EQ(1, *mset->getByIndex(0));
ASSERT_EQ(2, *mset->getByIndex(1));
ASSERT_EQ(3, *mset->getByIndex(2));
ASSERT_EQ(4, *mset->getByIndex(3));
ASSERT_FALSE(mset->getByIndex(5));
ASSERT_FALSE(mset->getByIndex(1000));
#endif
// Summation and clearing
{
SummationOperator<int> summation_operator;
@@ -269,7 +237,7 @@ TEST(Multiset, NoncopyableWithCounter)
static const int POOL_BLOCKS = 3;
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
typedef Multiset<NoncopyableWithCounter, 2> MultisetType;
typedef Multiset<NoncopyableWithCounter> MultisetType;
std::auto_ptr<MultisetType> mset(new MultisetType(pool));
ASSERT_EQ(0, NoncopyableWithCounter::num_objects);
@@ -286,11 +254,9 @@ TEST(Multiset, NoncopyableWithCounter)
mset->removeFirst(NoncopyableWithCounter(0));
ASSERT_EQ(4, NoncopyableWithCounter::num_objects);
ASSERT_EQ(123, mset->getByIndex(0)->value);
mset->removeFirstWhere(&NoncopyableWithCounter::isNegative);
ASSERT_EQ(3, NoncopyableWithCounter::num_objects);
ASSERT_EQ(456, mset->getByIndex(1)->value); // -456 is now removed
mset->removeAllWhere(&NoncopyableWithCounter::isNegative);
ASSERT_EQ(2, NoncopyableWithCounter::num_objects); // Only 1 and 2 are left
@@ -373,11 +373,11 @@ public:
{
assert(num_ifaces_ > 0 && num_ifaces_ <= uavcan::MaxCanIfaces);
const unsigned quota_per_iface = allocator_.getNumBlocks() / num_ifaces_;
const unsigned quota_per_iface = allocator_.getBlockCapacity() / num_ifaces_;
const unsigned quota_per_queue = quota_per_iface; // 2x overcommit
UAVCAN_TRACE("VirtualCanDriver", "Total blocks: %u, quota per queue: %u",
unsigned(allocator_.getNumBlocks()), unsigned(quota_per_queue));
unsigned(allocator_.getBlockCapacity()), unsigned(quota_per_queue));
for (unsigned i = 0; i < num_ifaces_; i++)
{