aboutsummaryrefslogtreecommitdiff
path: root/OperationLimiter.h
diff options
context:
space:
mode:
authorBernie Innocenti <codewiz@google.com>2020-11-27 23:31:58 +0900
committerBernie Innocenti <codewiz@google.com>2020-12-01 11:40:21 +0900
commite1b9d0c85aaf26b0c381c2ce3bdbfb68c154df86 (patch)
treed3e0ae18ff9445b5cf9d2bf36ebed7e0267af9d0 /OperationLimiter.h
parentac6c9a404d3f1d2671f4ed1f9770b57e14b504ad (diff)
downloadDnsResolver-e1b9d0c85aaf26b0c381c2ce3bdbfb68c154df86.tar.gz
Add an optional global limit to OperationLimiter
TODO: unit tests Change-Id: Id98157ba9b973e0a4f7b82c4e2bc9249a7da628c
Diffstat (limited to 'OperationLimiter.h')
-rw-r--r--OperationLimiter.h39
1 files changed, 28 insertions, 11 deletions
diff --git a/OperationLimiter.h b/OperationLimiter.h
index df39cd0e..1fa1bf23 100644
--- a/OperationLimiter.h
+++ b/OperationLimiter.h
@@ -33,39 +33,44 @@ namespace netdutils {
// The intended usage pattern is:
// OperationLimiter<UserId> connections_per_user;
// ...
-// // Before opening a new connection
-// if (!limiter.start(user)) {
-// return error;
-// } else {
-// // open the connection
-// // ...do some work...
-// // close the connection
-// limiter.finish(user);
+// int connectToSomeResource(int user) {
+// if (!connections_per_user.start(user)) return TRY_AGAIN_LATER;
+// // ...do expensive work here...
+// connections_per_user.finish(user);
// }
//
// This class is thread-safe.
template <typename KeyType>
class OperationLimiter {
public:
- explicit OperationLimiter(int limit) : mLimitPerKey(limit) {}
+ OperationLimiter(int limitPerKey, int globalLimit = INT_MAX)
+ : mLimitPerKey(limitPerKey), mGlobalLimit(globalLimit) {}
~OperationLimiter() {
DCHECK(mCounters.empty()) << "Destroying OperationLimiter with active operations";
}
- // Returns false if |key| has reached the maximum number of concurrent
- // operations, otherwise increments the counter and returns true.
+ // Returns false if |key| has reached the maximum number of concurrent operations,
+ // or if the global limit has been reached. Otherwise, increments the counter and returns true.
//
// Note: each successful start(key) must be matched by exactly one call to
// finish(key).
bool start(KeyType key) EXCLUDES(mMutex) {
std::lock_guard lock(mMutex);
+
+ if (mGlobalCounter >= mGlobalLimit) {
+ // Oh, no!
+ return false;
+ }
+
auto& cnt = mCounters[key]; // operator[] creates new entries as needed.
if (cnt >= mLimitPerKey) {
// Oh, no!
return false;
}
+
++cnt;
+ ++mGlobalCounter;
return true;
}
@@ -73,6 +78,13 @@ class OperationLimiter {
// See usage notes on start().
void finish(KeyType key) EXCLUDES(mMutex) {
std::lock_guard lock(mMutex);
+
+ --mGlobalCounter;
+ if (mGlobalCounter < 0) {
+ LOG(FATAL_WITHOUT_ABORT) << "Global operations counter going negative, this is a bug.";
+ return;
+ }
+
auto it = mCounters.find(key);
if (it == mCounters.end()) {
LOG(FATAL_WITHOUT_ABORT) << "Decremented non-existent counter for key=" << key;
@@ -93,8 +105,13 @@ class OperationLimiter {
// Tracks the number of outstanding queries by key.
std::unordered_map<KeyType, int> mCounters GUARDED_BY(mMutex);
+ int mGlobalCounter GUARDED_BY(mMutex) = 0;
+
// Maximum number of outstanding queries from a single key.
const int mLimitPerKey;
+
+ // Maximum number of outstanding queries, globally.
+ const int mGlobalLimit;
};
} // namespace netdutils