@@ -219,28 +219,28 @@ runAgainstBucket ::
219
219
(Handlers m -> m a ) ->
220
220
m (State m , a )
221
221
runAgainstBucket config action = do
222
- runThreadVar <- atomically newEmptyTMVar -- see note [Leaky bucket design].
222
+ leakingPeriodVersionTMVar <- atomically newEmptyTMVar -- see note [Leaky bucket design].
223
223
tid <- myThreadId
224
224
bucket <- init config
225
- withAsync (leak runThreadVar tid bucket) $ \ _ -> do
226
- atomicallyWithMonotonicTime $ maybeStartThread Nothing runThreadVar bucket
225
+ withAsync (leak (readTMVar leakingPeriodVersionTMVar) tid bucket) $ \ _ -> do
226
+ atomicallyWithMonotonicTime $ maybeStartThread Nothing leakingPeriodVersionTMVar bucket
227
227
result <-
228
228
action $
229
229
Handlers
230
230
{ fill = \ r t -> (snd <$> ) $ snapshotFill bucket r t,
231
231
setPaused = setPaused bucket,
232
- updateConfig = updateConfig runThreadVar bucket
232
+ updateConfig = updateConfig leakingPeriodVersionTMVar bucket
233
233
}
234
234
state <- atomicallyWithMonotonicTime $ snapshot bucket
235
235
pure (state, result)
236
236
where
237
- -- Start the thread (that is, write to its 'runThreadVar ') if it is useful.
238
- -- Takes a potential old value of the 'runThreadVar ' as first argument,
237
+ -- Start the thread (that is, write to its 'leakingPeriodVersionTMVar ') if it is useful.
238
+ -- Takes a potential old value of the 'leakingPeriodVersionTMVar ' as first argument,
239
239
-- which will be increased to help differentiate between restarts.
240
240
maybeStartThread :: Maybe Int -> StrictTMVar m Int -> Bucket m -> Time -> STM m ()
241
- maybeStartThread oldRunThread runThreadVar bucket time = do
241
+ maybeStartThread mLeakingPeriodVersion leakingPeriodVersionTMVar bucket time = do
242
242
State {config = Config {rate}} <- snapshot bucket time
243
- when (rate > 0 ) $ void $ tryPutTMVar runThreadVar $ maybe 0 (+ 1 ) oldRunThread
243
+ when (rate > 0 ) $ void $ tryPutTMVar leakingPeriodVersionTMVar $ maybe 0 (+ 1 ) mLeakingPeriodVersion
244
244
245
245
setPaused :: Bucket m -> Bool -> Time -> STM m ()
246
246
setPaused bucket paused time = do
@@ -253,7 +253,7 @@ runAgainstBucket config action = do
253
253
((Rational , Config m ) -> (Rational , Config m )) ->
254
254
Time ->
255
255
STM m ()
256
- updateConfig runThreadVar bucket f time = do
256
+ updateConfig leakingPeriodVersionTMVar bucket f time = do
257
257
State
258
258
{ level = oldLevel,
259
259
paused,
@@ -272,9 +272,9 @@ runAgainstBucket config action = do
272
272
configGeneration = oldConfigGeneration + 1 ,
273
273
config = newConfig
274
274
}
275
- -- Ensure that 'runThreadVar ' is empty, then maybe start the thread.
276
- oldRunThread <- tryTakeTMVar runThreadVar
277
- maybeStartThread oldRunThread runThreadVar bucket time
275
+ -- Ensure that 'leakingPeriodVersionTMVar ' is empty, then maybe start the thread.
276
+ mLeakingPeriodVersion <- tryTakeTMVar leakingPeriodVersionTMVar
277
+ maybeStartThread mLeakingPeriodVersion leakingPeriodVersionTMVar bucket time
278
278
279
279
-- | Initialise a bucket given a configuration. The bucket starts full at the
280
280
-- time where one calls 'init'.
@@ -299,7 +299,7 @@ init config@Config {capacity} = do
299
299
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~
300
300
--
301
301
-- The leaky bucket works by running the given action against a thread that
302
- -- makes the bucket leak. Since that would be extremely inefficient to actually
302
+ -- makes the bucket leak. Since it would be inefficient to actually
303
303
-- remove tokens one by one from the bucket, the 'leak' thread instead looks at
304
304
-- the current state of the bucket, computes how much time it would take for the
305
305
-- bucket to empty, and then wait that amount of time. Once the wait is over, it
@@ -317,41 +317,42 @@ init config@Config {capacity} = do
317
317
-- for the action to lower the waiting time by changing the bucket configuration
318
318
-- to one where the rate is higher.
319
319
--
320
- -- We fix both those issues with one mechanism, the @runThreadVar@. It is an
321
- -- MVar containing an integer that tells the thread whether it should be
322
- -- running. An empty MVar means that the thread should not be running, for
323
- -- instance if the rate is null. A full MVar ( no matter what the integer is)
324
- -- means that the thread should be running. When recursing, the thread blocks
325
- -- until the MVar is full , and only then proceeds as described above.
320
+ -- We fix both those issues with one mechanism, the @leakingPeriodVersionSTM@.
321
+ -- It is a computation returning an integer that identifies a version of the
322
+ -- configuration that controls the leaking period. If the computation blocks,
323
+ -- it means that no configuration has been determined yet.
324
+ -- The leak thread first waits until @leakingPeriodVersionSTM@ yields a
325
+ -- value , and only then proceeds as described above.
326
326
-- Additionally, while waiting for the bucket to empty, the thread monitors
327
- -- changes to the MVar, indicating either that the thread should stop running or
328
- -- that the configuration changed as that it might have to wait less long. The
329
- -- change in configuration is detected by changes in the integer.
327
+ -- for changes to the version of the leaking period, indicating either that the
328
+ -- thread should pause running if the @leakingPeriodVersionSTM@ starts blocking
329
+ -- again or that the configuration changed as that it might have to wait less
330
+ -- long.
330
331
--
331
- -- Note that we call \“start\”/\“stop\” running the action of filling/emptying the
332
- -- MVar. This is not to mistaken for the thread actually being spawned/killed.
333
332
334
- -- | Monadic action that calls 'threadDelay' until the bucket is empty, then
335
- -- runs the 'onEmpty' action and terminates . See note [Leaky bucket design].
333
+ -- | Neverending computation that runs 'onEmpty' whenever the bucket becomes
334
+ -- empty . See note [Leaky bucket design].
336
335
leak ::
337
336
( MonadDelay m ,
338
337
MonadCatch m ,
339
338
MonadFork m ,
340
339
MonadAsync m ,
341
340
MonadTimer m
342
341
) =>
343
- -- | A variable indicating whether the thread should run (when it is filled)
344
- -- or not (otherwise). The integer it carries only helps in differentiating
345
- -- between starts and restarts. 'leak' does not modify this variable.
346
- StrictTMVar m Int ->
342
+ -- | A computation indicating the version of the configuration affecting the
343
+ -- leaking period. Whenever the configuration changes, the returned integer
344
+ -- must be incremented. While no configuration is available, the computation
345
+ -- should block. Blocking is allowed at any time, and it will cause the
346
+ -- leaking to pause.
347
+ STM m Int ->
347
348
-- | The 'ThreadId' of the action's thread, which is used to throw exceptions
348
349
-- at it.
349
350
ThreadId m ->
350
351
Bucket m ->
351
352
m ()
352
- leak runThreadVar actionThreadId bucket = forever $ do
353
- -- Block until we are allowed to run. Do not modify the TMVar.
354
- oldRunThread <- atomically $ readTMVar runThreadVar
353
+ leak leakingPeriodVersionSTM actionThreadId bucket = forever $ do
354
+ -- Block until we are allowed to run.
355
+ leakingPeriodVersion <- atomically leakingPeriodVersionSTM
355
356
-- NOTE: It is tempting to group this @atomically@ and
356
357
-- @atomicallyWithMonotonicTime@ into one; however, because the former is
357
358
-- blocking, the latter could get a _very_ inaccurate time, which we
@@ -377,7 +378,7 @@ leak runThreadVar actionThreadId bucket = forever $ do
377
378
atomically $
378
379
(check =<< TVar. readTVar varTimeout)
379
380
`orElse`
380
- (void $ blockUntilChanged id ( Just oldRunThread) $ tryReadTMVar runThreadVar )
381
+ (void $ blockUntilChanged id leakingPeriodVersion leakingPeriodVersionSTM )
381
382
382
383
-- | Take a snapshot of the bucket, that is compute its state at the current
383
384
-- time.
0 commit comments