Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Review & Discuss] Update TxQueue to have a backing AVL Tree (Issues #189 #195) #198

Merged
merged 23 commits into from
Mar 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
906a5e4
AVL Tree backed queue
ntakouris Jan 29, 2019
01ad9c6
Adhere to proposed code review changes
ntakouris Jan 29, 2019
722f80b
Update CanIOManager.Transmission tests (fixes from queue changes -- m…
ntakouris Jan 29, 2019
f71bf9e
(have to switch to desktop pc)
ntakouris Jan 29, 2019
188eeb8
Fix some errors
ntakouris Jan 29, 2019
e743bf4
private decls then protected then public
ntakouris Jan 29, 2019
4223aea
Add sanity tests for AvlTree
ntakouris Jan 29, 2019
3d16be6
Add multiple entries per key tests for AvlTree
ntakouris Jan 31, 2019
ec17bb7
Remove remove_always (does not keep rotations)
ntakouris Jan 31, 2019
63b48c7
Update CanIOManager.Transmission test
ntakouris Jan 31, 2019
e0d9324
Add rotation tests for AvlTree
ntakouris Feb 4, 2019
6f02ef3
style and naming changes
ntakouris Feb 13, 2019
36701bc
Remove heap allocs, adhere to suggested changes
ntakouris Feb 20, 2019
1a08155
Fix the unconditional for loop, styling & formatting
ntakouris Feb 22, 2019
bd69b04
Remove auto in avl_tree.cpp
ntakouris Feb 22, 2019
2c12d20
Fix tx_queue.cpp tests
ntakouris Feb 22, 2019
41d92b6
naming
ntakouris Feb 22, 2019
6ebda4a
Drop QoS completely
ntakouris Feb 22, 2019
5b207da
Fix explicit this
ntakouris Feb 23, 2019
d5a21fe
Remove one reduntant static cast, add suggested change on balanceOf
ntakouris Feb 28, 2019
5df8572
Braces revert for canio manager
ntakouris Mar 1, 2019
e61e964
Add OOM test for AvlTree node allocation
ntakouris Mar 1, 2019
4986156
Remove null check on getSize, revert min-height Of
ntakouris Mar 1, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions libuavcan/include/uavcan/driver/can.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ struct UAVCAN_EXPORT CanFrame
return (id == rhs.id) && (dlc == rhs.dlc) && equal(data, data + dlc, rhs.data);
}

bool operator<(const CanFrame & other) const
{
return this->priorityLowerThan(other);
}

bool operator>(const CanFrame & other) const
{
return this->priorityHigherThan(other);
}

bool isExtended() const { return id & FlagEFF; }
bool isRemoteTransmissionRequest() const { return id & FlagRTR; }
bool isErrorFrame() const { return id & FlagERR; }
Expand Down
5 changes: 1 addition & 4 deletions libuavcan/include/uavcan/node/abstract_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,12 @@ class UAVCAN_EXPORT INode
*
* Optional parameters:
*
* @param qos Quality of service. Please refer to the CAN IO manager for details.
*
* @param flags CAN IO flags. Please refer to the CAN driver API for details.
*/
int injectTxFrame(const CanFrame& frame, MonotonicTime tx_deadline, uint8_t iface_mask,
CanTxQueue::Qos qos = CanTxQueue::Volatile,
CanIOFlags flags = 0)
{
return getDispatcher().getCanIOManager().send(frame, tx_deadline, MonotonicTime(), iface_mask, qos, flags);
return getDispatcher().getCanIOManager().send(frame, tx_deadline, MonotonicTime(), iface_mask, flags);
}

#if !UAVCAN_TINY
Expand Down
11 changes: 3 additions & 8 deletions libuavcan/include/uavcan/node/generic_publisher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class GenericPublisherBase : Noncopyable

bool isInited() const;

int doInit(DataTypeKind dtkind, const char* dtname, CanTxQueue::Qos qos);
int doInit(DataTypeKind dtkind, const char* dtname);

MonotonicTime getTxDeadline() const;

Expand Down Expand Up @@ -93,12 +93,6 @@ class UAVCAN_EXPORT GenericPublisher : public GenericPublisherBase
ZeroTransferBuffer,
StaticTransferBuffer<BitLenToByteLen<DataStruct::MaxBitLen>::Result> >::Result Buffer;

enum
{
Qos = (DataTypeKind(DataSpec::DataTypeKind) == DataTypeKindMessage) ?
CanTxQueue::Volatile : CanTxQueue::Persistent
};

int checkInit();

int doEncode(const DataStruct& message, ITransferBuffer& buffer) const;
Expand Down Expand Up @@ -157,7 +151,8 @@ int GenericPublisher<DataSpec, DataStruct>::checkInit()
{
return 0;
}
return doInit(DataTypeKind(DataSpec::DataTypeKind), DataSpec::getDataTypeFullName(), CanTxQueue::Qos(Qos));

return doInit(DataTypeKind(DataSpec::DataTypeKind), DataSpec::getDataTypeFullName());
}

