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 : }
|