Skip to content

Commit f450d3e

Browse files
committed
Do a better job of documenting concurrencyController
1 parent 1425588 commit f450d3e

1 file changed

Lines changed: 37 additions & 14 deletions

File tree

concurrency.go

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,54 @@ import (
44
"sync"
55
)
66

7-
// concurrencyControl controls how many concurrent operations of some type can occur at
8-
// the same time. A concurrencyController is created with newConcurrencyController() which
9-
// specifies the maximum number of concurrent operations. Henceforth callers call start()
10-
// which will block until the number of concurrent operations is below the maximum allowed
11-
// and will return when the caller can commence their concurrent operation. Once the
12-
// currency operation is complete, the caller calls done() which releases up the next
13-
// waiter sitting on the start() call.
7+
// concurrencyController controls how many gorutines can run at the same time.
8+
//
9+
// A concurrencyController is created with newConcurrencyController() which specifies the
10+
// maximum number of concurrent operations. Henceforth callers call start() in a separate
11+
// goroutine which will block until the number of concurrent operations is below the
12+
// maximum allowed and will return when the caller can commence their concurrent
13+
// operation. Once the currency operation is complete, the caller calls done() which
14+
// releases up the next waiter sitting on the start() call.
1415
//
1516
// Normally the caller creates many separate goroutines, the number of which may easily
1617
// exceed the concurrency limit, they call start() and are concurrently enabled upto the
17-
// configured maximum.
18+
// configured maximum. In effect, concurrencyControl uses the goroutine stack as the queue
19+
// of pending goroutines to run.
20+
//
21+
// Idiomatic code looks like this:
22+
//
23+
// const max = 10
24+
// const required = 20
25+
// cc := newConcurrencyController(max)
26+
// for ix := 0; ix < required; ix++ {
27+
// go func() {
28+
// cc.start() // Stalls if max exceeded
29+
// doStuff()
30+
// cc.done()
31+
// }()
32+
// }
33+
// cc.wait() // All functions are complete on return
1834
//
1935
// Very limit checking is done to ensure that callers are honoring the correct call
20-
// sequence and inappropriate calls, such as more done() calls than start() calls can
21-
// result in a panic or a deadlock or some other undesired behaviour.
36+
// sequence and avoiding inappropriate calls; e.g.: making more done() calls than start()
37+
// calls is not checked, but it can result in a panic or a deadlock or some other
38+
// undesired behaviour.
2239
//
2340
// The control mechanism is a channel containing the concurrency limit of tokens. A call
2441
// to start() reads the next token from the channel and a call to done() writes a
2542
// previously read token back to the channel. The caller is responsible for ensuring
2643
// matching calls to start() and done().
27-
28-
type concurrencyToken struct{}
29-
3044
type concurrencyController struct {
3145
sync.RWMutex
3246
limit int
3347
minimum int
3448
tokens chan concurrencyToken
3549
}
3650

51+
type concurrencyToken struct{}
52+
53+
// newConcurrencyController creates a concurrencyController which allows a maximum of
54+
// "limit" concurrency goroutines to run at any one time.
3755
func newConcurrencyController(limit int) *concurrencyController {
3856
cc := &concurrencyController{limit: limit, minimum: limit,
3957
tokens: make(chan concurrencyToken, limit)}
@@ -45,6 +63,9 @@ func newConcurrencyController(limit int) *concurrencyController {
4563
return cc
4664
}
4765

66+
// start is normally called by an independent goroutine. start blocks until the number of
67+
// running goroutines is below the limit at which point it returns to the caller. A caller
68+
// calls done() on completion to allow the next goroutine blocked on start() to proceed.
4869
func (cc *concurrencyController) start() {
4970
cc.Lock()
5071
defer cc.Unlock()
@@ -56,11 +77,13 @@ func (cc *concurrencyController) start() {
5677
}
5778
}
5879

80+
// done informs the concurrencyController that this goroutine has completed and that the
81+
// next goroutine blocked on start() can proceed.
5982
func (cc *concurrencyController) done() {
6083
cc.tokens <- concurrencyToken{}
6184
}
6285

63-
// Return true if all tokens have been issued at any time since creation
86+
// limitReached returns true if all tokens have been issued at any time since creation
6487
func (cc *concurrencyController) limitReached() bool {
6588
cc.RLock()
6689
defer cc.RUnlock()

0 commit comments

Comments
 (0)