Skip to content

Commit

Permalink
Bulk alias allocator (#463)
Browse files Browse the repository at this point in the history
Adds an alternative implementation by which aliases can be allocated for an interface.
The BulkAliasAllocator works with high parallelism. It sends out many CID frames at once,
and handles if conflicts arrive for any one of these aliases being tested. Using this method
we can allocate many aliases a lot faster than with AliasAllocator which can allocate aliases
only sequentially, with a 200 msec delay each.

===

* Adds config option for how much memory should the bulk alias allocator use.

* Adds interface and implementation to bulk alias allocator.

* fix whitespace

* Small changes to alias allocator:
- adds accessor to get the node id used for allocation.
- fixes todo to check whether newly generated aliases are in the existing caches.
- adds ability to get the next proposed alias from the sequence.

* Adds bulk allocator to the test.

* Adds a test facility to invoke a flow without waiting for the result.

* fixes bugs

* Adds basic test for bulk allocator.

* adds time expectation

* Adds support for conflict handling in the bulk alias allocator.

* fix whitespace

* Fix comments.

* Fixes comments.

* fixes comments.
  • Loading branch information
balazsracz authored Nov 15, 2020
2 parents 44f31ee + 6b8fba2 commit d782b0a
Show file tree
Hide file tree
Showing 10 changed files with 542 additions and 5 deletions.
4 changes: 4 additions & 0 deletions include/nmranet_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ DECLARE_CONST(enable_all_memory_space);
* standard. */
DECLARE_CONST(node_init_identify);

/** How many CAN frames should the bulk alias allocator be sending at the same
* time. */
DECLARE_CONST(bulk_alias_num_can_frames);

/** Stack size for @ref SocketListener threads. */
DECLARE_CONST(socket_listener_stack_size);

Expand Down
22 changes: 19 additions & 3 deletions src/openlcb/AliasAllocator.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,7 @@ StateFlowBase::Action AliasAllocator::entry()
HASSERT(pending_alias()->state == AliasInfo::STATE_EMPTY);
while (!pending_alias()->alias)
{
pending_alias()->alias = seed_;
next_seed();
// TODO(balazs.racz): check if the alias is already known about.
pending_alias()->alias = get_new_seed();
}
// Registers ourselves as a handler for incoming CAN frames to detect
// conflicts.
Expand All @@ -124,6 +122,24 @@ StateFlowBase::Action AliasAllocator::entry()
return call_immediately(STATE(handle_allocate_for_cid_frame));
}

NodeAlias AliasAllocator::get_new_seed()
{
while (true)
{
NodeAlias ret = seed_;
next_seed();
if (if_can()->local_aliases()->lookup(ret))
{
continue;
}
if (if_can()->remote_aliases()->lookup(ret))
{
continue;
}
return ret;
}
}

void AliasAllocator::next_seed()
{
uint16_t offset;
Expand Down
109 changes: 108 additions & 1 deletion src/openlcb/AliasAllocator.cxxtest
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#include <map>

#include "utils/async_if_test_helper.hxx"
#include "openlcb/AliasAllocator.hxx"
#include "openlcb/AliasCache.hxx"
#include "openlcb/BulkAliasAllocator.hxx"
#include "openlcb/CanDefs.hxx"
#include "utils/async_if_test_helper.hxx"

namespace openlcb
{
Expand All @@ -13,6 +14,7 @@ protected:
AsyncAliasAllocatorTest()
: b_(nullptr)
, alias_allocator_(TEST_NODE_ID, ifCan_.get())
, bulkAllocator_(create_bulk_alias_allocator(ifCan_.get()))
{
}

Expand Down Expand Up @@ -48,8 +50,55 @@ protected:
}
}

/// Pre-generates some aliases into a vector.
void generate_aliases(AliasAllocator *alloc, unsigned count)
{
set_seed(0x555, alloc);
run_x([this, count, alloc]() {
for (unsigned i = 0; i < count; i++)
{
auto a = alloc->get_new_seed();
LOG(INFO, "alias %03X", a);
aliases_.push_back(a);
}
});
set_seed(0x555, alloc);
}

