Skip to content

Commit

Permalink
Adds support for conflict handling in the bulk alias allocator.
Browse files Browse the repository at this point in the history
  • Loading branch information
balazsracz committed Nov 14, 2020
1 parent 06841e8 commit 60e854f
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 22 deletions.
110 changes: 88 additions & 22 deletions src/openlcb/AliasAllocator.cxxtest
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +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 @@ -224,39 +270,59 @@ TEST_F(AsyncAliasAllocatorTest, DifferentGenerated)

TEST_F(AsyncAliasAllocatorTest, BulkFew)
{
set_seed(0x555, ifCan_->alias_allocator());
std::vector<NodeAlias> aliases;
run_x([this, &aliases]() {
for (unsigned i = 0; i < 5; i++)
{
auto a = ifCan_->alias_allocator()->get_new_seed();
aliases.push_back(a);
}
});
for (unsigned i = 0; i < 5; i++) {
auto a = aliases[i];
LOG(INFO, "alias %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));
}
generate_aliases(ifCan_->alias_allocator(), 5);
expect_cid(aliases_.begin(), aliases_.end());
LOG(INFO, "invoke");
set_seed(0x555, ifCan_->alias_allocator());
auto start_time = os_get_time_monotonic();
auto invocation = invoke_flow_nowait(bulkAllocator_.get(), 5);
wait();
LOG(INFO, "expect RIDs");
clear_expect(true);
for (unsigned i = 0; i < 5; i++) {
auto a = aliases[i];
expect_packet(StringPrintf(":X10700%03XN;", a));
}
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
21 changes: 21 additions & 0 deletions src/openlcb/BulkAliasAllocator.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class BulkAliasAllocator : public CallableFlow<BulkAliasRequest>
pendingAliasesByKey_.clear();
nextToStampTime_ = 0;
nextToClaim_ = 0;
if_can()->frame_dispatcher()->register_handler(&conflictHandler_, 0, 0);
return call_immediately(STATE(send_cid_frames));
}

Expand Down Expand Up @@ -103,6 +104,11 @@ class BulkAliasAllocator : public CallableFlow<BulkAliasRequest>
{
return complete();
}
if (request()->numAliases_)
{
// Some conflicts were identified, go and allocate more.
return call_immediately(STATE(send_cid_frames));
}
auto ctime = relative_time();
unsigned num_sent = 0;
bn_.reset(this);
Expand Down Expand Up @@ -140,12 +146,27 @@ class BulkAliasAllocator : public CallableFlow<BulkAliasRequest>

Action complete()
{
if_can()->frame_dispatcher()->unregister_handler_all(&conflictHandler_);
pendingAliasesByTime_.clear();
pendingAliasesByKey_.clear();
return return_ok();
}

private:
void handle_conflict(Buffer<CanMessageData> *message)
{
auto rb = get_buffer_deleter(message);
auto alias = CanDefs::get_src(GET_CAN_FRAME_ID_EFF(*message->data()));
auto it = pendingAliasesByKey_.find(alias);
if (it != pendingAliasesByKey_.end() && !it->hasConflict_) {
it->hasConflict_ = 1;
++request()->numAliases_;
}
}

/// Listens to incoming CAN frames and handles alias conflicts.
IncomingFrameHandler::GenericHandler conflictHandler_{this, &BulkAliasAllocator::handle_conflict};

/// How many count to wait before sending out the RID frames. One count is
/// 10 msec (see { \link relative_time } ).
static constexpr unsigned ALLOCATE_DELAY = 20;
Expand Down

0 comments on commit 60e854f

Please sign in to comment.