1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/policy/core/common/cloud/rate_limiter.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/location.h"
10#include "base/logging.h"
11#include "base/sequenced_task_runner.h"
12#include "base/time/tick_clock.h"
13
14namespace policy {
15
16RateLimiter::RateLimiter(size_t max_requests,
17                         const base::TimeDelta& duration,
18                         const base::Closure& callback,
19                         scoped_refptr<base::SequencedTaskRunner> task_runner,
20                         scoped_ptr<base::TickClock> clock)
21    : max_requests_(max_requests),
22      duration_(duration),
23      callback_(callback),
24      task_runner_(task_runner),
25      clock_(clock.Pass()) {
26  DCHECK_GT(max_requests_, 0u);
27}
28
29RateLimiter::~RateLimiter() {}
30
31void RateLimiter::PostRequest() {
32  DCHECK(CalledOnValidThread());
33
34  const base::TimeTicks now = clock_->NowTicks();
35  const base::TimeTicks period_start = now - duration_;
36  while (!invocation_times_.empty() &&
37         invocation_times_.front() <= period_start) {
38    invocation_times_.pop();
39  }
40
41  delayed_callback_.Cancel();
42
43  if (invocation_times_.size() < max_requests_) {
44    invocation_times_.push(now);
45    callback_.Run();
46  } else {
47    // From the while() loop above we have front() > period_start,
48    // so time_until_next_callback > 0.
49    const base::TimeDelta time_until_next_callback =
50        invocation_times_.front() - period_start;
51    delayed_callback_.Reset(
52        base::Bind(&RateLimiter::PostRequest, base::Unretained(this)));
53    task_runner_->PostDelayedTask(
54        FROM_HERE, delayed_callback_.callback(), time_until_next_callback);
55  }
56}
57
58}  // namespace policy
59