netwerk/base/src/EventTokenBucket.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/src/EventTokenBucket.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,146 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef NetEventTokenBucket_h__
    1.11 +#define NetEventTokenBucket_h__
    1.12 +
    1.13 +#include "nsCOMPtr.h"
    1.14 +#include "nsDeque.h"
    1.15 +#include "nsITimer.h"
    1.16 +
    1.17 +#include "mozilla/TimeStamp.h"
    1.18 +
    1.19 +class nsICancelable;
    1.20 +
    1.21 +namespace mozilla {
    1.22 +namespace net {
    1.23 +
    1.24 +/* A token bucket is used to govern the maximum rate a series of events
    1.25 +   can be executed at. For instance if your event was "eat a piece of cake"
    1.26 +   then a token bucket configured to allow "1 piece per day" would spread
    1.27 +   the eating of a 8 piece cake over 8 days even if you tried to eat the
    1.28 +   whole thing up front. In a practical sense it 'costs' 1 token to execute
    1.29 +   an event and tokens are 'earned' at a particular rate as time goes by.
    1.30 +   
    1.31 +   The token bucket can be perfectly smooth or allow a configurable amount of
    1.32 +   burstiness. A bursty token bucket allows you to save up unused credits, while
    1.33 +   a perfectly smooth one would not. A smooth "1 per day" cake token bucket
    1.34 +   would require 9 days to eat that cake if you skipped a slice on day 4
    1.35 +   (use the token or lose it), while a token bucket configured with a burst
    1.36 +   of 2 would just let you eat 2 slices on day 5 (the credits for day 4 and day
    1.37 +   5) and finish the cake in the usual 8 days.
    1.38 +
    1.39 +   EventTokenBucket(hz=20, burst=5) creates a token bucket with the following properties:
    1.40 +
    1.41 +  + events from an infinite stream will be admitted 20 times per second (i.e.
    1.42 +    hz=20 means 1 event per 50 ms). Timers will be used to space things evenly down to
    1.43 +    5ms gaps (i.e. up to 200hz). Token buckets with rates greater than 200hz will admit
    1.44 +    multiple events with 5ms gaps between them. 10000hz is the maximum rate and 1hz is
    1.45 +    the minimum rate.
    1.46 +
    1.47 +  + The burst size controls the limit of 'credits' that a token bucket can accumulate
    1.48 +    when idle. For our (20,5) example each event requires 50ms of credit (again, 20hz = 50ms
    1.49 +    per event). a burst size of 5 means that the token bucket can accumulate a
    1.50 +    maximum of 250ms (5 * 50ms) for this bucket. If no events have been admitted for the 
    1.51 +    last full second the bucket can still only accumulate 250ms of credit - but that credit
    1.52 +    means that 5 events can be admitted without delay. A burst size of 1 is the minimum.
    1.53 +    The EventTokenBucket is created with maximum credits already applied, but they
    1.54 +    can be cleared with the ClearCredits() method. The maximum burst size is
    1.55 +    15 minutes worth of events.
    1.56 +
    1.57 +  + An event is submitted to the token bucket asynchronously through SubmitEvent().
    1.58 +    The OnTokenBucketAdmitted() method of the submitted event is used as a callback
    1.59 +    when the event is ready to run. A cancelable event is returned to the SubmitEvent() caller
    1.60 +    for use in the case they do not wish to wait for the callback.
    1.61 +*/
    1.62 +
    1.63 +class EventTokenBucket;
    1.64 +
    1.65 +class ATokenBucketEvent 
    1.66 +{
    1.67 +public:
    1.68 +  virtual void OnTokenBucketAdmitted() = 0;
    1.69 +};
    1.70 +
    1.71 +class TokenBucketCancelable;
    1.72 +
    1.73 +class EventTokenBucket : public nsITimerCallback
    1.74 +{
    1.75 +public:
    1.76 +  NS_DECL_THREADSAFE_ISUPPORTS
    1.77 +  NS_DECL_NSITIMERCALLBACK
    1.78 +
    1.79 +  // This should be constructed on the main thread
    1.80 +  EventTokenBucket(uint32_t eventsPerSecond, uint32_t burstSize);
    1.81 +  virtual ~EventTokenBucket();
    1.82 +
    1.83 +  // These public methods are all meant to be called from the socket thread
    1.84 +  void ClearCredits();
    1.85 +  uint32_t BurstEventsAvailable();
    1.86 +  uint32_t QueuedEvents();
    1.87 +
    1.88 +  // a paused token bucket will not process any events, but it will accumulate
    1.89 +  // credits. ClearCredits can be used before unpausing if desired.
    1.90 +  void Pause();
    1.91 +  void UnPause();
    1.92 +  void Stop() { mStopped = true; }
    1.93 +
    1.94 +  // The returned cancelable event can only be canceled from the socket thread
    1.95 +  nsresult SubmitEvent(ATokenBucketEvent *event, nsICancelable **cancelable);
    1.96 +
    1.97 +private:
    1.98 +  friend class RunNotifyEvent;
    1.99 +  friend class SetTimerEvent;
   1.100 +
   1.101 +  bool TryImmediateDispatch(TokenBucketCancelable *event);
   1.102 +  void SetRate(uint32_t eventsPerSecond, uint32_t burstSize);
   1.103 +
   1.104 +  void DispatchEvents();
   1.105 +  void UpdateTimer();
   1.106 +  void UpdateCredits();
   1.107 +
   1.108 +  const static uint64_t kUsecPerSec =  1000000;
   1.109 +  const static uint64_t kUsecPerMsec = 1000;
   1.110 +  const static uint64_t kMaxHz = 10000;
   1.111 +
   1.112 +  uint64_t mUnitCost;   // usec of credit needed for 1 event (from eventsPerSecond)
   1.113 +  uint64_t mMaxCredit; // usec mCredit limit (from busrtSize)
   1.114 +  uint64_t mCredit; // usec of accumulated credit.
   1.115 +
   1.116 +  bool     mPaused;
   1.117 +  bool     mStopped;
   1.118 +  nsDeque  mEvents;
   1.119 +  bool     mTimerArmed;
   1.120 +  TimeStamp mLastUpdate;
   1.121 +
   1.122 +  // The timer is created on the main thread, but is armed and executes Notify()
   1.123 +  // callbacks on the socket thread in order to maintain low latency of event
   1.124 +  // delivery.
   1.125 +  nsCOMPtr<nsITimer> mTimer;
   1.126 +
   1.127 +#ifdef XP_WIN
   1.128 +  // Windows timers are 15ms granularity by default. When we have active events
   1.129 +  // that need to be dispatched at 50ms  or less granularity we change the OS
   1.130 +  // granularity to 1ms. 90 seconds after that need has elapsed we will change it
   1.131 +  // back
   1.132 +  const static uint64_t kCostFineGrainThreshold =  50 * kUsecPerMsec;
   1.133 +
   1.134 +  void FineGrainTimers(); // get 1ms granularity
   1.135 +  void NormalTimers(); // reset to default granularity
   1.136 +  void WantNormalTimers(); // reset after 90 seconds if not needed in interim
   1.137 +  void FineGrainResetTimerNotify(); // delayed callback to reset
   1.138 +
   1.139 +  TimeStamp mLastFineGrainTimerUse;
   1.140 +  bool mFineGrainTimerInUse;
   1.141 +  bool mFineGrainResetTimerArmed;
   1.142 +  nsCOMPtr<nsITimer> mFineGrainResetTimer;
   1.143 +#endif
   1.144 +};
   1.145 +
   1.146 +} // ::mozilla::net
   1.147 +} // ::mozilla
   1.148 +
   1.149 +#endif

mercurial