Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bf80bee authored by Bernie Innocenti's avatar Bernie Innocenti Committed by Gerrit Code Review
Browse files

Merge "Add an optional global limit to OperationLimiter"

parents deecd67e e1b9d0c8
Loading
Loading
Loading
Loading
+28 −11
Original line number Original line Diff line number Diff line
@@ -33,39 +33,44 @@ namespace netdutils {
// The intended usage pattern is:
// The intended usage pattern is:
//     OperationLimiter<UserId> connections_per_user;
//     OperationLimiter<UserId> connections_per_user;
//     ...
//     ...
//     // Before opening a new connection
//     int connectToSomeResource(int user) {
//     if (!limiter.start(user)) {
//         if (!connections_per_user.start(user)) return TRY_AGAIN_LATER;
//         return error;
//         // ...do expensive work here...
//     } else {
//         connections_per_user.finish(user);
//         // open the connection
//         // ...do some work...
//         // close the connection
//         limiter.finish(user);
//     }
//     }
//
//
// This class is thread-safe.
// This class is thread-safe.
template <typename KeyType>
template <typename KeyType>
class OperationLimiter {
class OperationLimiter {
  public:
  public:
    explicit OperationLimiter(int limit) : mLimitPerKey(limit) {}
    OperationLimiter(int limitPerKey, int globalLimit = INT_MAX)
        : mLimitPerKey(limitPerKey), mGlobalLimit(globalLimit) {}


    ~OperationLimiter() {
    ~OperationLimiter() {
        DCHECK(mCounters.empty()) << "Destroying OperationLimiter with active operations";
        DCHECK(mCounters.empty()) << "Destroying OperationLimiter with active operations";
    }
    }


    // Returns false if |key| has reached the maximum number of concurrent
    // Returns false if |key| has reached the maximum number of concurrent operations,
    // operations, otherwise increments the counter and returns true.
    // 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
    // Note: each successful start(key) must be matched by exactly one call to
    // finish(key).
    // finish(key).
    bool start(KeyType key) EXCLUDES(mMutex) {
    bool start(KeyType key) EXCLUDES(mMutex) {
        std::lock_guard lock(mMutex);
        std::lock_guard lock(mMutex);

        if (mGlobalCounter >= mGlobalLimit) {
            // Oh, no!
            return false;
        }

        auto& cnt = mCounters[key];  // operator[] creates new entries as needed.
        auto& cnt = mCounters[key];  // operator[] creates new entries as needed.
        if (cnt >= mLimitPerKey) {
        if (cnt >= mLimitPerKey) {
            // Oh, no!
            // Oh, no!
            return false;
            return false;
        }
        }

        ++cnt;
        ++cnt;
        ++mGlobalCounter;
        return true;
        return true;
    }
    }


@@ -73,6 +78,13 @@ class OperationLimiter {
    // See usage notes on start().
    // See usage notes on start().
    void finish(KeyType key) EXCLUDES(mMutex) {
    void finish(KeyType key) EXCLUDES(mMutex) {
        std::lock_guard lock(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);
        auto it = mCounters.find(key);
        if (it == mCounters.end()) {
        if (it == mCounters.end()) {
            LOG(FATAL_WITHOUT_ABORT) << "Decremented non-existent counter for key=" << key;
            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.
    // Tracks the number of outstanding queries by key.
    std::unordered_map<KeyType, int> mCounters GUARDED_BY(mMutex);
    std::unordered_map<KeyType, int> mCounters GUARDED_BY(mMutex);


    int mGlobalCounter GUARDED_BY(mMutex) = 0;

    // Maximum number of outstanding queries from a single key.
    // Maximum number of outstanding queries from a single key.
    const int mLimitPerKey;
    const int mLimitPerKey;

    // Maximum number of outstanding queries, globally.
    const int mGlobalLimit;
};
};


}  // namespace netdutils
}  // namespace netdutils