template <typename DataSpec, typename DataStruct>
Expand Down
116 changes: 55 additions & 61 deletions libuavcan/include/uavcan/transport/can_io.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* CAN bus IO logic.
* Copyright (C) 2014 Pavel Kirienko <[email protected]>
* Copyright (C) 2019 Theodoros Ntakouris <[email protected]>
*/

#ifndef UAVCAN_TRANSPORT_CAN_IO_HPP_INCLUDED
Expand All @@ -10,6 +11,7 @@
#include <uavcan/error.hpp>
#include <uavcan/std.hpp>
#include <uavcan/util/linked_list.hpp>
#include <uavcan/util/avl_tree.hpp>
#include <uavcan/dynamic_memory.hpp>
#include <uavcan/build_config.hpp>
#include <uavcan/util/templates.hpp>
Expand All @@ -20,7 +22,6 @@

namespace uavcan
{

struct UAVCAN_EXPORT CanRxFrame : public CanFrame
{
MonotonicTime ts_mono;
Expand All @@ -36,87 +37,81 @@ struct UAVCAN_EXPORT CanRxFrame : public CanFrame
#endif
};


class UAVCAN_EXPORT CanTxQueue : Noncopyable
struct UAVCAN_EXPORT CanTxQueueEntry // Not required to be packed - fits the block in any case
{
public:
enum Qos { Volatile, Persistent };
MonotonicTime deadline;
const CanFrame frame;
CanIOFlags flags;

CanTxQueueEntry(const CanFrame& arg_frame, const MonotonicTime arg_deadline, CanIOFlags arg_flags)
thirtytwobits marked this conversation as resolved.
Show resolved Hide resolved
: deadline(arg_deadline)
, frame(arg_frame)
, flags(arg_flags)
ntakouris marked this conversation as resolved.
Show resolved Hide resolved
{
IsDynamicallyAllocatable<CanTxQueueEntry>::check();
}

static void destroy(CanTxQueueEntry*& obj, IPoolAllocator& allocator);

bool isExpired(const MonotonicTime timestamp) const { return timestamp > deadline; }

bool operator<(const CanTxQueueEntry& other) const
{
return this->frame.priorityLowerThan(other.frame);
}

bool operator>(const CanTxQueueEntry& other) const
{
return this->frame.priorityHigherThan(other.frame);
}

struct Entry : public LinkedListNode<Entry> // Not required to be packed - fits the block in any case
bool operator==(const CanTxQueueEntry& other) const
{
MonotonicTime deadline;
CanFrame frame;
uint8_t qos;
CanIOFlags flags;

Entry(const CanFrame& arg_frame, MonotonicTime arg_deadline, Qos arg_qos, CanIOFlags arg_flags)
: deadline(arg_deadline)
, frame(arg_frame)
, qos(uint8_t(arg_qos))
, flags(arg_flags)
{
UAVCAN_ASSERT((qos == Volatile) || (qos == Persistent));
IsDynamicallyAllocatable<Entry>::check();
}

static void destroy(Entry*& obj, IPoolAllocator& allocator);

bool isExpired(MonotonicTime timestamp) const { return timestamp > deadline; }

bool qosHigherThan(const CanFrame& rhs_frame, Qos rhs_qos) const;
bool qosLowerThan(const CanFrame& rhs_frame, Qos rhs_qos) const;
bool qosHigherThan(const Entry& rhs) const { return qosHigherThan(rhs.frame, Qos(rhs.qos)); }
bool qosLowerThan(const Entry& rhs) const { return qosLowerThan(rhs.frame, Qos(rhs.qos)); }
return this->frame == other.frame;
}

#if UAVCAN_TOSTRING
std::string toString() const;
#endif
};
};

