LCOV - code coverage report
Current view: top level - db - db_table.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 334 354 94.4 %
Date: 2026-11-09 02:10:49 Functions: 64 71 90.1 %
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 <vector>
       7             : #include <atomic>
       8             : 
       9             : #include <tbb/spin_rw_mutex.h>
      10             : 
      11             : #include <boost/bind/bind.hpp>
      12             : #include <boost/foreach.hpp>
      13             : #include <boost/dynamic_bitset.hpp>
      14             : #include <boost/type_traits.hpp>
      15             : 
      16             : #include "base/compiler.h"
      17             : #include "base/logging.h"
      18             : #include "base/task_annotations.h"
      19             : #include "base/time_util.h"
      20             : #include "db/db.h"
      21             : #include "db/db_partition.h"
      22             : #include "db/db_table.h"
      23             : #include "db/db_table_partition.h"
      24             : #include "db/db_table_walk_mgr.h"
      25             : #include "db/db_types.h"
      26             : 
      27             : class DBEntry;
      28             : class DBEntryBase;
      29             : 
      30             : using namespace std;
      31             : using namespace boost::placeholders;
      32             : 
      33     1320144 : DBRequest::DBRequest() : oper(static_cast<DBOperation>(0)) {
      34     1320133 : }
      35             : 
      36     1348255 : DBRequest::~DBRequest() {
      37             : #if defined(__GNUC__)
      38             : #if (__GNUC_PREREQ(4, 2) > 0)
      39             :     boost::has_virtual_destructor<DBRequestKey>::type key_has_destructor;
      40             :     boost::has_virtual_destructor<DBRequestData>::type data_has_destructor;
      41     1348255 :     assert(key_has_destructor && data_has_destructor);
      42             : #endif
      43             : #endif
      44     1348180 : }
      45             : 
      46      665184 : void DBRequest::Swap(DBRequest *rhs) {
      47      665184 :     swap(oper, rhs->oper);
      48      665187 :     swap(key, rhs->key);
      49      665181 :     swap(data, rhs->data);
      50      665161 : }
      51             : 
      52             : // we need copy to be able to resize vector of atomics
      53             : // therefore we don't need the same value in both instances
      54             : template<typename _Tp>
      55             : struct AtomicWithCopy : public std::atomic<_Tp> {
      56             :     // Inherit constructors
      57             :     using std::atomic<_Tp>::atomic;
      58             : 
      59             :     // Bring in base class operators and methods
      60             :     using std::atomic<_Tp>::operator=;
      61             :     using std::atomic<_Tp>::load;
      62             :     using std::atomic<_Tp>::store;
      63             : 
      64             :     // Custom constructor to handle copy from another AtomicWithCopy (must perform a load/store)
      65     1014631 :     AtomicWithCopy(const AtomicWithCopy& other) : std::atomic<_Tp>(other.load()) {}
      66             :     
      67             :     // Custom assignment operator
      68      794462 :     AtomicWithCopy& operator=(const AtomicWithCopy& other) {
      69     1588921 :         this->store(other.load());
      70      794455 :         return *this;
      71             :     }
      72             : };
      73             : 
      74             : class DBTableBase::ListenerInfo {
      75             : public:
      76             :     typedef vector<ChangeCallback> CallbackList;
      77             :     typedef vector<string> NameList;
      78             :     typedef vector<AtomicWithCopy<uint64_t>> StateCountList;
      79             : 
      80     1531129 :     explicit ListenerInfo(const string &table_name) :
      81     1531129 :         db_state_accounting_(true) {
      82     1531048 :         if (table_name.find("__ifmap_") != string::npos) {
      83             :             // TODO need to have unconditional DB state accounting
      84             :             // for now skipp DB State accounting for ifmap tables
      85     1233751 :             db_state_accounting_ = false;
      86             :         }
      87     1531074 :     }
      88             : 
      89      795028 :     DBTableBase::ListenerId Register(ChangeCallback callback,
      90             :         const string &name) {
      91      795028 :         tbb::spin_rw_mutex::scoped_lock write_lock(rw_mutex_, true);
      92      795090 :         size_t i = bmap_.find_first();
      93      794844 :         if (i == bmap_.npos) {
      94      794401 :             i = callbacks_.size();
      95      794394 :             callbacks_.push_back(callback);
      96      794492 :             names_.push_back(name);
      97      794477 :             state_count_.resize(i + 1);
      98      794479 :             state_count_[i] = AtomicWithCopy<uint64_t>(0);
      99             :         } else {
     100         443 :             bmap_.reset(i);
     101         444 :             if (bmap_.none()) {
     102         323 :                 bmap_.clear();
     103             :             }
     104         444 :             callbacks_[i] = callback;
     105         444 :             names_[i] = name;
     106         444 :             state_count_[i] = 0;
     107             :         }
     108      795127 :         return i;
     109      795175 :     }
     110             : 
     111      795111 :     void Unregister(ListenerId listener) {
     112      795111 :         tbb::spin_rw_mutex::scoped_lock write_lock(rw_mutex_, true);
     113      795112 :         callbacks_[listener] = NULL;
     114      795110 :         names_[listener] = "";
     115             :         // During Unregister Listener should have cleaned up,
     116             :         // DB states from all the entries in this table.
     117      795111 :         assert(state_count_[listener] == 0);
     118      795111 :         if ((size_t) listener == callbacks_.size() - 1) {
     119     1259937 :             while (!callbacks_.empty() && callbacks_.back() == NULL) {
     120      794561 :                 callbacks_.pop_back();
     121      794560 :                 names_.pop_back();
     122      794560 :                 state_count_.pop_back();
     123             :             }
     124      465377 :             if (bmap_.size() > callbacks_.size()) {
     125      226141 :                 bmap_.resize(callbacks_.size());
     126             :             }
     127             :         } else {
     128      329734 :             if ((size_t) listener >= bmap_.size()) {
     129      327580 :                 bmap_.resize(listener + 1);
     130             :             }
     131      329734 :             bmap_.set(listener);
     132             :         }
     133      795111 :     }
     134             : 
     135             :     // concurrency: called from DBPartition task.
     136     2551733 :     void RunNotify(DBTablePartBase *tpart, DBEntryBase *entry) {
     137     2551733 :         tbb::spin_rw_mutex::scoped_lock read_lock(rw_mutex_, false);
     138     2552288 :         for (CallbackList::iterator iter = callbacks_.begin();
     139     8903960 :              iter != callbacks_.end(); ++iter) {
     140     6350646 :             if (*iter != NULL) {
     141     6322877 :                 ChangeCallback cb = *iter;
     142     6322028 :                 (cb)(tpart, entry);
     143     6324856 :             }
     144             :         }
     145     2551502 :     }
     146             : 
     147     3911801 :     void AddToDBStateCount(ListenerId listener, int count) {
     148     3911801 :         if (db_state_accounting_ && listener != DBTableBase::kInvalidId) {
     149     3695123 :             tbb::spin_rw_mutex::scoped_lock read_lock(rw_mutex_, false);
     150     3696023 :             state_count_[listener] += count;
     151     3696965 :         }
     152     3913309 :     }
     153             : 
     154     1312455 :     uint64_t GetDBStateCount(ListenerId listener) {
     155     1312455 :         assert(db_state_accounting_ && listener != DBTableBase::kInvalidId);
     156     1312469 :         tbb::spin_rw_mutex::scoped_lock read_lock(rw_mutex_, false);
     157     2625438 :         return state_count_[listener];
     158     1312593 :     }
     159             : 
     160        6527 :     void FillListeners(vector<ShowTableListener> *listeners) const {
     161        6527 :         tbb::spin_rw_mutex::scoped_lock read_lock(rw_mutex_, false);
     162        6630 :         ListenerId id = 0;
     163        6630 :         for (CallbackList::const_iterator iter = callbacks_.begin();
     164       15807 :              iter != callbacks_.end(); ++iter, ++id) {
     165        9219 :             if (*iter != NULL) {
     166        9213 :                 ShowTableListener item;
     167        9208 :                 item.id = id;
     168        9208 :                 item.name = names_[id];
     169        9221 :                 item.state_count = state_count_[id];
     170        9162 :                 listeners->push_back(item);
     171        9191 :             }
     172             :         }
     173        6576 :     }
     174             : 
     175     3766062 :     bool empty() const {
     176     3766062 :         tbb::spin_rw_mutex::scoped_lock read_lock(rw_mutex_, false);
     177     7532642 :         return callbacks_.empty();
     178     3766088 :     }
     179             : 
     180       10317 :     size_t size() const {
     181       10317 :         tbb::spin_rw_mutex::scoped_lock read_lock(rw_mutex_, false);
     182       20634 :         return (callbacks_.size() - bmap_.count());
     183       10317 :     }
     184             : 
     185             : private:
     186             :     bool db_state_accounting_;
     187             :     CallbackList callbacks_;
     188             :     NameList names_;
     189             :     StateCountList state_count_;
     190             :     mutable tbb::spin_rw_mutex rw_mutex_;
     191             :     boost::dynamic_bitset<> bmap_;      // free list.
     192             : };
     193             : 
     194     1531121 : DBTableBase::DBTableBase(DB *db, const string &name)
     195     1531100 :         : db_(db), name_(name), info_(new ListenerInfo(name)),
     196     1531121 :           enqueue_count_(0), input_count_(0), notify_count_(0) {
     197     1531035 :     walker_count_ = 0;
     198     1531232 :     walk_request_count_ = 0;
     199     1531194 :     walk_complete_count_ = 0;
     200     1531222 :     walk_cancel_count_ = 0;
     201     1531228 :     walk_again_count_ = 0;
     202     1531231 :     walk_count_ = 0;
     203     1531202 : }
     204             : 
     205     1531237 : DBTableBase::~DBTableBase() {
     206     1531237 : }
     207             : 
     208      795104 : DBTableBase::ListenerId DBTableBase::Register(ChangeCallback callback,
     209             :     const string &name) {
     210      795104 :     return info_->Register(callback, name);
     211             : }
     212             : 
     213      795111 : void DBTableBase::Unregister(ListenerId listener) {
     214      795111 :     info_->Unregister(listener);
     215             :     // If a table is marked for deletion, then we may trigger the deletion
     216             :     // process when the last client is removed
     217      795112 :     if (info_->empty())
     218      361766 :         RetryDelete();
     219      795112 : }
     220             : 
     221      661588 : bool DBTableBase::Enqueue(DBRequest *req) {
     222      661588 :     DBTablePartBase *tpart = GetTablePartition(req->key.get());
     223      661577 :     DBPartition *partition = db_->GetPartition(tpart->index());
     224      661576 :     enqueue_count_++;
     225      661576 :     return partition->EnqueueRequest(tpart, NULL, req);
     226             : }
     227             : 
     228      372953 : void DBTableBase::EnqueueRemove(DBEntryBase *db_entry) {
     229      372953 :     DBTablePartBase *tpart = GetTablePartition(db_entry);
     230      372887 :     DBPartition *partition = db_->GetPartition(tpart->index());
     231      372875 :     partition->EnqueueRemove(tpart, db_entry);
     232      373003 : }
     233             : 
     234     2551860 : void DBTableBase::RunNotify(DBTablePartBase *tpart, DBEntryBase *entry) {
     235     2551860 :     notify_count_++;
     236     2551860 :     info_->RunNotify(tpart, entry);
     237     2552335 : }
     238             : 
     239     3912402 : void DBTableBase::AddToDBStateCount(ListenerId listener, int count) {
     240     3912402 :     info_->AddToDBStateCount(listener, count);
     241     3913310 : }
     242             : 
     243     1312524 : uint64_t DBTableBase::GetDBStateCount(ListenerId listener) {
     244     1312524 :     return info_->GetDBStateCount(listener);
     245             : }
     246             : 
     247      289041 : bool DBTableBase::MayDelete() const {
     248      289041 :     if (HasListeners()) {
     249           0 :         return false;
     250             :     }
     251      289041 :     if (HasWalkers()) {
     252           0 :         return false;
     253             :     }
     254      289041 :     if (!empty()) {
     255           0 :         return false;
     256             :     }
     257             : 
     258      289041 :     return true;
     259             : }
     260             : 
     261     2971031 : bool DBTableBase::HasListeners() const {
     262     2971031 :     return !info_->empty();
     263             : }
     264             : 
     265       10317 : size_t DBTableBase::GetListenerCount() const {
     266       10317 :     return info_->size();
     267             : }
     268             : 
     269        6527 : void DBTableBase::FillListeners(vector<ShowTableListener> *listeners) const {
     270        6527 :     info_->FillListeners(listeners);
     271        6642 : }
     272             : 
     273             : class DBTable::WalkWorker : public Task {
     274             : public:
     275             :     WalkWorker(TableWalker *walker, int db_partition_id);
     276             : 
     277             :     virtual bool Run();
     278             : 
     279           0 :     std::string Description() const { return "DBTable::WalkWorker"; }
     280             : 
     281             : private:
     282             :     // Store the last visited node to continue walk
     283             :     std::unique_ptr<DBRequestKey> walk_ctx_;
     284             : 
     285             :     // Table partition for which this worker was created
     286             :     DBTablePartition *tbl_partition_;
     287             : 
     288             :     TableWalker *walker_;
     289             : };
     290             : 
     291             : class DBTable::TableWalker {
     292             : public:
     293     1531176 :     TableWalker(DBTable *table) : table_(table) {
     294     1531118 :         pending_workers_ = 0;
     295     1531224 :     }
     296             : 
     297             :     void StartWalk();
     298             : 
     299     1061279 :     DBTable *table() {
     300     1061279 :         return table_;
     301             :     }
     302             : 
     303      227844 :     void ClearWalkWorks() {
     304      227844 :         worker_tasks_.clear();
     305      227844 :     }
     306             : 
     307             :     DBTable *table_;
     308             :     // check whether iteration is completed on all Table Partition
     309             :     std::atomic<uint16_t> pending_workers_;
     310             :     // For debugging purpose. Few of the tasks in this list could has finished
     311             :     // executing and destroyed. List of workers are useful in debugging with
     312             :     // gdb/gcore to see the current state of the walk and walk_context
     313             :     std::list<Task *> worker_tasks_;
     314             : };
     315             : 
     316      796679 : bool DBTable::WalkWorker::Run() {
     317      796679 :     int count = 0;
     318      796679 :     DBRequestKey *key_resume = walk_ctx_.get();
     319      796686 :     DBTable *table = walker_->table();
     320      796676 :     int max_walk_entry_count = table->GetWalkIterationToYield();
     321             :     DBEntry *entry;
     322             : 
     323      796675 :     if (key_resume != NULL) {
     324      664423 :         std::unique_ptr<const DBEntryBase> start;
     325      664423 :         start = table->AllocEntry(key_resume);
     326             :         // Find matching or next in sort order
     327      664238 :         entry = tbl_partition_->lower_bound(start.get());
     328      664560 :     } else {
     329      132252 :         entry = tbl_partition_->GetFirst();
     330             :     }
     331      796590 :     if (entry == NULL) {
     332         557 :         goto walk_done;
     333             :     }
     334             : 
     335     1592099 :     for (DBEntry *next = NULL; entry; entry = next) {
     336     1460311 :         next = tbl_partition_->GetNext(entry);
     337     1460488 :         if (count == max_walk_entry_count) {
     338             :             // store the context
     339      664491 :             walk_ctx_ = entry->GetDBRequestKey();
     340      663628 :             return false;
     341             :         }
     342             : 
     343             :         // Invoke walker function
     344      795997 :         bool more = table->InvokeWalkCb(tbl_partition_, entry);
     345      796161 :         if (!more) {
     346         104 :             break;
     347             :         }
     348             : 
     349      796057 :         db_walker_wait();
     350      796066 :         count++;
     351             :     }
     352             : 
     353      131788 : walk_done:
     354             :     // Check whether all other walks on the table is completed
     355      132449 :     long num_walkers_on_tpart = walker_->pending_workers_.fetch_sub(1);
     356      132449 :     if (num_walkers_on_tpart == 1) {
     357      107744 :         table->WalkDone();
     358             :     }
     359      132298 :     return true;
     360             : }
     361             : 
     362      132298 : DBTable::WalkWorker::WalkWorker(TableWalker *walker, int db_partition_id)
     363      132298 :     : Task(walker->table()->GetWalkerTaskId(), db_partition_id), walker_(walker) {
     364      132298 :     tbl_partition_ = static_cast<DBTablePartition *>
     365      132298 :         (walker_->table()->GetTablePartition(db_partition_id));
     366      132298 : }
     367             : 
     368      227844 : void DBTable::TableWalker::StartWalk() {
     369      227844 :     CHECK_CONCURRENCY("db::Walker");
     370      227844 :     assert(pending_workers_ == 0);
     371      740691 :     for (int i = 0; i < table_->PartitionCount(); i++) {
     372             :         DBTablePartition *partition = static_cast<DBTablePartition *>(
     373      512847 :             table_->GetTablePartition(i));
     374      512847 :         if (!partition->size()) continue;
     375      132298 :         worker_tasks_.push_back(new WalkWorker(this, i));
     376      132298 :         pending_workers_++;
     377             :     }
     378      227844 :     if (pending_workers_ == 0) {
     379      120100 :         table_->WalkDone();
     380             :     } else {
     381      107744 :         TaskScheduler *scheduler = TaskScheduler::GetInstance();
     382      240042 :         for (auto *task : worker_tasks_) scheduler->Enqueue(task);
     383             :     }
     384      227844 : }
     385             : 
     386             : ///////////////////////////////////////////////////////////
     387             : // Implementation of DBTable methods
     388             : ///////////////////////////////////////////////////////////
     389     1531123 : DBTable::DBTable(DB *db, const string &name)
     390             :     : DBTableBase(db, name),
     391     1531198 :       walker_(new TableWalker(this)),
     392     3062206 :       walker_task_id_(db->task_id()) {
     393             : 
     394             :     static bool init_ = false;
     395             :     static int iter_to_yield_env_ = 0;
     396             : 
     397     1531038 :     if (!init_) {
     398             :         // XXX To be used for testing purposes only.
     399         159 :         char *count_str = getenv("DB_ITERATION_TO_YIELD");
     400         159 :         if (count_str) {
     401         159 :             iter_to_yield_env_ = strtol(count_str, NULL, 0);
     402             :         } else {
     403           0 :             iter_to_yield_env_ = kIterationToYield;
     404             :         }
     405         159 :         init_ = true;
     406             :     }
     407     1531038 :     max_walk_iteration_to_yield_ = iter_to_yield_env_;
     408     1531038 : }
     409             : 
     410     1531237 : DBTable::~DBTable() {
     411     1531237 :     STLDeleteValues(&partitions_);
     412     1531237 : }
     413             : 
     414     1523027 : void DBTable::Init() {
     415     3599195 :     for (int i = 0; i < PartitionCount(); i++) {
     416     2076376 :         partitions_.push_back(AllocPartition(i));
     417             :     }
     418     1522749 : }
     419             : 
     420     2076294 : DBTablePartition *DBTable::AllocPartition(int index) {
     421     2076294 :     return new DBTablePartition(this, index);
     422             : }
     423             : 
     424      227844 : void DBTable::StartWalk() {
     425      227844 :     CHECK_CONCURRENCY("db::Walker");
     426      227844 :     incr_walk_count();
     427      227844 :     walker_->StartWalk();
     428      227844 : }
     429             : 
     430      212750 : DBEntry *DBTable::Add(const DBRequest *req) {
     431      212750 :     return AllocEntry(req->key.get()).release();
     432             : }
     433             : 
     434        1323 : void DBTable::Change(DBEntryBase *entry) {
     435        1323 :     DBTablePartBase *tpart = GetTablePartition(entry);
     436        1323 :     tpart->Notify(entry);
     437        1323 : }
     438             : 
     439           0 : bool DBTable::OnChange(DBEntry *entry, const DBRequest *req) {
     440           0 :     return true;
     441             : }
     442             : 
     443           0 : bool DBTable::Delete(DBEntry *entry, const DBRequest *req) {
     444           0 :     return true;
     445             : }
     446             : 
     447     1411810 : int DBTable::PartitionCount() const {
     448     1411810 :     return DB::PartitionCount();
     449             : }
     450             : 
     451    16600058 : static size_t HashToPartition(size_t hash) {
     452    16600058 :     return hash % DB::PartitionCount();
     453             : }
     454             : 
     455    20074671 : DBTablePartBase *DBTable::GetTablePartition(const int index) {
     456    20074671 :     return partitions_[index];
     457             : }
     458             : 
     459       24089 : const DBTablePartBase *DBTable::GetTablePartition(const int index) const {
     460       24089 :     return partitions_[index];
     461             : }
     462             : 
     463      751418 : DBTablePartBase *DBTable::GetTablePartition(const DBRequestKey *key) {
     464      751418 :     int id = HashToPartition(Hash(key));
     465      751400 :     return GetTablePartition(id);
     466             : }
     467             : 
     468           0 : const DBTablePartBase *DBTable::GetTablePartition(
     469             :         const DBRequestKey *key) const {
     470           0 :     int id = HashToPartition(Hash(key));
     471           0 :     return GetTablePartition(id);
     472             : }
     473             : 
     474    15269854 : DBTablePartBase *DBTable::GetTablePartition(const DBEntryBase *entry) {
     475    15269854 :     const DBEntry *gentry = static_cast<const DBEntry *>(entry);
     476    15269854 :     size_t id = HashToPartition(Hash(gentry));
     477    15262350 :     return GetTablePartition(id);
     478             : }
     479             : 
     480       24089 : const DBTablePartBase *DBTable::GetTablePartition(
     481             :         const DBEntryBase *entry) const {
     482       24089 :     const DBEntry *gentry = static_cast<const DBEntry *>(entry);
     483       24089 :     size_t id = HashToPartition(Hash(gentry));
     484       24089 :     return GetTablePartition(id);
     485             : }
     486             : 
     487             : // Find DB Entry without taking lock. Calling routine must ensure its
     488             : // running in exclusion with DB task
     489         117 : DBEntry *DBTable::FindNoLock(const DBEntry *entry) {
     490         117 :     size_t id = HashToPartition(Hash(entry));
     491             :     DBTablePartition *tbl_partition =
     492         117 :         static_cast<DBTablePartition *>(GetTablePartition(id));
     493         117 :     return tbl_partition->FindNoLock(entry);
     494             : }
     495             : 
     496      396549 : DBEntry *DBTable::Find(const DBEntry *entry) {
     497      396549 :     size_t id = HashToPartition(Hash(entry));
     498             :     DBTablePartition *tbl_partition =
     499      396546 :         static_cast<DBTablePartition *>(GetTablePartition(id));
     500      396545 :     return tbl_partition->Find(entry);
     501             : }
     502             : 
     503           0 : const DBEntry *DBTable::Find(const DBEntry *entry) const {
     504           0 :     return const_cast<DBTable *>(this)->Find(entry);
     505             : }
     506             : 
     507             : // Find DB Entry without taking lock. Calling routine must ensure its
     508             : // running in exclusion with DB task
     509          36 : DBEntry *DBTable::FindNoLock(const DBRequestKey *key) {
     510          36 :     int id = HashToPartition(Hash(key));
     511             :     DBTablePartition *tbl_partition =
     512          36 :     static_cast<DBTablePartition *>(GetTablePartition(id));
     513          36 :     return tbl_partition->FindNoLock(key);
     514             : }
     515             : 
     516      166261 : DBEntry *DBTable::Find(const DBRequestKey *key, int id) {
     517      166261 :     if (id == -1)
     518      166233 :         id = HashToPartition(Hash(key));
     519             :     DBTablePartition *tbl_partition =
     520      166261 :     static_cast<DBTablePartition *>(GetTablePartition(id));
     521      166261 :     return tbl_partition->Find(key);
     522             : }
     523             : 
     524         260 : const DBEntry *DBTable::Find(const DBRequestKey *key, int id) const {
     525         260 :     return const_cast<DBTable *>(this)->Find(key, id);
     526             : }
     527             : 
     528             : //
     529             : // Concurrency: called from task that's mutually exclusive with db::DBTable
     530             : // or db::IFMapTable as applicable.
     531             : //
     532             : // Calculate the size across all partitions.
     533             : //
     534     1897266 : size_t DBTable::Size() const {
     535     1897266 :     size_t total = 0;
     536     1897266 :     for (vector<DBTablePartition *>::const_iterator iter = partitions_.begin();
     537     6999803 :          iter != partitions_.end(); iter++) {
     538     5102609 :         total += (*iter)->size();
     539             :     }
     540     1897219 :     return total;
     541             : }
     542             : 
     543       18558 : void DBTable::Input(DBTablePartition *tbl_partition, DBClient *client,
     544             :                     DBRequest *req) {
     545             :     DBRequestKey *key =
     546       18558 :         static_cast<DBRequestKey *>(req->key.get());
     547       18558 :     DBEntry *entry = NULL;
     548             : 
     549       18558 :     entry = tbl_partition->Find(key);
     550       18558 :     if (req->oper == DBRequest::DB_ENTRY_ADD_CHANGE) {
     551       13713 :         if (entry) {
     552       10417 :             if (OnChange(entry, req) || entry->IsDeleted()) {
     553             :                 // The entry may currently be marked as deleted.
     554         658 :                 entry->ClearDelete();
     555         658 :                 tbl_partition->Change(entry);
     556             :             }
     557             :         } else {
     558        3296 :             if ((entry = Add(req)) != NULL) {
     559        3296 :                 tbl_partition->Add(entry);
     560             :             }
     561             :         }
     562        4845 :     } else if (req->oper == DBRequest::DB_ENTRY_DELETE) {
     563        3436 :         if (entry) {
     564        3436 :             if (Delete(entry, req)) {
     565        3436 :                 tbl_partition->Delete(entry);
     566             :             }
     567             :         }
     568        1409 :     } else if (req->oper == DBRequest::DB_ENTRY_NOTIFY) {
     569        1409 :         if (entry) {
     570        1409 :             tbl_partition->Notify(entry);
     571             :         }
     572             :     } else {
     573           0 :         assert(0);
     574             :     }
     575       18558 : }
     576             : 
     577           2 : void DBTable::DBStateClear(DBTable *table, ListenerId id) {
     578           2 :     DBEntryBase *next = NULL;
     579             : 
     580           4 :     for (int i = 0; i < table->PartitionCount(); ++i) {
     581             :         DBTablePartition *partition = static_cast<DBTablePartition *>(
     582           2 :             table->GetTablePartition(i));
     583             : 
     584           2 :         for (DBEntryBase *entry = partition->GetFirst(); entry; entry = next) {
     585           0 :             next = partition->GetNext(entry);
     586           0 :             DBState *state = entry->GetState(table, id);
     587           0 :             if (state) {
     588           0 :                 entry->ClearState(table, id);
     589           0 :                 delete state;
     590             :             }
     591             :         }
     592             :     }
     593           2 : }
     594             : 
     595             : //
     596             : // Callback for table walk triggered by NotifyAllEntries.
     597             : //
     598      129699 : bool DBTable::WalkCallback(DBTablePartBase *tpart, DBEntryBase *entry) {
     599      129699 :     tpart->Notify(entry);
     600      129701 :     return true;
     601             : }
     602             : 
     603             : //
     604             : // Callback for completion of table walk triggered by NotifyAllEntries.
     605             : //
     606       61312 : void DBTable::WalkCompleteCallback(DBTableBase *tbl_base) {
     607       61312 :     walk_ref_.reset();
     608       61312 : }
     609             : 
     610             : //
     611             : // Concurrency: called from task that's mutually exclusive with db::DBTable
     612             : // or db::IFMapTable as applicable.
     613             : //
     614             : // Trigger notification of all entries to all listeners.
     615             : // Should be used sparingly e.g. to handle significant configuration change.
     616             : //
     617             : // The walk callback just turns around and puts the DBentryBase on the change
     618             : // list.
     619             : //
     620             : // If the walk is already running, it is allowed to complete and WalkAgain API
     621             : // is invoked to trigger walk on current walk completion.
     622             : //
     623       63091 : void DBTable::NotifyAllEntries() {
     624       63091 :     CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper", "bgp::RTFilter",
     625             :                       "db::DBTable");
     626             : 
     627       63090 :     if (empty())
     628        1773 :         return;
     629             : 
     630       61318 :     if (walk_ref_ == NULL) {
     631             :         walk_ref_ =
     632      122624 :             AllocWalker(boost::bind(&DBTable::WalkCallback, this, _1, _2),
     633       61312 :                     boost::bind(&DBTable::WalkCompleteCallback, this, _2));
     634       61312 :         WalkTable(walk_ref_);
     635             :     } else {
     636           6 :         WalkAgain(walk_ref_);
     637             :     }
     638             : }
     639             : 
     640      222230 : DBTable::DBTableWalkRef DBTable::AllocWalker(WalkFn walk_fn,
     641             :                                              WalkCompleteFn walk_complete) {
     642      222230 :     DBTableWalkMgr *walk_mgr = database()->GetWalkMgr();
     643      222230 :     return walk_mgr->AllocWalker(this, walk_fn, walk_complete);
     644             : }
     645             : 
     646      158931 : void DBTable::ReleaseWalker(DBTable::DBTableWalkRef &walk) {
     647      158931 :     DBTableWalkMgr *walk_mgr = database()->GetWalkMgr();
     648      158931 :     walk_mgr->ReleaseWalker(walk);
     649      158931 :     return;
     650             : }
     651             : 
     652      226980 : void DBTable::WalkTable(DBTable::DBTableWalkRef walk) {
     653      226980 :     DBTableWalkMgr *walk_mgr = database()->GetWalkMgr();
     654      226980 :     walk_mgr->WalkTable(walk);
     655      226980 :     return;
     656             : }
     657             : 
     658       31928 : void DBTable::WalkAgain(DBTable::DBTableWalkRef walk) {
     659       31928 :     DBTableWalkMgr *walk_mgr = database()->GetWalkMgr();
     660       31928 :     walk_mgr->WalkAgain(walk);
     661       31928 :     return;
     662             : }
     663             : 
     664      796206 : bool DBTable::InvokeWalkCb(DBTablePartBase *part, DBEntryBase *entry) {
     665      796206 :     DBTableWalkMgr *walk_mgr = database()->GetWalkMgr();
     666      796051 :     return walk_mgr->InvokeWalkCb(part, entry);
     667             : }
     668             : 
     669      227844 : void DBTable::WalkDone() {
     670      227844 :     incr_walk_complete_count();
     671      227844 :     walker_->ClearWalkWorks();
     672      227844 :     DBTableWalkMgr *walk_mgr = database()->GetWalkMgr();
     673      227844 :     return walk_mgr->WalkDone();
     674             : }

Generated by: LCOV version 1.14