зеркало из https://github.com/mozilla/gecko-dev.git
Bug 931186 - Dirt simple token bucket class. r=ekr
This commit is contained in:
Родитель
e48a231554
Коммит
0f1fb3b85f
|
@ -15,6 +15,7 @@ EXPORTS.mtransport += [
|
|||
'../runnable_utils.h',
|
||||
'../runnable_utils_generated.h',
|
||||
'../sigslot.h',
|
||||
'../simpletokenbucket.h',
|
||||
'../transportflow.h',
|
||||
'../transportlayer.h',
|
||||
'../transportlayerdtls.h',
|
||||
|
|
|
@ -13,6 +13,7 @@ mtransport_lcppsrcs = [
|
|||
'nriceresolver.cpp',
|
||||
'nriceresolverfake.cpp',
|
||||
'nrinterfaceprioritizer.cpp',
|
||||
'simpletokenbucket.cpp',
|
||||
'transportflow.cpp',
|
||||
'transportlayer.cpp',
|
||||
'transportlayerdtls.cpp',
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Original author: bcampen@mozilla.com */
|
||||
|
||||
#include "simpletokenbucket.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "prinrval.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
SimpleTokenBucket::SimpleTokenBucket(size_t bucket_size,
|
||||
size_t tokens_per_second) :
|
||||
max_tokens_(bucket_size),
|
||||
num_tokens_(bucket_size),
|
||||
tokens_per_second_(tokens_per_second),
|
||||
last_time_tokens_added_(PR_IntervalNow()) {
|
||||
}
|
||||
|
||||
size_t SimpleTokenBucket::getTokens(size_t num_requested_tokens) {
|
||||
// Only fill if there isn't enough to satisfy the request.
|
||||
// If we get tokens so seldomly that we are able to roll the timer all
|
||||
// the way around its range, then we lose that entire range of time
|
||||
// for token accumulation. Probably not the end of the world.
|
||||
if (num_requested_tokens > num_tokens_) {
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
|
||||
// If we roll over the max, since everything in this calculation is the same
|
||||
// unsigned type, this will still yield the elapsed time (unless we've
|
||||
// wrapped more than once).
|
||||
PRIntervalTime elapsed_ticks = now - last_time_tokens_added_;
|
||||
|
||||
uint32_t elapsed_milli_sec = PR_IntervalToMilliseconds(elapsed_ticks);
|
||||
size_t tokens_to_add = (elapsed_milli_sec * tokens_per_second_)/1000;
|
||||
|
||||
// Only update our timestamp if we added some tokens
|
||||
// TODO:(bcampen@mozilla.com) Should we attempt to "save" leftover time?
|
||||
if (tokens_to_add) {
|
||||
num_tokens_ += tokens_to_add;
|
||||
if (num_tokens_ > max_tokens_) {
|
||||
num_tokens_ = max_tokens_;
|
||||
}
|
||||
|
||||
last_time_tokens_added_ = now;
|
||||
}
|
||||
|
||||
if (num_requested_tokens > num_tokens_) {
|
||||
return num_tokens_;
|
||||
}
|
||||
}
|
||||
|
||||
num_tokens_ -= num_requested_tokens;
|
||||
return num_requested_tokens;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Original author: bcampen@mozilla.com */
|
||||
|
||||
/*
|
||||
* This file defines a dirt-simple token bucket class.
|
||||
*/
|
||||
|
||||
#ifndef simpletokenbucket_h__
|
||||
#define simpletokenbucket_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "prinrval.h"
|
||||
|
||||
#include "m_cpp_utils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class SimpleTokenBucket {
|
||||
public:
|
||||
/*
|
||||
* Create a SimpleTokenBucket with a given maximum size and
|
||||
* token replenishment rate.
|
||||
* (eg; if you want a maximum rate of 5 per second over a 7 second
|
||||
* period, call SimpleTokenBucket b(5*7, 5);)
|
||||
*/
|
||||
SimpleTokenBucket(size_t bucket_size, size_t tokens_per_second);
|
||||
|
||||
/*
|
||||
* Attempt to acquire a number of tokens. If successful, returns
|
||||
* |num_tokens|, otherwise returns the number of tokens currently
|
||||
* in the bucket.
|
||||
* Note: To get the number of tokens in the bucket, pass something
|
||||
* like UINT32_MAX.
|
||||
*/
|
||||
size_t getTokens(size_t num_tokens);
|
||||
|
||||
protected: // Allow testing to touch these.
|
||||
uint64_t max_tokens_;
|
||||
uint64_t num_tokens_;
|
||||
size_t tokens_per_second_;
|
||||
PRIntervalTime last_time_tokens_added_;
|
||||
|
||||
DISALLOW_COPY_ASSIGN(SimpleTokenBucket);
|
||||
};
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // simpletokenbucket_h__
|
||||
|
|
@ -11,6 +11,7 @@ if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
|
|||
'ice_unittest.cpp',
|
||||
'nrappkit_unittest.cpp',
|
||||
'runnable_utils_unittest.cpp',
|
||||
'simpletokenbucket_unittest.cpp',
|
||||
'sockettransportservice_unittest.cpp',
|
||||
'TestSyncRunnable.cpp',
|
||||
'transport_unittests.cpp',
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Original author: bcampen@mozilla.com */
|
||||
|
||||
#include "simpletokenbucket.h"
|
||||
|
||||
#define GTEST_HAS_RTTI 0
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest_utils.h"
|
||||
|
||||
using mozilla::SimpleTokenBucket;
|
||||
|
||||
class TestSimpleTokenBucket : public SimpleTokenBucket {
|
||||
public:
|
||||
TestSimpleTokenBucket(size_t bucketSize, size_t tokensPerSecond) :
|
||||
SimpleTokenBucket(bucketSize, tokensPerSecond) {
|
||||
}
|
||||
|
||||
void fastForward(int32_t timeMilliSeconds) {
|
||||
if (timeMilliSeconds >= 0) {
|
||||
last_time_tokens_added_ -= PR_MillisecondsToInterval(timeMilliSeconds);
|
||||
} else {
|
||||
last_time_tokens_added_ += PR_MillisecondsToInterval(-timeMilliSeconds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestConstruct) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestGet) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(5U, b.getTokens(5));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestGetAll) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(10U, b.getTokens(10));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestGetInsufficient) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(5U, b.getTokens(5));
|
||||
ASSERT_EQ(5U, b.getTokens(6));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestGetBucketCount) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(10U, b.getTokens(UINT32_MAX));
|
||||
ASSERT_EQ(5U, b.getTokens(5));
|
||||
ASSERT_EQ(5U, b.getTokens(UINT32_MAX));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestTokenRefill) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(5U, b.getTokens(5));
|
||||
b.fastForward(1000);
|
||||
ASSERT_EQ(6U, b.getTokens(6));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestNoTimeWasted) {
|
||||
// Makes sure that when the time elapsed is insufficient to add any
|
||||
// tokens to the bucket, the internal timestamp that is used in this
|
||||
// calculation is not updated (ie; two subsequent 0.5 second elapsed times
|
||||
// counts as a full second)
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(5U, b.getTokens(5));
|
||||
b.fastForward(500);
|
||||
ASSERT_EQ(5U, b.getTokens(6));
|
||||
b.fastForward(500);
|
||||
ASSERT_EQ(6U, b.getTokens(6));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestNegativeTime) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
b.fastForward(-1000);
|
||||
// Make sure we don't end up with an invalid number of tokens, but otherwise
|
||||
// permit anything.
|
||||
ASSERT_GT(11U, b.getTokens(100));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestEmptyBucket) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(10U, b.getTokens(10));
|
||||
ASSERT_EQ(0U, b.getTokens(10));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestEmptyThenFillBucket) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(10U, b.getTokens(10));
|
||||
ASSERT_EQ(0U, b.getTokens(1));
|
||||
b.fastForward(50000);
|
||||
ASSERT_EQ(10U, b.getTokens(10));
|
||||
}
|
||||
|
||||
TEST(SimpleTokenBucketTest, TestNoOverflow) {
|
||||
TestSimpleTokenBucket b(10, 1);
|
||||
ASSERT_EQ(10U, b.getTokens(10));
|
||||
ASSERT_EQ(0U, b.getTokens(1));
|
||||
b.fastForward(50000);
|
||||
ASSERT_EQ(10U, b.getTokens(11));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
int rv = RUN_ALL_TESTS();
|
||||
return rv;
|
||||
}
|
||||
|
Загрузка…
Ссылка в новой задаче