From 60e854f8f56dc0c0dbc28ff8a938f7b61a219fb2 Mon Sep 17 00:00:00 2001
From: Balazs Racz <balazs.racz@gmail.com>
Date: Sun, 15 Nov 2020 00:27:52 +0100
Subject: [PATCH] Adds support for conflict handling in the bulk alias
 allocator.

---
 src/openlcb/AliasAllocator.cxxtest | 110 +++++++++++++++++++++++------
 src/openlcb/BulkAliasAllocator.cxx |  21 ++++++
 2 files changed, 109 insertions(+), 22 deletions(-)

diff --git a/src/openlcb/AliasAllocator.cxxtest b/src/openlcb/AliasAllocator.cxxtest
index 44080c417..621c6559b 100644
--- a/src/openlcb/AliasAllocator.cxxtest
+++ b/src/openlcb/AliasAllocator.cxxtest
@@ -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)
@@ -224,34 +270,15 @@ 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);
@@ -259,4 +286,43 @@ TEST_F(AsyncAliasAllocatorTest, BulkFew)
     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
diff --git a/src/openlcb/BulkAliasAllocator.cxx b/src/openlcb/BulkAliasAllocator.cxx
index c19e5855e..ba9deafca 100644
--- a/src/openlcb/BulkAliasAllocator.cxx
+++ b/src/openlcb/BulkAliasAllocator.cxx
@@ -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));
     }
 
@@ -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);
@@ -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;