LCOV - code coverage report
Current view: top level - ifmap - ifmap_update_sender.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 121 183 66.1 %
Date: 2026-11-09 02:10:49 Functions: 13 16 81.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include "ifmap/ifmap_update_sender.h"
       7             : #include "base/task.h"
       8             : #include "ifmap/ifmap_client.h"
       9             : #include "ifmap/ifmap_server.h"
      10             : #include "ifmap/ifmap_exporter.h"
      11             : #include "ifmap/ifmap_log.h"
      12             : #include "ifmap/ifmap_log_types.h"
      13             : #include "ifmap/ifmap_update.h"
      14             : #include "ifmap/ifmap_update_queue.h"
      15             : 
      16             : using namespace std;
      17             : 
      18         195 : IFMapUpdateSender::IFMapUpdateSender(IFMapServer *server,
      19         195 :                                      IFMapUpdateQueue *queue)
      20         195 :     : server_(server), queue_(queue), message_(new IFMapMessage()),
      21         390 :       task_scheduled_(false), queue_active_(false) {
      22         195 : }
      23             : 
      24         378 : IFMapUpdateSender::~IFMapUpdateSender() {
      25         195 :     delete(message_);
      26         378 : }
      27             : 
      28             : class IFMapUpdateSender::SendTask : public Task {
      29             : public:
      30          50 :     explicit SendTask(IFMapUpdateSender *sender)
      31          50 :         : Task(TaskScheduler::GetInstance()->GetTaskId("db::IFMapTable"), 0),
      32          50 :           sender_(sender) {
      33          50 :     }
      34          50 :     virtual bool Run() {
      35          50 :         BitSet send_scheduled;
      36          50 :         sender_->GetSendScheduled(&send_scheduled);
      37          50 :         sender_->send_blocked_.Reset(send_scheduled);
      38          50 :         for (size_t i = send_scheduled.find_first(); i != BitSet::npos;
      39           0 :              i = send_scheduled.find_next(i)) {
      40             :             // Dequeue from client marker (i).
      41           0 :             IFMAP_UPD_SENDER_TRACE(IFMapUSSendScheduled, "Send scheduled for",
      42             :                 send_scheduled.ToNumberedString(), "client", i,
      43             :                 sender_->queue_->GetMarker(i)->ToString());
      44           0 :             sender_->Send(sender_->queue_->GetMarker(i));
      45             :         }
      46          50 :         if (sender_->queue_active_) {
      47             :             // Dequeue from tail marker.
      48             :             // Reset queue_active_
      49          50 :             IFMAP_UPD_SENDER_TRACE(IFMapUSQueueActive, "Queue active for",
      50             :                 sender_->queue_->tail_marker()->ToString());
      51          50 :             sender_->Send(sender_->queue_->tail_marker());
      52          50 :             sender_->queue_active_ = false;
      53             :         }
      54          50 :         return true;
      55          50 :     }
      56             : 
      57           0 :     std::string Description() const { return "IFMapUpdateSender::SendTask"; }
      58             : private:
      59             :     IFMapUpdateSender *sender_;
      60             : };
      61             : 
      62          50 : void IFMapUpdateSender::StartTask() {
      63          50 :     if (!task_scheduled_) {
      64             :         // create new task
      65          50 :         SendTask *send_task = new SendTask(this);
      66          50 :         TaskScheduler *scheduler = TaskScheduler::GetInstance();
      67          50 :         scheduler->Enqueue(send_task);
      68          50 :         task_scheduled_ = true;
      69             :     }
      70          50 : }
      71             : 
      72        1006 : void IFMapUpdateSender::QueueActive() {
      73        1006 :     if (queue_active_) {
      74         956 :         return;
      75             :     }
      76          50 :     queue_active_ = true;
      77          50 :     std::scoped_lock lock(mutex_);
      78          50 :     StartTask();
      79          50 : }
      80             : 
      81           0 : void IFMapUpdateSender::SendActive(int index) {
      82           0 :     std::scoped_lock lock(mutex_);
      83           0 :     send_scheduled_.set(index);
      84           0 :     StartTask();
      85           0 : }
      86             : 
      87          50 : void IFMapUpdateSender::GetSendScheduled(BitSet *current) {
      88          50 :     std::scoped_lock lock(mutex_);
      89          50 :     *current = send_scheduled_;
      90          50 :     send_scheduled_.clear();
      91          50 :     task_scheduled_ = false;
      92          50 : }
      93             : 
      94           6 : void IFMapUpdateSender::CleanupClient(int index) {
      95           6 :     std::scoped_lock lock(mutex_);
      96           6 :     send_scheduled_.reset(index);
      97           6 :     send_blocked_.reset(index);
      98           6 : }
      99             : 
     100             : // We return only under 2 conditions:
     101             : // 1. All the clients in the marker are blocked.
     102             : // 2. We have finished traversing the Q.
     103             : // Invariant: while we are traversing the Q, the marker that we are working
     104             : // with only has ready clients. As soon as a client blocks, we split it out and
     105             : // continue with the ready set.
     106          50 : void IFMapUpdateSender::Send(IFMapMarker *imarker) {
     107          50 :     IFMapMarker *marker = imarker;
     108             : 
     109             :     // Get the clients in this marker that are blocked. If all of the clients in
     110             :     // this marker are blocked, we are done.
     111          50 :     BitSet blocked_clients;
     112          50 :     blocked_clients = (marker->mask & send_blocked_);
     113          50 :     if (blocked_clients == marker->mask) {
     114           0 :         return;
     115             :     }
     116             : 
     117             :     // If any of the clients are blocked, create a new marker for the set of
     118             :     // blocked clients, insert it before marker and continue with the ready
     119             :     // set.
     120          50 :     if (!blocked_clients.empty()) {
     121           0 :         IFMAP_UPD_SENDER_TRACE(IFMapUSSplitBlocked, "Splitting blocked clients",
     122             :             blocked_clients.ToNumberedString(), "from", marker->ToString());
     123           0 :         queue_->MarkerSplitBefore(marker, marker, blocked_clients);
     124             :     }
     125             : 
     126          50 :     IFMapListEntry *next = queue_->Next(marker);
     127          50 :     BitSet base_send_set;
     128             : 
     129             :     // Start with the node after the 'marker'
     130         940 :     for (IFMapListEntry *curr = next; curr != NULL; curr = next) {
     131         890 :         next = queue_->Next(curr);
     132             : 
     133         890 :         if (curr->IsMarker()) {
     134           0 :             IFMapMarker *next_marker = static_cast<IFMapMarker *>(curr);
     135             :             // Processing the next_marker can change the send_set and all
     136             :             // clients in the next_marker should have already seen the updates
     137             :             // currently sitting in the buffer. So, flush the buffer to the
     138             :             // existing client-set before processing the marker so that we dont
     139             :             // send duplicates.
     140           0 :             if (!message_->IsEmpty()) {
     141           0 :                 BitSet blocked_set;
     142           0 :                 SendUpdate(base_send_set, &blocked_set);
     143           0 :             }
     144             :             bool done;
     145           0 :             marker = ProcessMarker(marker, next_marker, &done);
     146           0 :             if (done) {
     147             :                 // All the clients in this marker are blocked. We are done.
     148           0 :                 return;
     149             :             }
     150             :             // marker has the ready clients. Continue as if we are starting
     151             :             // fresh.
     152           0 :             base_send_set.clear();
     153           0 :             continue;
     154           0 :         }
     155             : 
     156             :         // ...else its an update or delete
     157             : 
     158         890 :         IFMapUpdate *update = static_cast<IFMapUpdate *>(curr);
     159         890 :         BitSet send_set = update->advertise() & marker->mask;
     160         890 :         if (send_set.empty()) {
     161           0 :             continue;
     162             :         }
     163             : 
     164         890 :         if (base_send_set.empty()) {
     165          50 :             base_send_set = send_set;
     166             :         }
     167             : 
     168             :         // Flush the message to all possible clients if:
     169             :         // 1. The buffer is full OR
     170             :         // 2. The send_set is changing and buffer is filled.
     171        1724 :         if (message_->IsFull() ||
     172         834 :             ((base_send_set != send_set) && !message_->IsEmpty())) {
     173             : 
     174          64 :             BitSet blocked_set;
     175          64 :             SendUpdate(base_send_set, &blocked_set);
     176          64 :             if (!blocked_set.empty()) {
     177             :                 // All the clients in this marker are blocked. We are done.
     178           0 :                 if (blocked_set == marker->mask) {
     179           0 :                     IFMAP_UPD_SENDER_TRACE(IFMapUSAllBlocked, marker->ToString(),
     180             :                         "blocked before", curr->ToString());
     181           0 :                     queue_->MoveMarkerBefore(marker, curr);
     182           0 :                     return;
     183             :                 }
     184             :                 // Only a subset of clients in this marker are blocked. Insert
     185             :                 // a marker for them 'before' curr since they have seen
     186             :                 // everything before curr. Let the ready clients continue the
     187             :                 // traversal.
     188           0 :                 IFMAP_UPD_SENDER_TRACE(IFMapUSSubsetBlocked, "Clients",
     189             :                     blocked_set.ToNumberedString(), "blocked before",
     190             :                     curr->ToString(), "and split from", marker->ToString());
     191           0 :                 queue_->MarkerSplitBefore(marker, curr, blocked_set);
     192           0 :                 send_set.Reset(blocked_set);
     193             :             }
     194             : 
     195             :             // The send_set for this marker is changing. Pick up the new one.
     196          64 :             base_send_set = send_set;
     197          64 :         }
     198             : 
     199             :         // base_send_set is same as send_set at this point.
     200         890 :         ProcessUpdate(update, base_send_set);
     201         890 :     }
     202             : 
     203             :     // The buffer will be filled in the common case of updates being added
     204             :     // after the tail_marker.
     205          50 :     BitSet blk_set;
     206          50 :     if (!message_->IsEmpty()) {
     207          50 :         SendUpdate(base_send_set, &blk_set);
     208             :     }
     209             :     // If the last node in the Q was the tail_marker, we would have already
     210             :     // flushed the buffer and merged with it and we would be the last node in
     211             :     // the Q.
     212          50 :     IFMapListEntry *last = queue_->GetLast();
     213          50 :     if (marker != last) {
     214             :         // Since we have reached the end of the Q, we better be the tail_marker
     215           0 :         assert(marker == queue_->tail_marker());
     216             :         // If we have any blocked clients, splitting markers for them is not
     217             :         // useful at this point. Just move the marker to the end of the Q,
     218             :         // immediately after last, even if it has blocked clients. Being lazy
     219             :         // is advantageous since by the time we get the next trigger, a blocked
     220             :         // client could have become ready and splitting the marker now would be
     221             :         // useless.
     222           0 :         IFMAP_UPD_SENDER_TRACE(IFMapUSMoveAfterLast, "Moving", marker->ToString(),
     223             :             "before", last->ToString(), "with blocked_set",
     224             :             blk_set.ToNumberedString());
     225           0 :         queue_->MoveMarkerAfter(marker, last);
     226             :     }
     227          50 :     return;
     228          50 : }
     229             : 
     230         890 : void IFMapUpdateSender::ProcessUpdate(IFMapUpdate *update,
     231             :                                       const BitSet &base_send_set) {
     232         890 :     LogAndCountSentUpdate(update, base_send_set);
     233             : 
     234             :     // Append the contents of the update-node to the message.
     235         890 :     message_->EncodeUpdate(update);
     236             : 
     237             :     // Clean up the node if everybody has seen it.
     238         890 :     update->AdvertiseReset(base_send_set);
     239         890 :     if (update->advertise().empty()) {
     240         890 :         queue_->Dequeue(update);
     241             :     }
     242             :     // Update may be freed.
     243        1780 :     server_->exporter()->StateUpdateOnDequeue(update, base_send_set,
     244         890 :                                               update->IsDelete());
     245         890 : }
     246             : 
     247             : // blocked_set is a subset of send_set
     248         114 : void IFMapUpdateSender::SendUpdate(BitSet send_set, BitSet *blocked_set) {
     249             :     IFMapClient *client;
     250             :     bool send_result;
     251             : 
     252         114 :     assert(!message_->IsEmpty());
     253             : 
     254         230 :     for (size_t i = send_set.find_first(); i != BitSet::npos;
     255         116 :          i = send_set.find_next(i)) {
     256         116 :         assert(!send_blocked_.test(i));
     257         116 :         client = server_->GetClient(i);
     258         116 :         assert(client);
     259             : 
     260         116 :         message_->SetReceiverInMsg(client->identifier());
     261             :         // Close the message to save the document as string
     262         116 :         message_->Close();
     263             : 
     264             :         // Send the string version of the message to the client.
     265         116 :         send_result = client->SendUpdate(message_->get_string());
     266             : 
     267             :         // Keep track of all the clients whose buffers are full.
     268         116 :         if (!send_result) {
     269           0 :             blocked_set->set(i);
     270           0 :             send_blocked_.set(i);
     271             :         }
     272             :     }
     273             :     // Reset the message to init things for the next message
     274         114 :     message_->Reset();
     275         114 : }
     276             : 
     277             : // marker is before next_marker in the Q. next_marker could be the tail_marker.
     278             : // 'done' is set to true only if all the clients in the union of the
     279             : // client-sets of the 2 markers are blocked.
     280           0 : IFMapMarker* IFMapUpdateSender::ProcessMarker(IFMapMarker *marker,
     281             :                                               IFMapMarker *next_marker,
     282             :                                               bool *done) {
     283             :     // There should never be a marker beyond the tail_marker
     284           0 :     assert(marker != queue_->tail_marker());
     285             : 
     286             :     // Get the union (total_set) of the client-sets in the 2 markers. Then, get
     287             :     // the subset of clients in the union that are blocked (blocked_set). The
     288             :     // remaining subset of clients are ready (ready_set).
     289           0 :     BitSet total_set = (marker->mask | next_marker->mask);
     290           0 :     BitSet blocked_set = (total_set & send_blocked_);
     291           0 :     BitSet ready_set;
     292           0 :     ready_set.BuildComplement(total_set, blocked_set); // *this = lhs & ~rhs
     293             : 
     294             :     // If all the clients are ready or all are blocked, merge marker into
     295             :     // next_marker. marker will be deleted.
     296           0 :     if (blocked_set.empty() || ready_set.empty()) {
     297           0 :         IFMAP_UPD_SENDER_TRACE(IFMapUSMarkerMerge, "Merging", marker->ToString(),
     298             :             "into", next_marker->ToString());
     299           0 :         queue_->MarkerMerge(next_marker, marker, marker->mask);
     300           0 :         assert(next_marker->mask == total_set);
     301             :     } else {
     302             :         // We have both, ready and blocked, clients. First, merge both the
     303             :         // markers into next_marker so that next_marker has the total_set. Then
     304             :         // split next_marker into 2 markers: first with the blocked_set and the
     305             :         // second with the ready_set, with first(blocked) preceding the
     306             :         // second(ready).
     307           0 :         IFMAP_UPD_SENDER_TRACE(IFMapUSMarkerMerge, "Merging", marker->ToString(),
     308             :             "into", next_marker->ToString());
     309           0 :         queue_->MarkerMerge(next_marker, marker, marker->mask);
     310           0 :         assert(next_marker->mask == total_set);
     311           0 :         IFMAP_UPD_SENDER_TRACE(IFMapUSMarkerSplit, "Splitting blocked clients",
     312             :             blocked_set.ToNumberedString(), "from", next_marker->ToString());
     313           0 :         queue_->MarkerSplitBefore(next_marker, next_marker, blocked_set);
     314             :     }
     315           0 :     if (ready_set.empty()) {
     316             :         // If all the clients are blocked, we are done.
     317           0 :         *done = true;
     318             :     } else {
     319             :         // Atleast some clients are ready to continue.
     320           0 :         *done = false;
     321             :     }
     322             : 
     323             :     // next_marker has the ready_set if done is false
     324           0 :     return next_marker;
     325           0 : }
     326             : 
     327         890 : void IFMapUpdateSender::LogAndCountSentUpdate(IFMapUpdate *update,
     328             :                                               const BitSet &base_send_set) {
     329         890 :     size_t total = base_send_set.count();
     330             :     // Avoid dealing with return value of BitSet::npos
     331         890 :     if (total) {
     332         890 :         string name = update->ConfigName();
     333         890 :         string operation = update->TypeToString();
     334         890 :         size_t client_id = base_send_set.find_first();
     335        1782 :         while (total--) {
     336         892 :             IFMapClient *client = server_->GetClient(client_id);
     337         892 :             if (client) {
     338         892 :                 IFMAP_DEBUG_ONLY(IFMapClientSendInfo, operation, name,
     339             :                                  client->identifier(), client->name());
     340         892 :                 if (update->IsNode()) {
     341         434 :                     if (update->IsUpdate()) {
     342         434 :                         client->incr_update_nodes_sent();
     343           0 :                     } else if (update->IsDelete()) {
     344           0 :                         client->incr_delete_nodes_sent();
     345             :                     } else {
     346           0 :                         assert(0);
     347             :                     }
     348         458 :                 } else if (update->IsLink()) {
     349         458 :                     if (update->IsUpdate()) {
     350         458 :                         client->incr_update_links_sent();
     351           0 :                     } else if (update->IsDelete()) {
     352           0 :                         client->incr_delete_links_sent();
     353             :                     } else {
     354           0 :                         assert(0);
     355             :                     }
     356             :                 }
     357             :             }
     358         892 :             client_id = base_send_set.find_next(client_id);
     359             :         }
     360         890 :     }
     361         890 : }
     362             : 

Generated by: LCOV version 1.14