First stab at global refactoring of memory management - the library builds, but unit tests are failing horribly

This commit is contained in:
Pavel Kirienko
2015-10-14 08:29:50 +03:00
parent 0643879922
commit be84897ed6
33 changed files with 203 additions and 756 deletions
-18
View File
@@ -239,24 +239,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
@@ -119,25 +119,16 @@ public:
* 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
template<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;
typedef TransferListenerTemplate<BufferSize> Type;
};
/**
+4 -19
View File
@@ -35,22 +35,8 @@ namespace uavcan
* 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.
*/
template <std::size_t MemPoolSize_,
#if UAVCAN_TINY
unsigned OutgoingTransferRegistryStaticEntries = 0
#else
unsigned OutgoingTransferRegistryStaticEntries = 10
#endif
>
template <std::size_t MemPoolSize_>
class UAVCAN_EXPORT Node : public INode
{
enum
@@ -61,7 +47,7 @@ class UAVCAN_EXPORT Node : public INode
typedef PoolAllocator<MemPoolSize, MemPoolBlockSize> Allocator;
Allocator pool_allocator_;
OutgoingTransferRegistry<OutgoingTransferRegistryStaticEntries> outgoing_trans_reg_;
OutgoingTransferRegistry outgoing_trans_reg_;
Scheduler scheduler_;
NodeStatusProvider proto_nsp_;
@@ -260,9 +246,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_)
{
@@ -21,13 +21,11 @@
namespace uavcan
{
template <typename ServiceDataType, unsigned NumStaticReceiversAndBuffers>
template <typename ServiceDataType>
class UAVCAN_EXPORT ServiceResponseTransferListenerInstantiationHelper
{
public: // so much templating it hurts
public:
typedef typename TransferListenerInstantiationHelper<typename ServiceDataType::Response,
NumStaticReceiversAndBuffers,
NumStaticReceiversAndBuffers,
TransferListenerWithFilter>::Type Type;
};
@@ -217,24 +215,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 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 ServiceResponseTransferListenerInstantiationHelper<DataType_>::Type>
, public ServiceClientBase
{
public:
@@ -244,16 +236,13 @@ 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 typename ServiceResponseTransferListenerInstantiationHelper<DataType>::Type TransferListenerType;
typedef GenericSubscriber<DataType, ResponseType, TransferListenerType> SubscriberType;
typedef Multiset<CallState, NumStaticCalls> CallRegistry;
typedef Multiset<CallState> CallRegistry;
struct TimeoutCallbackCaller
{
@@ -424,8 +413,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 +426,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 +436,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 +448,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 +472,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 +495,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))
{
@@ -573,8 +561,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 +571,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,19 @@ 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>
typename TransferListenerInstantiationHelper<typename DataType_::Request>::Type>
{
public:
typedef DataType_ DataType;
@@ -112,8 +98,7 @@ public:
typedef Callback_ Callback;
private:
typedef typename TransferListenerInstantiationHelper<RequestType, NumStaticReceivers, NumStaticBufs>::Type
TransferListenerType;
typedef typename TransferListenerInstantiationHelper<RequestType>::Type TransferListenerType;
typedef GenericSubscriber<DataType, RequestType, TransferListenerType> SubscriberType;
typedef GenericPublisher<DataType, ResponseType> PublisherType;
+2 -4
View File
@@ -20,9 +20,7 @@ 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_>
class UAVCAN_EXPORT SubNode : public INode
{
enum
@@ -33,7 +31,7 @@ class UAVCAN_EXPORT SubNode : public INode
typedef PoolAllocator<MemPoolSize, MemPoolBlockSize> Allocator;
Allocator pool_allocator_;
OutgoingTransferRegistry<OutgoingTransferRegistryStaticEntries> outgoing_trans_reg_;
OutgoingTransferRegistry outgoing_trans_reg_;
Scheduler scheduler_;
uint64_t internal_failure_cnt_;
+4 -19
View File
@@ -34,38 +34,23 @@ 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>
typename TransferListenerInstantiationHelper<DataType_>::Type>
{
public:
typedef Callback_ Callback;
private:
typedef typename TransferListenerInstantiationHelper<DataType_, NumStaticReceivers, NumStaticBufs>::Type
TransferListenerType;
typedef typename TransferListenerInstantiationHelper<DataType_>::Type TransferListenerType;
typedef GenericSubscriber<DataType_, DataType_, TransferListenerType> 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_);
@@ -60,6 +60,8 @@ public:
* Outgoing transfer registry keeps track of Transfer ID values for all currently existing local transfer senders.
* If a local transfer sender was inactive for a sufficiently long time, the outgoing transfer registry will
* remove the respective Transfer ID tracking object.
*
* TODO: Deinterface this.
*/
class UAVCAN_EXPORT IOutgoingTransferRegistry
{
@@ -74,7 +76,6 @@ public:
};
template <int NumStaticEntries>
class UAVCAN_EXPORT OutgoingTransferRegistry : public IOutgoingTransferRegistry, Noncopyable
{
struct Value
@@ -123,7 +124,7 @@ class UAVCAN_EXPORT OutgoingTransferRegistry : public IOutgoingTransferRegistry,
}
};
Map<OutgoingTransferRegistryKey, Value, NumStaticEntries> map_;
Map<OutgoingTransferRegistryKey, Value> map_;
public:
explicit OutgoingTransferRegistry(IPoolAllocator& allocator)
@@ -140,11 +141,12 @@ public:
// ----------------------------------------------------------------------------
/*
* OutgoingTransferRegistry<>
* OutgoingTransferRegistry
* TODO: deinterface and move to .cpp
*/
template <int NumStaticEntries>
TransferID* OutgoingTransferRegistry<NumStaticEntries>::accessOrCreate(const OutgoingTransferRegistryKey& key,
MonotonicTime new_deadline)
inline
TransferID* OutgoingTransferRegistry::accessOrCreate(const OutgoingTransferRegistryKey& key,
MonotonicTime new_deadline)
{
UAVCAN_ASSERT(!new_deadline.isZero());
Value* p = map_.access(key);
@@ -161,14 +163,14 @@ TransferID* OutgoingTransferRegistry<NumStaticEntries>::accessOrCreate(const Out
return &p->tid;
}
template <int NumStaticEntries>
bool OutgoingTransferRegistry<NumStaticEntries>::exists(DataTypeID dtid, TransferType tt) const
inline
bool OutgoingTransferRegistry::exists(DataTypeID dtid, TransferType tt) const
{
return NULL != map_.find(ExistenceCheckingPredicate(dtid, tt));
}
template <int NumStaticEntries>
void OutgoingTransferRegistry<NumStaticEntries>::cleanup(MonotonicTime ts)
inline
void OutgoingTransferRegistry::cleanup(MonotonicTime ts)
{
map_.removeAllWhere(DeadlineExpiredPredicate(ts));
}
@@ -194,8 +194,6 @@ public:
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
bool migrateFrom(const TransferBufferManagerEntry* tbme);
};
template <uint16_t Size>
@@ -250,11 +248,7 @@ class TransferBufferManagerImpl : public ITransferBufferManager, Noncopyable
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)
@@ -269,49 +263,20 @@ public:
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();
}
unsigned getNumBuffers() const;
};
template <uint16_t MaxBufSize>
class UAVCAN_EXPORT TransferBufferManager<MaxBufSize, 0> : public TransferBufferManagerImpl
class UAVCAN_EXPORT TransferBufferManager : public TransferBufferManagerImpl
{
virtual StaticTransferBufferManagerEntry<MaxBufSize>* getStaticByIndex(uint16_t) const
{
return NULL;
}
public:
explicit TransferBufferManager(IPoolAllocator& allocator)
: TransferBufferManagerImpl(MaxBufSize, allocator)
{
StaticAssert<(MaxBufSize > 0)>::check();
}
explicit TransferBufferManager(IPoolAllocator& allocator) :
TransferBufferManagerImpl(MaxBufSize, allocator)
{ }
};
template <>
class UAVCAN_EXPORT TransferBufferManager<0, 0> : public ITransferBufferManager
class UAVCAN_EXPORT TransferBufferManager<0> : public ITransferBufferManager
{
public:
TransferBufferManager() { }
@@ -99,7 +99,7 @@ public:
class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListenerBase>, Noncopyable
{
const DataTypeDescriptor& data_type_;
MapBase<TransferBufferManagerKey, TransferReceiver>& receivers_;
Map<TransferBufferManagerKey, TransferReceiver>& receivers_;
ITransferBufferManager& bufmgr_;
TransferPerfCounter& perf_;
const TransferCRC crc_base_; ///< Pre-initialized with data type hash, thus constant
@@ -123,7 +123,7 @@ class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListene
protected:
TransferListenerBase(TransferPerfCounter& perf, const DataTypeDescriptor& data_type,
MapBase<TransferBufferManagerKey, TransferReceiver>& receivers,
Map<TransferBufferManagerKey, TransferReceiver>& receivers,
ITransferBufferManager& bufmgr)
: data_type_(data_type)
, receivers_(receivers)
@@ -157,24 +157,18 @@ public:
/**
* This class should be derived by transfer receivers (subscribers, servers).
*/
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
template <unsigned MaxBufSize>
class UAVCAN_EXPORT TransferListener : public TransferListenerBase
{
TransferBufferManager<MaxBufSize, NumStaticBufs> bufmgr_;
Map<TransferBufferManagerKey, TransferReceiver, NumStaticReceivers> receivers_;
TransferBufferManager<MaxBufSize> bufmgr_;
Map<TransferBufferManagerKey, TransferReceiver> 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()
{
@@ -200,15 +194,15 @@ public:
/**
* This class should be derived by callers.
*/
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
class UAVCAN_EXPORT TransferListenerWithFilter : public TransferListener<MaxBufSize, NumStaticBufs, NumStaticReceivers>
template <unsigned MaxBufSize>
class UAVCAN_EXPORT TransferListenerWithFilter : public TransferListener<MaxBufSize>
{
const ITransferAcceptanceFilter* filter_;
virtual void handleFrame(const RxFrame& frame);
public:
typedef TransferListener<MaxBufSize, NumStaticBufs, NumStaticReceivers> BaseType;
typedef TransferListener<MaxBufSize> BaseType;
TransferListenerWithFilter(TransferPerfCounter& perf, const DataTypeDescriptor& data_type,
IPoolAllocator& allocator)
@@ -227,8 +221,8 @@ public:
/*
* TransferListenerWithFilter<>
*/
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
void TransferListenerWithFilter<MaxBufSize, NumStaticBufs, NumStaticReceivers>::handleFrame(const RxFrame& frame)
template <unsigned MaxBufSize>
void TransferListenerWithFilter<MaxBufSize>::handleFrame(const RxFrame& frame)
{
if (filter_ != NULL)
{
+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();