Line data Source code
1 : /* 2 : * Copyright (c) 2016 Juniper Networks, Inc. All rights reserved. 3 : * Copyright Contributors to the OpenSDN project 4 : */ 5 : 6 : #ifndef ctrlplane_db_table_walk_mgr_h 7 : #define ctrlplane_db_table_walk_mgr_h 8 : 9 : #include <list> 10 : #include <set> 11 : #include <mutex> 12 : 13 : #include <boost/assign.hpp> 14 : #include <boost/function.hpp> 15 : #include <boost/scoped_ptr.hpp> 16 : #include <boost/shared_ptr.hpp> 17 : 18 : 19 : #include "base/logging.h" 20 : #include "base/task_trigger.h" 21 : #include "base/util.h" 22 : 23 : #include "db/db_table.h" 24 : 25 : // 26 : // DBTableWalkMgr: 27 : // ============== 28 : // DBTableWalkMgr provides infrastructure to walk DBTable. 29 : // 30 : // DBTable class provides API for walking DBTable. Following APIs are provided 31 : // 32 : // 1. AllocWalker: This API allocates a walk handle and returns to the caller 33 : // Application is suppose to release the walker using ReleaseWalker. 34 : // 35 : // AllocWalker API can be called from any task context. 36 : // 37 : // Application provides two callback function as input to for this API. 38 : // 39 : // DBTable::WalkFn : Callback invoked by walk infra while traversing each 40 : // DBEntry in DBTable. This API is invoked in db::DBTable task context 41 : // by default. Application can configure the task id in which 42 : // DBTable walk is performed using DBTable::SetWalkTaskId 43 : // 44 : // DBTable::WalkCompleteFn: Callback invoked by walk infra on completion of 45 : // ongoing walk request. This API is invoked in db::Walker task 46 : // context irrespective of the task id set with DBTable::SetWalkTaskId 47 : // This callback is not invoked in cases where application 48 : // calls ReleaseWalker on ongoing walker. Also, WalkCompleteFn is 49 : // called only after handling all WalkAgain requests on the walker. 50 : // 51 : // 2. ReleaseWalker: This API releases the walker. After invoking this API, 52 : // application will stop getting Walk callback for the ongoing Walk request. 53 : // Application should not refer to the DBTableWalkRef after invoking this API 54 : // 55 : // ReleaseWalker API can be called from any task context. 56 : // 57 : // 3. WalkTable: This API starts the table walk. This API should be called 58 : // from a task which is mutually exclusive from db::Walker task. 59 : // 60 : // 4. WalkAgain: To re-trigger/restart current walk request from application. 61 : // Callback for WalkFn is no longer invoked for ongoing walk and walk is 62 : // restarted from beginning of DBTable. This API should be called from a task 63 : // which is mutually exclusive from db::Walker task. 64 : // 65 : // DBTableWalkMgr ensures that not more than one DBTable is walked at any point 66 : // in time. All other DBTable walk requests are queued and taken up only after 67 : // current walk completes. 68 : // Actual DBTable walk (i.e. iterating the DBTablePartition) is performed in 69 : // db::DBTable task or task id configured with DBTable::SetWalkTaskId with 70 : // instance id set as partition index. 71 : // The advantage of running DBTable walk in serial manner is in clubbing 72 : // multiple walk requests on a given table and serving such requests in one 73 : // iteration of DBTable walk 74 : // 75 : // WalkReqList holds list of DBTableWalkRef(i.e. walkers created by multiple 76 : // application modules) that requested for DBTable walk on a specific table. 77 : // InvokeWalkCb notifies all such walkers stored in current_table_walk_, while 78 : // iterating through DBTable entries 79 : // 80 : // WalkRequestInfo: 81 : // =============== 82 : // WalkRequestInfo is a per table Walk request structure. It also holds 83 : // DBTableWalkRef which requested for DBTable walk. 84 : // 85 : // WalkRequestInfoList 86 : // =================== 87 : // walk_request_list_ holds list of WalkRequestInfo. This list is keyed by 88 : // DBTable. Additional walk_request_set_ is maintained for easy search of 89 : // WalkRequestInfo for a given DBTable. 90 : // Current table on which walk is going on will not be present in the 91 : // walk_request_list_. If caller requests for WalkAgain(), it is added back to 92 : // the walk_request_list_ (in the end of the list). 93 : // 94 : // Task Triggers: 95 : // walk_request_trigger_ : Task trigger which evaluate walk_request_list_. 96 : // It removes the WalkRequestInfo on top of this list and starts walk on the 97 : // table. This task trigger runs in "db::Walker" task context. 98 : // 99 : // walk_done_trigger_ : Task trigger ensures that WalkCompleteFn is triggered 100 : // in db::Walker task context for all DBTableWalkRef which requested for 101 : // current DBTable walk. At the end of ProcessWalkDone, walk_request_trigger_ is 102 : // triggered to evaluate walk request from top of walk_request_list_. 103 : // 104 : class DBTableWalkMgr { 105 : public: 106 : DBTableWalkMgr(); 107 : 108 22 : void DisableWalkProcessing() { 109 22 : walk_request_trigger_->set_disable(); 110 22 : } 111 : 112 22 : void EnableWalkProcessing() { 113 22 : walk_request_trigger_->set_enable(); 114 22 : } 115 : 116 : void DisableWalkDoneTrigger() { 117 : walk_done_trigger_->set_disable(); 118 : } 119 : 120 : void EnableWalkDoneTrigger() { 121 : walk_done_trigger_->set_enable(); 122 : } 123 : 124 : private: 125 : friend class DBTable; 126 : typedef std::set<DBTable::DBTableWalkRef> WalkReqList; 127 : 128 : struct WalkRequestInfo { 129 486753 : WalkRequestInfo(DBTable *table) : table(table) { 130 486753 : } 131 : 132 258908 : void AppendWalkReq(DBTable::DBTableWalkRef ref) { 133 258908 : pending_requests.insert(ref); 134 258908 : } 135 : 136 : void DeleteWalkReq(DBTable::DBTableWalkRef ref) { 137 : pending_requests.erase(ref); 138 : } 139 : 140 : bool WalkPending() { 141 : return !pending_requests.empty(); 142 : } 143 : DBTable *table; 144 : WalkReqList pending_requests; 145 : }; 146 : 147 : struct WalkRequestCompare { 148 1262116 : bool operator()(const WalkRequestInfo *lhs, 149 : const WalkRequestInfo *rhs) const { 150 1262116 : return lhs->table < rhs->table; 151 : } 152 : }; 153 : typedef boost::shared_ptr<WalkRequestInfo> WalkRequestInfoPtr; 154 : typedef std::list<WalkRequestInfoPtr> WalkRequestInfoList; 155 : typedef std::set<WalkRequestInfo *, WalkRequestCompare> WalkRequestInfoSet; 156 : 157 : // Create a DBTable Walker 158 : DBTable::DBTableWalkRef AllocWalker(DBTable *table, DBTable::WalkFn walk_fn, 159 : DBTable::WalkCompleteFn walk_complete); 160 : 161 : // Release the Walker 162 : void ReleaseWalker(DBTable::DBTableWalkRef &walk); 163 : 164 : // Start a walk on the table. 165 : void WalkTable(DBTable::DBTableWalkRef walk); 166 : 167 : // DBTable finished walking 168 : void WalkDone(); 169 : 170 : // Walk the table again 171 : void WalkAgain(DBTable::DBTableWalkRef walk); 172 : 173 : bool ProcessWalkRequestList(); 174 : 175 : bool ProcessWalkDone(); 176 : 177 : bool InvokeWalkCb(DBTablePartBase *part, DBEntryBase *entry); 178 : 179 : boost::scoped_ptr<TaskTrigger> walk_request_trigger_; 180 : boost::scoped_ptr<TaskTrigger> walk_done_trigger_; 181 : 182 : // Mutex to protect walk_request_list_ and walk_request_set_ as 183 : // Walk can be requested from task which may run concurrently 184 : std::mutex mutex_; 185 : WalkRequestInfoList walk_request_list_; 186 : WalkRequestInfoSet walk_request_set_; 187 : 188 : WalkReqList current_table_walk_; 189 : 190 : DISALLOW_COPY_AND_ASSIGN(DBTableWalkMgr); 191 : }; 192 : 193 : #endif