LCOV - code coverage report
Current view: top level - db - db_partition.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 140 157 89.2 %
Date: 2026-11-09 02:10:49 Functions: 28 35 80.0 %
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 "db/db_partition.h"
       7             : 
       8             : #include <list>
       9             : #include <mutex>
      10             : #include <atomic>
      11             : 
      12             : #include <tbb/concurrent_queue.h>
      13             : 
      14             : #include "base/task.h"
      15             : #include "db/db.h"
      16             : #include "db/db_client.h"
      17             : #include "db/db_entry.h"
      18             : 
      19             : using tbb::concurrent_queue;
      20             : 
      21             : struct RequestQueueEntry {
      22             :     // Constructor takes ownership of DBRequest key, data.
      23     1166277 :     RequestQueueEntry(DBTablePartBase *tpart, DBClient *client, DBRequest *req)
      24     1166277 :         : tpart(tpart), client(client) {
      25     1166271 :         request.Swap(req);
      26     1166220 :     }
      27             :     DBTablePartBase *tpart;
      28             :     DBClient *client;
      29             :     DBRequest request;
      30             : };
      31             : 
      32             : struct RemoveQueueEntry {
      33      524740 :     RemoveQueueEntry(DBTablePartBase *tpart, DBEntryBase *db_entry)
      34      524740 :         : tpart(tpart), db_entry(db_entry) {
      35      524740 :     }
      36             :     DBTablePartBase *tpart;
      37             :     DBEntryBase *db_entry;
      38             : };
      39             : 
      40             : class DBPartition::WorkQueue {
      41             : public:
      42             :     static const int kThreshold = 1024;
      43             :     typedef concurrent_queue<RequestQueueEntry *> RequestQueue;
      44             :     typedef concurrent_queue<RemoveQueueEntry *> RemoveQueue;
      45             :     typedef std::list<DBTablePartBase *> TablePartList;
      46             : 
      47      109374 :     explicit WorkQueue(DBPartition *partition, int partition_id)
      48      109374 :         : db_partition_(partition),
      49      109374 :           db_partition_id_(partition_id),
      50      109374 :           disable_(false),
      51      109374 :           running_(false) {
      52      109374 :         request_count_ = 0;
      53      109374 :         max_request_queue_len_ = 0;
      54      109374 :         total_request_count_ = 0;
      55      109374 :     }
      56      109310 :     ~WorkQueue() {
      57      109310 :         for (RequestQueue::iterator iter = request_queue_.unsafe_begin();
      58      109310 :              iter != request_queue_.unsafe_end();) {
      59           0 :             RequestQueueEntry *req_entry = *iter;
      60           0 :             ++iter;
      61           0 :             delete req_entry;
      62             :         }
      63      109310 :         request_queue_.clear();
      64      109310 :     }
      65             : 
      66     1166234 :     bool EnqueueRequest(RequestQueueEntry *req_entry) {
      67     1166234 :         request_queue_.push(req_entry);
      68     1166303 :         MaybeStartRunner();
      69     1166331 :         uint32_t max = request_count_.fetch_add(1);
      70     1166331 :         if (max > max_request_queue_len_)
      71      160521 :             max_request_queue_len_ = max;
      72     1166331 :         total_request_count_++;
      73     1166331 :         return max < (kThreshold - 1);
      74             : 
      75             :     }
      76             : 
      77     2014940 :     bool DequeueRequest(RequestQueueEntry **req_entry) {
      78     2014940 :         bool success = request_queue_.try_pop(*req_entry);
      79     2014942 :         if (success) {
      80     1166318 :             request_count_.fetch_sub(1);
      81             :         }
      82     2014942 :         return success;
      83             :     }
      84             : 
      85      524858 :     void EnqueueRemove(RemoveQueueEntry *rm_entry) {
      86      524858 :         remove_queue_.push(rm_entry);
      87      524829 :         MaybeStartRunner();
      88      524869 :     }
      89             : 
      90     1378090 :     bool DequeueRemove(RemoveQueueEntry **rm_entry) {
      91     1378090 :         bool success = remove_queue_.try_pop(*rm_entry);
      92     1378119 :         return success;
      93             :     }
      94             : 
      95             :     void MaybeStartRunnerUnlocked();
      96             :     void MaybeStartRunner();
      97             :     bool RunnerDone();
      98             : 
      99             :     // Normally called from single task that either runs in DB context or is
     100             :     // exclusive with DB task, but can be called concurrently from multiple
     101             :     // bgp::ConfigHelper tasks.
     102     1688681 :     void SetActive(DBTablePartBase *tpart) {
     103     1688681 :         std::scoped_lock lock(mutex_);
     104     1688920 :         change_list_.push_back(tpart);
     105     1688558 :         MaybeStartRunnerUnlocked();
     106     1688672 :     }
     107             : 
     108     2537381 :     DBTablePartBase *GetActiveTable() {
     109     2537381 :         DBTablePartBase *tpart = NULL;
     110     2537381 :         if (!change_list_.empty()) {
     111     1688838 :             tpart = change_list_.front();
     112     1688781 :             change_list_.pop_front();
     113             :         }
     114     2537381 :         return tpart;
     115             :     }
     116             : 
     117      801582 :     int db_partition_id() {
     118      801582 :         return db_partition_id_;
     119             :     }
     120             : 
     121      801577 :     int db_task_id() const { return db_partition_->task_id(); }
     122             : 
     123     6254074 :     bool IsDBQueueEmpty() const {
     124     6254074 :         return (request_queue_.empty() && change_list_.empty());
     125             :     }
     126             : 
     127      856721 :     bool disable() { return disable_; }
     128          90 :     void set_disable(bool disable) { disable_ = disable; }
     129             : 
     130           0 :     long request_queue_len() const {
     131           0 :         return request_count_;
     132             :     }
     133             : 
     134           0 :     uint64_t total_request_count() const {
     135           0 :         return total_request_count_;
     136             :     }
     137             : 
     138           0 :     uint64_t max_request_queue_len() const {
     139           0 :         return max_request_queue_len_;
     140             :     }
     141             : 
     142             : private:
     143             :     DBPartition *db_partition_;
     144             :     RequestQueue request_queue_;
     145             :     TablePartList change_list_;
     146             :     std::atomic<long> request_count_;
     147             :     uint64_t total_request_count_;
     148             :     uint64_t max_request_queue_len_;
     149             :     RemoveQueue remove_queue_;
     150             :     std::mutex mutex_;
     151             :     int db_partition_id_;
     152             :     bool disable_;
     153             :     bool running_;
     154             : 
     155             :     DISALLOW_COPY_AND_ASSIGN(WorkQueue);
     156             : };
     157             : 
     158     6254074 : bool DBPartition::IsDBQueueEmpty() const {
     159     6254074 :     return work_queue_->IsDBQueueEmpty();
     160             : }
     161             : 
     162          90 : void DBPartition::SetQueueDisable(bool disable) {
     163          90 :     if (disable) {
     164          45 :         work_queue_->set_disable(true);
     165             :     } else {
     166          45 :         work_queue_->set_disable(false);
     167          45 :         work_queue_->MaybeStartRunner();
     168             :     }
     169          90 : }
     170             : 
     171             : class DBPartition::QueueRunner : public Task {
     172             : public:
     173             :     static const int kMaxIterations = 32;
     174      801586 :     QueueRunner(WorkQueue *queue)
     175      801586 :         : Task(queue->db_task_id(), queue->db_partition_id()),
     176      801586 :           queue_(queue) {
     177      801563 :     }
     178             : 
     179      856719 :     virtual bool Run() {
     180      856719 :         int count = 0;
     181             : 
     182             :         // Skip if the queue is disabled.
     183      856719 :         if (queue_->disable())
     184        1138 :             return queue_->RunnerDone();
     185             : 
     186      855583 :         RemoveQueueEntry *rm_entry = NULL;
     187     1378132 :         while (queue_->DequeueRemove(&rm_entry)) {
     188      524889 :             DBEntryBase *db_entry = rm_entry->db_entry;
     189             :             {
     190             :                 tbb::spin_rw_mutex::scoped_lock
     191      524889 :                     lock(rm_entry->tpart->dbstate_mutex(), false);
     192     1049631 :                 if (!db_entry->IsDeleted() || db_entry->is_onlist() ||
     193      524793 :                     !db_entry->is_state_empty_unlocked(rm_entry->tpart)) {
     194          41 :                     db_entry->ClearOnRemoveQ();
     195          42 :                     db_entry = NULL;
     196             :                 }
     197      524828 :             }
     198      524911 :             if (db_entry) {
     199      524869 :                 rm_entry->tpart->Remove(db_entry);
     200             :             }
     201      524898 :             delete rm_entry;
     202      524907 :             if (++count == kMaxIterations) {
     203        2358 :                 return false;
     204             :             }
     205             :         }
     206             : 
     207      853226 :         RequestQueueEntry *req_entry = NULL;
     208     2014939 :         while (queue_->DequeueRequest(&req_entry)) {
     209     1166287 :             req_entry->tpart->Process(req_entry->client, &req_entry->request);
     210     1166269 :             delete req_entry;
     211     1166287 :             if (++count == kMaxIterations) {
     212        4574 :                 return false;
     213             :             }
     214             :         }
     215             : 
     216             :         while (true) {
     217     2537193 :             DBTablePartBase *tpart = queue_->GetActiveTable();
     218     2537377 :             if (tpart == NULL) {
     219      848613 :                 break;
     220             :             }
     221     1688764 :             bool done = tpart->RunNotify();
     222     1688608 :             if (!done) {
     223          58 :                 return false;
     224             :             }
     225     1688550 :         }
     226             : 
     227             :         // Running is done only if queue_ is empty. It's possible that more
     228             :         // entries are added into in the input or remove queues during the
     229             :         // time we were processing those queues.
     230      848613 :         return queue_->RunnerDone();
     231             :     }
     232             : 
     233           0 :     std::string Description() const {
     234           0 :         return "DBPartition QueueRunner";
     235             :     }
     236             : private:
     237             :     WorkQueue *queue_;
     238             : };
     239             : 
     240     3379707 : void DBPartition::WorkQueue::MaybeStartRunnerUnlocked() {
     241     3379707 :     if (running_) {
     242     2578250 :         return;
     243             :     }
     244      801457 :     running_ = true;
     245      801457 :     QueueRunner *runner = new QueueRunner(this);
     246      801554 :     TaskScheduler *scheduler = TaskScheduler::GetInstance();
     247      801539 :     scheduler->Enqueue(runner);
     248             : }
     249             : 
     250     1691148 : void DBPartition::WorkQueue::MaybeStartRunner() {
     251     1691148 :     std::scoped_lock lock(mutex_);
     252     1691234 :     MaybeStartRunnerUnlocked();
     253     1691254 : }
     254             : 
     255      849739 : bool DBPartition::WorkQueue::RunnerDone() {
     256      849739 :     std::scoped_lock lock(mutex_);
     257      849773 :     if (disable_ || (request_queue_.empty() && remove_queue_.empty())) {
     258      801605 :         running_ = false;
     259      801605 :         return true;
     260             :     }
     261             : 
     262       48102 :     running_ = true;
     263       48102 :     return false;
     264      849707 : }
     265             : 
     266      109374 : DBPartition::DBPartition(DB *db, int partition_id)
     267      109374 :     : db_(db), work_queue_(new WorkQueue(this, partition_id)) {
     268      109374 : }
     269             : 
     270             : // The DBPartition destructor needs to be defined after WorkQueue has
     271             : // been declared.
     272      109310 : DBPartition::~DBPartition() {
     273      109310 : }
     274             : 
     275     1166272 : bool DBPartition::EnqueueRequest(DBTablePartBase *tpart, DBClient *client,
     276             :                                  DBRequest *req) {
     277     1166272 :     RequestQueueEntry *entry = new RequestQueueEntry(tpart, client, req);
     278     1166220 :     return work_queue_->EnqueueRequest(entry);
     279             : }
     280             : 
     281      524715 : void DBPartition::EnqueueRemove(DBTablePartBase *tpart, DBEntryBase *db_entry) {
     282      524715 :     RemoveQueueEntry *entry = new RemoveQueueEntry(tpart, db_entry);
     283      524740 :     db_entry->SetOnRemoveQ();
     284      524924 :     work_queue_->EnqueueRemove(entry);
     285      524867 : }
     286             : 
     287             : // concurrency: called from DBPartition task.
     288     1688729 : void DBPartition::OnTableChange(DBTablePartBase *tablepart) {
     289     1688729 :     work_queue_->SetActive(tablepart);
     290     1688897 : }
     291             : 
     292           0 : long DBPartition::request_queue_len() const {
     293           0 :     return work_queue_->request_queue_len();
     294             : }
     295             : 
     296           0 : uint64_t DBPartition::total_request_count() const {
     297           0 :     return work_queue_->total_request_count();
     298             : }
     299             : 
     300           0 : uint64_t DBPartition::max_request_queue_len() const {
     301           0 :     return work_queue_->max_request_queue_len();
     302             : }
     303             : 
     304      801576 : int DBPartition::task_id() const {
     305      801576 :     return db_->task_id();
     306             : }

Generated by: LCOV version 1.14