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

Bulk alias allocator #463

Merged
merged 14 commits into from
Nov 15, 2020
Merged
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