Errata
The errata list is a list of errors and their corrections that were found after the product was released.
The following errata were submitted by our customers and have not yet been approved or disproved by the author or editor. They solely represent the opinion of the customer.
Color Key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update
Version | Location | Description | Submitted by | Date submitted |
---|---|---|---|---|
Safari Books Online | In chapter 6 there is an illustration of a FIFO queue going from P1 through a centralized queue to P2. Centralized is not spelt correctly in the illustration, it's missing the 'n' |
Anonymous | Jul 09, 2020 | |
Safari Books Online | Chapter 3 sync.Once section |
There is this code describing sync.Once - var numCalcsCreated int calcPool := &sync.Pool { New: func() interface{} { numCalcsCreated += 1 mem := make([]byte, 1024) return &mem 1 }, } New function may potentially be called concurrently by multiple goroutines. This can lead to a data race for accessing numCalcsCreated. To ensure atomic increments, appropriate atomic functions must be used. |
Nitish Chandra | Sep 12, 2020 |
Printed, PDF | Page page 127 code line 13 |
log.Fatal("error: %v", err) should be log.Fatalf("error: %v", err) |
fanghao | Jun 20, 2022 |
Safari Books Online | 1 sync.Cond.Broadcast code sample |
The sync.Cond Broadcast code includes a "subscribe" function like this: ``` subscribe := func(c *sync.Cond, fn func()) { var goroutineRunning sync.WaitGroup goroutineRunning.Add(1) go func() { goroutineRunning.Done() c.L.Lock() defer c.L.Unlock() c.Wait() fn() }() goroutineRunning.Wait() } // ... three subscribes ... button.Clicked.Broadcast() ``` Unfortunately there is no guarantee that all subscribed functions will be at `c.Wait()` before the `button.Clicked.Broadcast()` . This can be pretty easily demonstrated with a test that re-runs the example many times: ``` import ( "sync" "testing" ) type Button struct { Clicked *sync.Cond } func TestExampleInBook(t *testing.T) { // typically fails far earlier rounds := 1000000 for i:=0; i<rounds; i++ { button := Button{Clicked: sync.NewCond(&sync.Mutex{})} subscribe := func(c *sync.Cond, fn func()) { var goroutineRunning sync.WaitGroup goroutineRunning.Add(1) go func() { goroutineRunning.Done() c.L.Lock() defer c.L.Unlock() c.Wait() fn() }() goroutineRunning.Wait() } var clickRegistered sync.WaitGroup clickRegistered.Add(3) subscribe(button.Clicked, func() { clickRegistered.Done() }) subscribe(button.Clicked, func() { clickRegistered.Done() }) subscribe(button.Clicked, func() { clickRegistered.Done() }) button.Clicked.Broadcast() clickRegistered.Wait() } } ``` Unfortunately there's no quick fix for this. The root of the problem is that there's no way to make sure all subscribed function's goroutines are at `c.Wait()`, because 1) you can't send any signal after that point to signal readiness, and 2) any signal sent before that point then needs to race to the `c.Wait()` with whatever is listening to that signal. Unfortunately, sync.Cond suffers from this in *any* construct where your logic requires something to be waiting. There's simply no guarantee that you have any waiters, and the broadcast is not "buffered" so if they're not ready nothing happens. Periodic broadcasts / a system where a single failure doesn't matter fit just fine, but that's not what this code sample shows, nor is it called out in the text. |
Steven Littiebrant | Apr 04, 2018 |
Printed | Page 1 124 |
the simple pipeline example uses a function sleep(done, 1*time.Second, zeros) Maybe I missed it, but I couldn't find a definition of sleep in that chapter. Can you please clarify ? Thanks |
Edward Ishaq | Apr 21, 2019 |
Printed | Page 4 3rd |
Extract: "Indeed for this very book, I've gotten as many eyes as *possbile* on the code to try and mitigate this." 'possbile' must be 'possible'. |
Baumes | Jul 18, 2020 |
Page 8 first code sample, at the top of the page |
The body of the anonymous function on the second line is surrounded by unbalanced space. Where it reads: go func() { data++}() it should perhaps read¹: go func() { data++ }() ¹: gofmt concurrs. |
pancho horrillo | Apr 01, 2021 | |
Printed, PDF, Other Digital Version | Page 37-39 Concept of demo |
The author has used an example of two people walking TOWARDS each other down a hallway and playing a game of "hallway-shuffle". However, the code used to demonstrate it appears to neglect the fact that the two people in the hallway are facing opposite directions. One person's left is the other person's right. The output of the demo code is as follows: Alice is trying to scoot: left right left right left right left right left right Alice tosses her hands up in exasperation! Barbara is trying to scoot: left right left right left right left right left right Barbara tosses her hands up in exasperation! If this were the case in real life and Alice and Barbara each move to their 'left' then they will pass each other without issue. Am I missing something? |
Alex Matthews | Nov 26, 2020 |
Printed, PDF | Page 52 2nd paragraph |
> 2. Here we make the producer sleep for one second to make it less active than the observer goroutines. This should actually read “nanosecond”, not “second”. Or the code sample on the page before should be extended with a time.Second multiplier - but that would substantially change the outcome shown in the table. |
Anonymous | Nov 24, 2018 |
Printed | Page 57 3rd paragraph |
The statement given in sentence is incorrect: "Were it not for the clickRegistered waitgroup, we could colud call button.Clicked.Broadcast() multiple times...". Goroutine started by "subscribe" will be executed only once and terminate. Subsequent calls to button.Clicked.Broadcast() will have no effect. |
Marin Prcela | Oct 15, 2017 |
Page 57 After the output |
On page 57: You can see that with one call to Broadcast on the Clicked Cond, all three handlers are run. Were it not for the clickRegistered WaitGroup, we could call button.Clicked.Broadcast() multiple times, and each time all three handlers would be invoked. This is something channels can’t do easily and thus is one of the main reasons to utilize the Cond type. I don't understand the second and third sentences, because that one call of button.Clicked.Broadcast() makes all handers return, so multiple-times calls doesn't make sense. |
Yoshiki Shibata | Dec 01, 2018 | |
Printed | Page 57 top half |
The callout numbering is incorrect. 4 should be 3, 5 should be 4, 6 should be 5, 7 should be 6, 3 partly corresponds to 7. |
Sonia Hamilton | Aug 14, 2019 |
Printed | Page 61 middle of page |
"8 calculators were created" should read "4 calculators were created". I tested this by running the code. |
Sonia Hamilton | Aug 14, 2019 |
Printed | Page 63 2 |
We let the producer wait for 1s This should not be 1s, it should be 1 Nanosecond |
zhaoyanhui | Oct 10, 2020 |
Printed | Page 75 Table 3-2 |
Line 2 of table should read: "Open and Not Empty Value, true" |
Sonia Hamilton | Aug 14, 2019 |
Printed | Page 76 List 1, Item 4 |
"Ecapsulate ..." should be "Encapsulate ..." |
Samvel | Jan 13, 2019 |
Other Digital Version | 82 4th Paragraph |
"The second return value is a way for a read operation to indicate whther the read off..." should be ""The second return value is a way for a read operation to indicate whther the read of..."" |
Chengtao Wen | Apr 11, 2018 |
Printed | Page 86 4th paragraph "Ad hoc confinement..." |
"...whether it be set by the languages community..." should be - "...whether it be set by the language's community..." |
Anonymous | Dec 04, 2018 |
Printed | Page 91 2nd paragraph |
Currently printed: "Therefore, the strings channel will never actually gets any strings written onto it, ..." Should be: "Therefore, the strings channel will never actually get any strings written onto it, ...) |
Taavi Kivisik | Aug 15, 2019 |
Printed | Page 106 1st paragraph |
the first line saying "...constructs a buffered channel of integers with a length equal to..." while in all the related previous sample codes, it's using a unbufferred channel. |
Fanghao | Jun 17, 2022 |
Printed | Page 108 Table |
In this example the done channel, that is shared amongst all stages of the pipeline, is closed. The behavior seen in the table looks like only the generator receives a done and the rest of the pipeline completing normally. Wouldn't the outcome of this be nondeterministic, with the more likely outcome of all or at least some stages of the pipeline closing simultaneously. |
Tobias Mühlberger | Aug 28, 2017 |
Printed | Page 110 Code example at top of page |
There is a line in the example: case takeStream <- <- valueStream: That should be: case takeStream <- valueStream: |
Dave Rolsky | Apr 30, 2018 |
Printed | Page 112 first block of code |
In the function "toString", the following line will of course panic if anything but a string is passed in: "case stringStream <- v.(string):" |
Sonia Hamilton | Aug 15, 2019 |
Printed | Page 113 halfway down the page |
The following line has an extra <- case takeStream <- <- valueStream: |
Sonia Hamilton | Aug 15, 2019 |
Printed | Page 113 halfway down the page |
The two arrows are correct, my previous errata is incorrect... |
Sonia Hamilton | Aug 15, 2019 |
Printed | Page 117 1st paragraph |
The text says "fanning means multplexing or joining together multiple streams of data ..." Multiplexing is the fan _out_ part. The fan in is demultiplexing (or demuxing). |
Dave Rolsky | Apr 30, 2018 |
Printed | Page 125 sample code |
`buffer := buffer(done, 2, short)` should be error on compile time as both are not in the same type. (channel and function) I assume the following is the intended code. ``` done := make(chan interface{}) defer close(done) zeros := take(done, 3, repeat(done, 0)) short := sleep(done, 1*time.Second, zeros) buf := buffer(done, 2, short) // Buffers sends from short by 2 long := sleep(done, 4*time.Second, buf) pipeline := long ``` |
Yoshi Yamaguchi | Aug 24, 2018 |
Printed | Page 127 code sample |
Instead of: func BenchmarkBufferedWrite(b *testing.B) { bufferredFile := bufio.NewWriter(tmpFileOrFatal()) performWrite(b, bufio.NewWriter(bufferredFile)) } I guess should be: func BenchmarkBufferedWrite(b *testing.B) { bufferredFile := bufio.NewWriter(tmpFileOrFatal()) performWrite(b, bufferredFile) } So we don't need to wrap twice in a buffer. |
José Luis Martínez de la Riva Manzano | Sep 02, 2018 |
Printed | Page 127 code snippet, 2nd function |
The bufferredFile is already a buffered writer in BenchmarkBufferedWrite function. So, the change should be from: performaWrite(b, bufio.NewWriter(bufferedFile)) to: performaWrite(b, bufferedFile) |
Samvel | Jan 13, 2019 |
Printed | Page 130 last 2 equations |
The equation should resolve into: Lr = 13r because the second line from the bottom is equivalent to: x - 3 = 10 Which leads to: x = 13 |
Samvel | Jan 13, 2019 |
Printed | Page 131 1st code snippet |
indicate that CancelFunc is a function, and Context is an interface, e.g. change from: type CancelFunc type Context to: type CancelFunc func() type Context interface { ... } |
Samvel | Jan 13, 2019 |
Printed | Page 136 diagram |
One side should be Farewell instead of printGreeting |
Samvel | Jan 13, 2019 |
Printed | Page 139 diagram |
one side of the graph should be Farewell |
Samvel | Jan 13, 2019 |
Printed | Page 152 first sample code |
Though there's a function call to `isGloballyExec()` in the function `runJob`, this will be error on compile time, as `isGloballyExec` is in "lowlevel" package. The sample code should be: ``` type IntermediateErr struct { error } func runJob(id string) error { const jobBinPath = "/bad/job/binary" isExecutable, err := lowlevel.isGloballyExec(jobBinPath) if err != nil { return err } else if isExecutable == false { return wrapError(nil, "job binary is not executable") } return exec.Command(jobBinPath, "--id="+id).Run() } ``` |
Yoshi Yamaguchi | Aug 24, 2018 |
Printed | Page 159 2nd paragraph |
The paragraph starts with the lower case. |
Samvel | Jan 13, 2019 |
Printed | Page 159 2nd paragraph |
The first letter of the first word in the sentence is not capitalised: “there’s another problem lurking here as well: ...” Should be: “There’s another problem lurking here as well: ...” |
James Allured | Apr 19, 2021 |
Printed | Page 160 diagram |
he text talks about Stage A2, while the diagram has "Stage 2", e.g. it should be "Stage A2" |
Samvel | Jan 13, 2019 |
Printed | Page 167 callout #2 |
"results" should be "workStream" ie "We don't want to include this in the same select black as the send on workStream because..." |
Sonia Hamilton | Aug 19, 2019 |
Printed | Page 172 2nd to last paragraph |
The paragraph talks about "nanoseconds" but the code is using seconds. |
Dave Rolsky | May 08, 2018 |
Printed | Page 189 code snippet |
it looks like the wardDone channel is shared between the old and new wards; what happens in the following sequence of events: the channel closes the new ward starts and overwrites the wardDone the old ward accesses the wardDone which channel will the old ward access: will it be the new channel? It might be better (but take more space) to encapsulate the ward hearbeats and done channels into a structure. |
Samvel | Jan 13, 2019 |
Printed | Page 191 line -8 |
zeros := take(done, 3, repeat(done, 0)) => zeros := take(done, repeat(done, 0), 3) |
Soojin Nam | Feb 02, 2020 |
Printed | Page 192 code example |
it seems there are multiple channel leaks, namely: - heartbeat - intChanStream |
Samvel | Jan 13, 2019 |
Printed | Page 202 1st paragraph |
the text refers to the fib(2) called from the fib(3), and the notation is <caller>-<callee>, e.g. 3-2. Thus, is should change from: fib(3) (i.e. 3-1) to: fib(3) (i.e. 3-2) |
Samvel | Jan 13, 2019 |
Printed | Page 207 table, T2 call stack column |
the T1 finished calculating fib(2) and is in the state (returns 2), while T2 is at the moment waiting for the goroutines to finish fib(4). Thus, the table should change from: (returns 2) to: fib(4) (unrealized join point) |
Samvel | Jan 13, 2019 |
Printed | Page 208 single thread tables, T1 work queue column |
change from: main to: cont. of main since the table describes continuation stealing. |
Samvel | Jan 13, 2019 |
Printed | Page 209 tables 3-4, T1 work queue column |
should have cont of fib(3) (unrealized join point) |
Samvel | Jan 13, 2019 |
Printed | Page 209 tables 6-7, T1 work queue column |
should have cont of fib(4) (unrealized join point) |
Samvel | Jan 13, 2019 |