class UAVCAN_EXPORT CanTxQueue : public AvlTree<CanTxQueueEntry>
{
private:
class PriorityInsertionComparator
{
const CanFrame& frm_;
public:
explicit PriorityInsertionComparator(const CanFrame& frm) : frm_(frm) { }
bool operator()(const Entry* entry)
{
UAVCAN_ASSERT(entry);
return frm_.priorityHigherThan(entry->frame);
}
};
void postOrderTraverseEntryCleanup(Node* n);

LinkedListRoot<Entry> queue_;
LimitedPoolAllocator allocator_;
protected:
ISystemClock& sysclock_;
uint32_t rejected_frames_cnt_;

void registerRejectedFrame();
bool linkedListContains(Node* head, const CanFrame& frame) const;
void safeIncrementRejectedFrames();
AvlTree::Node* searchForNonExpiredMax(Node* n);
ntakouris marked this conversation as resolved.
Show resolved Hide resolved

public:
CanTxQueue(IPoolAllocator& allocator, ISystemClock& sysclock, std::size_t allocator_quota)
: allocator_(allocator, allocator_quota)
, sysclock_(sysclock)
, rejected_frames_cnt_(0)
{ }
CanTxQueue(IPoolAllocator& allocator, ISystemClock& sysclock, std::size_t allocator_quota) :
AvlTree(allocator, allocator_quota),
sysclock_(sysclock),
rejected_frames_cnt_(0)
{}

~CanTxQueue();
~CanTxQueue() override;

void push(const CanFrame& frame, MonotonicTime tx_deadline, Qos qos, CanIOFlags flags);
/* Avl Tree allocates the AvlTree::Node, while this(CanTxQueue) allocates the CanTxQueueEntry
* Same logic for removal. */
void push(const CanFrame& frame, MonotonicTime tx_deadline, CanIOFlags flags);
void remove(CanTxQueueEntry* entry);

Entry* peek(); // Modifier
void remove(Entry*& entry);
const CanFrame* getTopPriorityPendingFrame() const;
uint32_t getRejectedFrameCount() const { return rejected_frames_cnt_; }

/// The 'or equal' condition is necessary to avoid frame reordering.
bool topPriorityHigherOrEqual(const CanFrame& rhs_frame) const;
bool contains(const CanFrame& frame) const;

uint32_t getRejectedFrameCount() const { return rejected_frames_cnt_; }
/* Tries to look up rightmost Node. If the frame is expired, removes it and continues traversing */
CanTxQueueEntry* peek();

bool isEmpty() const { return queue_.isEmpty(); }
bool topPriorityHigherOrEqual(const CanFrame& rhs_frame);
};


struct UAVCAN_EXPORT CanIfacePerfCounters
{
uint64_t frames_tx;
Expand All @@ -130,7 +125,6 @@ struct UAVCAN_EXPORT CanIfacePerfCounters
{ }
};


class UAVCAN_EXPORT CanIOManager : Noncopyable
{
struct IfaceFrameCounters
Expand Down Expand Up @@ -177,7 +171,7 @@ class UAVCAN_EXPORT CanIOManager : Noncopyable
* negative - failure
*/
int send(const CanFrame& frame, MonotonicTime tx_deadline, MonotonicTime blocking_deadline,
uint8_t iface_mask, CanTxQueue::Qos qos, CanIOFlags flags);
uint8_t iface_mask, CanIOFlags flags);
int receive(CanRxFrame& out_frame, MonotonicTime blocking_deadline, CanIOFlags& out_flags);
};

Expand Down
2 changes: 1 addition & 1 deletion libuavcan/include/uavcan/transport/dispatcher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class UAVCAN_EXPORT Dispatcher : Noncopyable
/**
* Refer to CanIOManager::send() for the parameter description
*/
int send(const Frame& frame, MonotonicTime tx_deadline, MonotonicTime blocking_deadline, CanTxQueue::Qos qos,
int send(const Frame& frame, MonotonicTime tx_deadline, MonotonicTime blocking_deadline,
CanIOFlags flags, uint8_t iface_mask);

void cleanup(MonotonicTime ts);
Expand Down
9 changes: 3 additions & 6 deletions libuavcan/include/uavcan/transport/transfer_sender.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class UAVCAN_EXPORT TransferSender
Dispatcher& dispatcher_;

TransferPriority priority_;
CanTxQueue::Qos qos_;
TransferCRC crc_base_;
DataTypeID data_type_id_;
CanIOFlags flags_;
Expand All @@ -41,30 +40,28 @@ class UAVCAN_EXPORT TransferSender
return MonotonicDuration::fromMSec(60 * 1000);
}

TransferSender(Dispatcher& dispatcher, const DataTypeDescriptor& data_type, CanTxQueue::Qos qos,
TransferSender(Dispatcher& dispatcher, const DataTypeDescriptor& data_type,
MonotonicDuration max_transfer_interval = getDefaultMaxTransferInterval())
: max_transfer_interval_(max_transfer_interval)
, dispatcher_(dispatcher)
, priority_(TransferPriority::Default)
, qos_(CanTxQueue::Qos())
, flags_(CanIOFlags(0))
, iface_mask_(AllIfacesMask)
, allow_anonymous_transfers_(false)
{
init(data_type, qos);
init(data_type);
}

TransferSender(Dispatcher& dispatcher, MonotonicDuration max_transfer_interval = getDefaultMaxTransferInterval())
: max_transfer_interval_(max_transfer_interval)
, dispatcher_(dispatcher)
, priority_(TransferPriority::Default)
, qos_(CanTxQueue::Qos())
, flags_(CanIOFlags(0))
, iface_mask_(AllIfacesMask)
, allow_anonymous_transfers_(false)
{ }

void init(const DataTypeDescriptor& dtid, CanTxQueue::Qos qos);
void init(const DataTypeDescriptor& dtid);

bool isInitialized() const { return data_type_id_ != DataTypeID(); }

Expand Down
Loading