Loading Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -259,6 +259,7 @@ cc_test { "DnsQueryLogTest.cpp", "DnsStatsTest.cpp", "ExperimentsTest.cpp", "OperationLimiterTest.cpp", "PrivateDnsConfigurationTest.cpp", ], shared_libs: [ Loading DnsProxyListener.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -39,7 +39,6 @@ #include <cutils/misc.h> // FIRST_APPLICATION_UID #include <cutils/multiuser.h> #include <netdutils/InternetAddresses.h> #include <netdutils/OperationLimiter.h> #include <netdutils/ResponseCode.h> #include <netdutils/Slice.h> #include <netdutils/Stopwatch.h> Loading @@ -50,6 +49,7 @@ #include "DnsResolver.h" #include "NetdPermissions.h" #include "OperationLimiter.h" #include "PrivateDnsConfiguration.h" #include "ResolverEventReporter.h" #include "dnsproxyd_protocol/DnsProxydProtocol.h" // NETID_USE_LOCAL_NAMESERVERS Loading OperationLimiter.h 0 → 100644 +103 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETUTILS_OPERATIONLIMITER_H #define NETUTILS_OPERATIONLIMITER_H #include <mutex> #include <unordered_map> #include <android-base/logging.h> #include <android-base/thread_annotations.h> namespace android { namespace netdutils { // Tracks the number of operations in progress on behalf of a particular key or // ID, rejecting further attempts to start new operations after a configurable // limit has been reached. // // 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); // } // // This class is thread-safe. template <typename KeyType> class OperationLimiter { public: explicit OperationLimiter(int limit) : mLimitPerKey(limit) {} ~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. // // 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); auto& cnt = mCounters[key]; // operator[] creates new entries as needed. if (cnt >= mLimitPerKey) { // Oh, no! return false; } ++cnt; return true; } // Decrements the number of operations in progress accounted to |key|. // See usage notes on start(). void finish(KeyType key) EXCLUDES(mMutex) { std::lock_guard lock(mMutex); auto it = mCounters.find(key); if (it == mCounters.end()) { LOG(FATAL_WITHOUT_ABORT) << "Decremented non-existent counter for key=" << key; return; } auto& cnt = it->second; --cnt; if (cnt <= 0) { // Cleanup counters once they drop down to zero. mCounters.erase(it); } } private: // Protects access to the map below. std::mutex mMutex; // Tracks the number of outstanding queries by key. std::unordered_map<KeyType, int> mCounters GUARDED_BY(mMutex); // Maximum number of outstanding queries from a single key. const int mLimitPerKey; }; } // namespace netdutils } // namespace android #endif // NETUTILS_OPERATIONLIMITER_H OperationLimiterTest.cpp 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "OperationLimiter.h" #include <gtest/gtest-spi.h> namespace android { namespace netdutils { TEST(OperationLimiter, limits) { OperationLimiter<int> limiter(3); EXPECT_TRUE(limiter.start(42)); EXPECT_TRUE(limiter.start(42)); EXPECT_TRUE(limiter.start(42)); // Limit reached... calling any number of times should have no effect. EXPECT_FALSE(limiter.start(42)); EXPECT_FALSE(limiter.start(42)); EXPECT_FALSE(limiter.start(42)); // Finishing a single operations is enough for starting a new one... limiter.finish(42); EXPECT_TRUE(limiter.start(42)); // ...but not two! EXPECT_FALSE(limiter.start(42)); // Different ids should still have quota... EXPECT_TRUE(limiter.start(666)); limiter.finish(666); // Finish all pending operations limiter.finish(42); limiter.finish(42); limiter.finish(42); } TEST(OperationLimiter, finishWithoutStart) { OperationLimiter<int> limiter(1); // Will output a LOG(FATAL_WITHOUT_ABORT), but we have no way to probe this. limiter.finish(42); // This will ensure that the finish() above didn't set a negative value. EXPECT_TRUE(limiter.start(42)); EXPECT_FALSE(limiter.start(42)); } TEST(OperationLimiter, destroyWithActiveOperations) { // The death message doesn't seem to be captured on Android. EXPECT_DEBUG_DEATH( { OperationLimiter<int> limiter(3); limiter.start(42); }, "" /* "active operations */); } } // namespace netdutils } // namespace android Loading
Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -259,6 +259,7 @@ cc_test { "DnsQueryLogTest.cpp", "DnsStatsTest.cpp", "ExperimentsTest.cpp", "OperationLimiterTest.cpp", "PrivateDnsConfigurationTest.cpp", ], shared_libs: [ Loading
DnsProxyListener.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -39,7 +39,6 @@ #include <cutils/misc.h> // FIRST_APPLICATION_UID #include <cutils/multiuser.h> #include <netdutils/InternetAddresses.h> #include <netdutils/OperationLimiter.h> #include <netdutils/ResponseCode.h> #include <netdutils/Slice.h> #include <netdutils/Stopwatch.h> Loading @@ -50,6 +49,7 @@ #include "DnsResolver.h" #include "NetdPermissions.h" #include "OperationLimiter.h" #include "PrivateDnsConfiguration.h" #include "ResolverEventReporter.h" #include "dnsproxyd_protocol/DnsProxydProtocol.h" // NETID_USE_LOCAL_NAMESERVERS Loading
OperationLimiter.h 0 → 100644 +103 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETUTILS_OPERATIONLIMITER_H #define NETUTILS_OPERATIONLIMITER_H #include <mutex> #include <unordered_map> #include <android-base/logging.h> #include <android-base/thread_annotations.h> namespace android { namespace netdutils { // Tracks the number of operations in progress on behalf of a particular key or // ID, rejecting further attempts to start new operations after a configurable // limit has been reached. // // 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); // } // // This class is thread-safe. template <typename KeyType> class OperationLimiter { public: explicit OperationLimiter(int limit) : mLimitPerKey(limit) {} ~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. // // 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); auto& cnt = mCounters[key]; // operator[] creates new entries as needed. if (cnt >= mLimitPerKey) { // Oh, no! return false; } ++cnt; return true; } // Decrements the number of operations in progress accounted to |key|. // See usage notes on start(). void finish(KeyType key) EXCLUDES(mMutex) { std::lock_guard lock(mMutex); auto it = mCounters.find(key); if (it == mCounters.end()) { LOG(FATAL_WITHOUT_ABORT) << "Decremented non-existent counter for key=" << key; return; } auto& cnt = it->second; --cnt; if (cnt <= 0) { // Cleanup counters once they drop down to zero. mCounters.erase(it); } } private: // Protects access to the map below. std::mutex mMutex; // Tracks the number of outstanding queries by key. std::unordered_map<KeyType, int> mCounters GUARDED_BY(mMutex); // Maximum number of outstanding queries from a single key. const int mLimitPerKey; }; } // namespace netdutils } // namespace android #endif // NETUTILS_OPERATIONLIMITER_H
OperationLimiterTest.cpp 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "OperationLimiter.h" #include <gtest/gtest-spi.h> namespace android { namespace netdutils { TEST(OperationLimiter, limits) { OperationLimiter<int> limiter(3); EXPECT_TRUE(limiter.start(42)); EXPECT_TRUE(limiter.start(42)); EXPECT_TRUE(limiter.start(42)); // Limit reached... calling any number of times should have no effect. EXPECT_FALSE(limiter.start(42)); EXPECT_FALSE(limiter.start(42)); EXPECT_FALSE(limiter.start(42)); // Finishing a single operations is enough for starting a new one... limiter.finish(42); EXPECT_TRUE(limiter.start(42)); // ...but not two! EXPECT_FALSE(limiter.start(42)); // Different ids should still have quota... EXPECT_TRUE(limiter.start(666)); limiter.finish(666); // Finish all pending operations limiter.finish(42); limiter.finish(42); limiter.finish(42); } TEST(OperationLimiter, finishWithoutStart) { OperationLimiter<int> limiter(1); // Will output a LOG(FATAL_WITHOUT_ABORT), but we have no way to probe this. limiter.finish(42); // This will ensure that the finish() above didn't set a negative value. EXPECT_TRUE(limiter.start(42)); EXPECT_FALSE(limiter.start(42)); } TEST(OperationLimiter, destroyWithActiveOperations) { // The death message doesn't seem to be captured on Android. EXPECT_DEBUG_DEATH( { OperationLimiter<int> limiter(3); limiter.start(42); }, "" /* "active operations */); } } // namespace netdutils } // namespace android