LCOV - code coverage report
Current view: top level - db - db_table.h (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 77 83 92.8 %
Date: 2026-11-09 02:10:49 Functions: 49 53 92.5 %
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             : #ifndef ctrlplane_db_table_h
       7             : #define ctrlplane_db_table_h
       8             : 
       9             : #include <atomic>
      10             : #include <memory>
      11             : #include <vector>
      12             : #include <unistd.h>
      13             : #include <boost/function.hpp>
      14             : #include <boost/intrusive_ptr.hpp>
      15             : 
      16             : #include "base/util.h"
      17             : 
      18             : class DB;
      19             : class DBClient;
      20             : class DBEntryBase;
      21             : class DBEntry;
      22             : class DBTablePartBase;
      23             : class DBTablePartition;
      24             : class DBTableWalk;
      25             : class ShowTableListener;
      26             : 
      27             : class DBRequestKey {
      28             : public:
      29     2478502 :     virtual ~DBRequestKey() { }
      30             : };
      31             : class DBRequestData {
      32             : public:
      33      504202 :     virtual ~DBRequestData() { }
      34             : };
      35             : 
      36             : struct DBRequest {
      37             :     typedef enum {
      38             :         DB_ENTRY_INVALID = 0,
      39             :         DB_ENTRY_ADD_CHANGE = 1,
      40             :         DB_ENTRY_DELETE = 2,
      41             :         DB_ENTRY_NOTIFY = 3,
      42             :     } DBOperation;
      43             :     DBOperation oper;
      44             : 
      45             :     DBRequest();
      46       28304 :     DBRequest(DBOperation op) : oper(op) { }
      47             :     ~DBRequest();
      48             : 
      49             :     std::unique_ptr<DBRequestKey> key;
      50             :     std::unique_ptr<DBRequestData> data;
      51             : 
      52             :     // Swap contents between two DBRequest entries.
      53             :     void Swap(DBRequest *rhs);
      54             : 
      55             : private:
      56             :     DISALLOW_COPY_AND_ASSIGN(DBRequest);
      57             : };
      58             : 
      59             : // Database table interface.
      60             : class DBTableBase {
      61             : public:
      62             :     typedef boost::function<void(DBTablePartBase *, DBEntryBase *)> ChangeCallback;
      63             :     typedef int ListenerId;
      64             : 
      65             :     static const int kInvalidId = -1;
      66             : 
      67             :     DBTableBase(DB *db, const std::string &name);
      68             :     virtual ~DBTableBase();
      69             : 
      70             :     // Enqueue a request to the table. Takes ownership of the data.
      71             :     bool Enqueue(DBRequest *req);
      72             :     void EnqueueRemove(DBEntryBase *db_entry);
      73             : 
      74             :     // Determine the table partition depending on the record key.
      75             :     virtual DBTablePartBase *GetTablePartition(const DBRequestKey *key) = 0;
      76             :     // Determine the table partition depending on the Entry
      77             :     virtual DBTablePartBase *GetTablePartition(const DBEntryBase *entry) = 0;
      78             :     // Determine the table partition for given index
      79             :     virtual DBTablePartBase *GetTablePartition(const int index) = 0;
      80             : 
      81             :     // Record has been modified.
      82             :     virtual void Change(DBEntryBase *) = 0;
      83             : 
      84             :     // Callback from table partition for entry add/remove.
      85     2021567 :     virtual void AddRemoveCallback(const DBEntryBase *entry, bool add) const { }
      86             : 
      87             :     // Register a DB listener.
      88             :     ListenerId Register(ChangeCallback callback,
      89             :         const std::string &name = "unspecified");
      90             :     void Unregister(ListenerId listener);
      91             : 
      92             :     void RunNotify(DBTablePartBase *tpart, DBEntryBase *entry);
      93             : 
      94             :     // Manage db state count for a listener.
      95             :     void AddToDBStateCount(ListenerId listener, int count);
      96             : 
      97             :     uint64_t GetDBStateCount(ListenerId listener);
      98             : 
      99             :     // Calculate the size across all partitions.
     100             :     // Must be called from Task which is mutually exclusive with db::DBTable.
     101           0 :     virtual size_t Size() const { return 0; }
     102     1207717 :     bool empty() const { return (Size() == 0); }
     103             : 
     104             :     // Suspended deletion resume hook for user function
     105      140567 :     virtual void RetryDelete() { }
     106             :     virtual bool MayDelete() const;
     107             : 
     108     3654889 :     DB *database() { return db_; }
     109             :     const DB *database() const { return db_; }
     110             : 
     111    11523726 :     const std::string &name() const { return name_; }
     112             : 
     113             :     bool HasListeners() const;
     114             :     size_t GetListenerCount() const;
     115             :     void FillListeners(std::vector<ShowTableListener> *listeners) const;
     116             : 
     117        1002 :     uint64_t enqueue_count() const { return enqueue_count_; }
     118             :     void incr_enqueue_count() { enqueue_count_++; }
     119             :     void reset_enqueue_count() { enqueue_count_ = 0; }
     120             : 
     121        1250 :     uint64_t input_count() const { return input_count_; }
     122      686325 :     void incr_input_count() { input_count_++; }
     123             :     void reset_input_count() { input_count_ = 0; }
     124             : 
     125        1002 :     uint64_t notify_count() const { return notify_count_; }
     126             :     void incr_notify_count() { notify_count_++; }
     127             :     void reset_notify_count() { notify_count_ = 0; }
     128             : 
     129      577702 :     bool HasWalkers() const { return walker_count_ != 0; }
     130       11315 :     uint64_t walker_count() const { return walker_count_; }
     131      222680 :     void incr_walker_count() { walker_count_++; }
     132      222680 :     uint64_t decr_walker_count() { return --walker_count_; }
     133             : 
     134       10371 :     uint64_t walk_request_count() const { return walk_request_count_; }
     135       10480 :     uint64_t walk_complete_count() const { return walk_complete_count_; }
     136       10313 :     uint64_t walk_cancel_count() const { return walk_cancel_count_; }
     137        9220 :     uint64_t walk_again_count() const { return walk_again_count_; }
     138        9254 :     uint64_t walk_count() const { return walk_count_; }
     139      258959 :     void incr_walk_request_count() { walk_request_count_++; }
     140      228294 :     void incr_walk_complete_count() { walk_complete_count_++; }
     141           0 :     void incr_walk_cancel_count() { walk_cancel_count_++; }
     142         399 :     void incr_walk_again_count() { walk_again_count_++; }
     143      227844 :     void incr_walk_count() { walk_count_++; }
     144             : 
     145             : private:
     146             :     class ListenerInfo;
     147             :     DB *db_;
     148             :     std::string name_;
     149             :     std::unique_ptr<ListenerInfo> info_;
     150             :     uint64_t enqueue_count_;
     151             :     uint64_t input_count_;
     152             :     uint64_t notify_count_;
     153             :     std::atomic<uint64_t> walker_count_;
     154             :     std::atomic<uint64_t> walk_count_;
     155             :     std::atomic<uint64_t> walk_request_count_;
     156             :     std::atomic<uint64_t> walk_complete_count_;
     157             :     std::atomic<uint64_t> walk_cancel_count_;
     158             :     std::atomic<uint64_t> walk_again_count_;
     159             : };
     160             : 
     161             : // An implementation of DBTableBase that uses boost::set as data-store
     162             : // Most of the DB Table implementations should derive from here instead of
     163             : // DBTableBase directly.
     164             : // Derive directly from DBTableBase only if there is a strong reason to do so
     165             : //
     166             : // Additionally, provides a set of virtual functions to override the default
     167             : // functionality
     168             : class DBTable : public DBTableBase {
     169             : public:
     170             :     typedef boost::intrusive_ptr<DBTableWalk> DBTableWalkRef;
     171             : 
     172             :     // Walker function:
     173             :     // Called for each DBEntry under a db::DBTable task that corresponds to the
     174             :     // specific partition.
     175             :     // arguments: DBTable partition and DBEntry.
     176             :     // returns: true (continue); false (stop).
     177             :     typedef boost::function<bool(DBTablePartBase *, DBEntryBase *)> WalkFn;
     178             : 
     179             :     // Called when all partitions are done iterating.
     180             :     typedef boost::function<void(DBTableWalkRef, DBTableBase *)> WalkCompleteFn;
     181             : 
     182             :     static const int kIterationToYield = 256;
     183             : 
     184             :     DBTable(DB *db, const std::string &name);
     185             :     virtual ~DBTable();
     186             :     void Init();
     187             : 
     188             :     ///////////////////////////////////////////////////////////
     189             :     // virtual functions to be implemented by derived class
     190             :     ///////////////////////////////////////////////////////////
     191             : 
     192             :     // Alloc a derived DBEntry
     193             :     virtual std::unique_ptr<DBEntry> AllocEntry(const DBRequestKey *key) const = 0;
     194             : 
     195             :     // Hash for an entry. Used to identify partition
     196      923792 :     virtual size_t Hash(const DBEntry *entry) const {return 0;};
     197             : 
     198             :     // Hash for key. Used to identify partition
     199      183448 :     virtual size_t Hash(const DBRequestKey *key) const {return 0;};
     200             : 
     201             :     // Alloc a derived DBTablePartBase entry. The default implementation
     202             :     // allocates DBTablePart should be good for most common cases.
     203             :     // Override if *really* necessary
     204             :     virtual DBTablePartition *AllocPartition(int index);
     205             : 
     206             :     // Input processing implemented by derived class. Default
     207             :     // implementation takes care of Add/Delete/Change.
     208             :     // Override if *really* necessary
     209             :     virtual void Input(DBTablePartition *tbl_partition, DBClient *client,
     210             :                        DBRequest *req);
     211             : 
     212             :     // Add hook for user function. Must return entry to be inserted into tree
     213             :     // Must return NULL if no entry is to be added into tree
     214             :     virtual DBEntry *Add(const DBRequest *req);
     215             :     // Change hook for user function. Return 'true' if clients need to be
     216             :     // notified of the change
     217             :     virtual bool OnChange(DBEntry *entry, const DBRequest *req);
     218             :     // Delete hook for user function
     219             :     virtual bool Delete(DBEntry *entry, const DBRequest *req);
     220             : 
     221             :     void NotifyAllEntries();
     222             : 
     223             :     ///////////////////////////////////////////////////////////
     224             :     // Utility methods for table
     225             :     ///////////////////////////////////////////////////////////
     226             :     // Find DB Entry. Get key from from argument
     227             :     DBEntry *Find(const DBEntry *entry);
     228             :     const DBEntry *Find(const DBEntry *entry) const;
     229             : 
     230             :     DBEntry *Find(const DBRequestKey *key, int id = -1);
     231             :     const DBEntry *Find(const DBRequestKey *key, int id = -1) const;
     232             : 
     233             :     // Find DB Entry without taking lock. Calling routine must ensure its
     234             :     // running in exclusion with DB task
     235             :     DBEntry *FindNoLock(const DBEntry *entry);
     236             :     DBEntry *FindNoLock(const DBRequestKey *key);
     237             : 
     238             :     ///////////////////////////////////////////////////////////////
     239             :     // Virtual functions from DBTableBase implemented by DBTable
     240             :     ///////////////////////////////////////////////////////////////
     241             : 
     242             :     // Return the table partition for a specific request.
     243             :     virtual DBTablePartBase *GetTablePartition(const DBRequestKey *key);
     244             :     virtual const DBTablePartBase *GetTablePartition(
     245             :         const DBRequestKey *key) const;
     246             :     // Return the table partition for a DBEntryBase
     247             :     virtual DBTablePartBase *GetTablePartition(const DBEntryBase *entry);
     248             :     virtual const DBTablePartBase *GetTablePartition(
     249             :         const DBEntryBase *entry) const;
     250             :     // Return the table partition for a index
     251             :     virtual DBTablePartBase *GetTablePartition(const int index);
     252             :     virtual const DBTablePartBase *GetTablePartition(const int index) const;
     253             : 
     254             :     // Change notification handler.
     255             :     virtual void Change(DBEntryBase *entry);
     256             : 
     257             :     virtual int PartitionCount() const;
     258             : 
     259             :     // Calculate the size across all partitions.
     260             :     virtual size_t Size() const;
     261             : 
     262             :     // helper functions
     263             : 
     264             :     // Delete all the state entries of a specific listener.
     265             :     // Not thread-safe. Used to shutdown and cleanup the process.
     266             :     static void DBStateClear(DBTable *table, ListenerId id);
     267             : 
     268             : 
     269             :     // Walk APIs
     270             :     // Create a DBTable Walker
     271             :     // Concurrency : can be invoked from any task
     272             :     DBTableWalkRef AllocWalker(WalkFn walk_fn, WalkCompleteFn walk_complete);
     273             : 
     274             :     // Release the Walker
     275             :     // Concurrency : can be invoked from any task
     276             :     void ReleaseWalker(DBTableWalkRef &walk);
     277             : 
     278             :     // Start a walk on the table.
     279             :     // Concurrency : should be invoked from a task which is mutually exclusive
     280             :     // "db::Walker" task
     281             :     void WalkTable(DBTableWalkRef walk);
     282             : 
     283             :     // Walk the table again
     284             :     // Concurrency : should be invoked from a task which is mutually exclusive
     285             :     // "db::Walker" task
     286             :     void WalkAgain(DBTableWalkRef walk);
     287             : 
     288           2 :     void SetWalkIterationToYield(int count) {
     289           2 :         max_walk_iteration_to_yield_ = count;
     290           2 :     }
     291             : 
     292      796649 :     int GetWalkIterationToYield() {
     293      796649 :         return max_walk_iteration_to_yield_;
     294             :     }
     295             : 
     296           6 :     void SetWalkTaskId(int task_id) {
     297           6 :         walker_task_id_ = task_id;
     298           6 :     }
     299             : 
     300      132298 :     int GetWalkerTaskId() {
     301      132298 :         return walker_task_id_;
     302             :     }
     303             : private:
     304             :     friend class DBTableWalkMgr;
     305             :     class TableWalker;
     306             :     // A Job for walking through the DBTablePartition
     307             :     class WalkWorker;
     308             : 
     309      796069 :     static void db_walker_wait() {
     310             :         static unsigned int walk_sleep_usecs_;
     311             :         static bool once;
     312             : 
     313      796069 :         if (!once) {
     314          94 :             once = true;
     315             : 
     316          94 :             char *wait = getenv("DB_WALKER_WAIT_USECS");
     317          94 :             if (wait) {
     318           0 :               walk_sleep_usecs_ = (unsigned int) strtoul(wait, NULL, 0);
     319           0 :               if (walk_sleep_usecs_ > 1000000)
     320           0 :                 walk_sleep_usecs_ = 1000000;
     321             :             }
     322             :             
     323             :         }
     324             : 
     325      796069 :         if (walk_sleep_usecs_) {
     326           0 :             usleep(walk_sleep_usecs_);
     327             :         }
     328      796069 :     }
     329             : 
     330             :     ///////////////////////////////////////////////////////////
     331             :     // Utility methods
     332             :     ///////////////////////////////////////////////////////////
     333             :     // Hash key to a partition id
     334             :     int GetPartitionId(const DBRequestKey *key);
     335             :     // Hash entry to a partition id
     336             :     int GetPartitionId(const DBEntry *entry);
     337             : 
     338             :     // Called from DBTableWalkMgr to start the walk
     339             :     void StartWalk();
     340             : 
     341             :     // Call DBTableWalkMgr to notify the walkers
     342             :     bool InvokeWalkCb(DBTablePartBase *part, DBEntryBase *entry);
     343             : 
     344             :     // Call DBTableWalkMgr::WalkDone
     345             :     void WalkDone();
     346             : 
     347             :     // Walker callback for NotifyAllEntries()
     348             :     bool WalkCallback(DBTablePartBase *tpart, DBEntryBase *entry);
     349             :     void WalkCompleteCallback(DBTableBase *tbl_base);
     350             : 
     351             :     std::unique_ptr<TableWalker> walker_;
     352             :     std::vector<DBTablePartition *> partitions_;
     353             :     DBTable::DBTableWalkRef walk_ref_;
     354             :     int walker_task_id_;
     355             :     int max_walk_iteration_to_yield_;
     356             : 
     357             :     DISALLOW_COPY_AND_ASSIGN(DBTable);
     358             : };
     359             : 
     360             : class DBTableWalk {
     361             : public:
     362             :     enum WalkState {
     363             :         INIT = 1,
     364             :         WALK_REQUESTED = 2,
     365             :         WALK_IN_PROGRESS = 3,
     366             :         WALK_DONE = 4,
     367             :         WALK_STOPPED = 5,
     368             :     };
     369             : 
     370      222230 :     DBTableWalk(DBTable *table, DBTable::WalkFn walk_fn,
     371             :                 DBTable::WalkCompleteFn walk_complete)
     372      222230 :         : table_(table), walk_fn_(walk_fn), walk_complete_(walk_complete) {
     373      222230 :         walk_state_ = INIT;
     374      222230 :         walk_again_ = false;
     375      222230 :         refcount_ = 0;
     376      222230 :     }
     377             : 
     378      711664 :     DBTable *table() const { return table_;}
     379      798319 :     DBTable::WalkFn walk_fn() const { return walk_fn_;}
     380      230160 :     DBTable::WalkCompleteFn walk_complete() const { return walk_complete_;}
     381             : 
     382             :     bool requested() const { return (walk_state_ == WALK_REQUESTED);}
     383      260500 :     bool in_progress() const { return (walk_state_ == WALK_IN_PROGRESS);}
     384      799088 :     bool done() const { return (walk_state_ == WALK_DONE);}
     385     1489491 :     bool stopped() const { return (walk_state_ == WALK_STOPPED);}
     386     1259208 :     bool walk_again() const { return walk_again_;}
     387       22653 :     bool walk_is_active() const {
     388       45284 :         return ((walk_state_ == WALK_REQUESTED) ||
     389       45284 :                 (walk_state_ == WALK_IN_PROGRESS));
     390             :     }
     391             : 
     392             :     WalkState walk_state() const { return walk_state_;}
     393             : 
     394             : private:
     395             :     friend class DBTableWalkMgr;
     396             : 
     397             :     friend void intrusive_ptr_add_ref(DBTableWalk *walker);
     398             :     friend void intrusive_ptr_release(DBTableWalk *walker);
     399             : 
     400         399 :     void set_walk_again() { walk_again_ = true;}
     401      230372 :     void reset_walk_again() { walk_again_ = false;}
     402             : 
     403      230163 :     void set_walk_done() { walk_state_ = WALK_DONE;}
     404      258719 :     void set_walk_requested() { walk_state_ = WALK_REQUESTED;}
     405      230372 :     void set_in_progress() { walk_state_ = WALK_IN_PROGRESS;}
     406      158931 :     void set_walk_stopped() { walk_state_ = WALK_STOPPED;}
     407             : 
     408             :     DBTable *table_;
     409             :     DBTable::WalkFn walk_fn_;
     410             :     DBTable::WalkCompleteFn walk_complete_;
     411             :     std::atomic<WalkState> walk_state_;
     412             :     std::atomic<bool> walk_again_;
     413             :     std::atomic<int> refcount_;
     414             : 
     415             :     DISALLOW_COPY_AND_ASSIGN(DBTableWalk);
     416             : };
     417             : 
     418     2808541 : inline void intrusive_ptr_add_ref(DBTableWalk *walker) {
     419     2808541 :     walker->refcount_.fetch_add(1);
     420     2808541 : }
     421             : 
     422     2808548 : inline void intrusive_ptr_release(DBTableWalk *walker) {
     423     2808548 :     int prev = walker->refcount_.fetch_sub(1);
     424     2808548 :     if (prev == 1) {
     425      222230 :         DBTable *table = walker->table();
     426      222230 :         delete walker;
     427      222230 :         table->decr_walker_count();
     428      222230 :         table->RetryDelete();
     429             :     }
     430     2808548 : }
     431             : #endif

Generated by: LCOV version 1.14