/// Expects that CID frames are sent to the bus.
/// @param begin iterator into alias array
/// @param end iterator (end) into alias array
template <typename It> void expect_cid(It begin, It end)
{
for (auto it = begin; it != end; ++it)
{
NodeAlias a = *it;
string msg = StringPrintf("cid %03X", a);
LOG(INFO, "cid %03X", a);
expect_packet(StringPrintf(":X17020%03XN;", a));
expect_packet(StringPrintf(":X1610D%03XN;", a));
expect_packet(StringPrintf(":X15000%03XN;", a));
expect_packet(StringPrintf(":X14003%03XN;", a));
}
}

/// Expects that RID frames are sent to the bus.
/// @param begin iterator into alias array
/// @param end iterator (end) into alias array
template <typename It> void expect_rid(It begin, It end)
{
for (auto it = begin; it != end; ++it)
{
NodeAlias a = *it;
LOG(INFO, "rid %03X", a);
expect_packet(StringPrintf(":X10700%03XN;", a));
}
}

Buffer<AliasInfo> *b_;
AliasAllocator alias_allocator_;
std::unique_ptr<BulkAliasAllocatorInterface> bulkAllocator_;
std::vector<NodeAlias> aliases_;
};

TEST_F(AsyncAliasAllocatorTest, SetupTeardown)
Expand Down Expand Up @@ -218,4 +267,62 @@ TEST_F(AsyncAliasAllocatorTest, DifferentGenerated)
// Makes sure 'other' disappears from the executor before destructing it.
wait();
}

TEST_F(AsyncAliasAllocatorTest, BulkFew)
{
generate_aliases(ifCan_->alias_allocator(), 5);
expect_cid(aliases_.begin(), aliases_.end());
LOG(INFO, "invoke");
auto start_time = os_get_time_monotonic();
auto invocation = invoke_flow_nowait(bulkAllocator_.get(), 5);
wait();
LOG(INFO, "expect RIDs");
clear_expect(true);
expect_rid(aliases_.begin(), aliases_.end());
LOG(INFO, "wait for complete");
invocation->wait();
clear_expect(true);
auto end_time = os_get_time_monotonic();
EXPECT_LT(MSEC_TO_NSEC(200), end_time - start_time);
}

TEST_F(AsyncAliasAllocatorTest, BulkConflict)
{
generate_aliases(ifCan_->alias_allocator(), 7);
clear_expect(true);
expect_cid(aliases_.begin(), aliases_.begin() + 5);
LOG(INFO, "invoke");
auto invocation = invoke_flow_nowait(bulkAllocator_.get(), 5);
wait();
LOG(INFO, "send conflicts");
clear_expect(true);
expect_cid(aliases_.begin() + 5, aliases_.end());
send_packet(StringPrintf(":X10700%03XN;", aliases_[0]));
send_packet(StringPrintf(":X10700%03XN;", aliases_[1]));
wait();
usleep(10000);
wait();
LOG(INFO, "expect RIDs");
clear_expect(true);
expect_rid(aliases_.begin() + 2, aliases_.end());
LOG(INFO, "wait for complete");
invocation->wait();
clear_expect(true);
}

TEST_F(AsyncAliasAllocatorTest, BulkMany)
{
generate_aliases(ifCan_->alias_allocator(), 150);
expect_cid(aliases_.begin(), aliases_.end());
LOG(INFO, "invoke");
auto invocation = invoke_flow_nowait(bulkAllocator_.get(), 150);
wait();
LOG(INFO, "expect RIDs");
clear_expect(true);
expect_rid(aliases_.begin(), aliases_.end());
LOG(INFO, "wait for complete");
invocation->wait();
clear_expect(true);
}

} // namespace openlcb
11 changes: 11 additions & 0 deletions src/openlcb/AliasAllocator.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,24 @@ public:
*/
AliasAllocator(NodeID if_id, IfCan *if_can);

/** Destructor */
virtual ~AliasAllocator();

/** @return the Node ID for the interface. */
NodeID if_node_id()
{
return if_id_;
}

/** Resets the alias allocator to the state it was at construction. useful
* after connection restart in order to ensure it will try to allocate the
* same alias. */
void reinit_seed();

/** Returns a new alias to check from the random sequence. Checks that it
* is not in the alias cache yet.*/
NodeAlias get_new_seed();

/** "Allocate" a buffer from this pool (but without initialization) in
* order to get a reserved alias. */
QAsync *reserved_aliases()
Expand Down
Loading

0 comments on commit d782b0a

Please sign in to comment.