Slices recycling issues

With data structure with an underlying byte slice, such as bytes.Buffer, we should be careful when using them combined with sync.Pool or a similar mechanism of recycling. Let's change the previous example and collect the buffer's bytes instead of printing them to standard output. The following is an example code for this:

var (
list = make([][]byte, 20)
m sync.Mutex
)
for i := 0; i < 20; i++ {
go func(v int) {
time.Sleep(time.Second * time.Duration(1+v/4))
b := Get()
defer func() {
Put(b)
wg.Done()
}()
fmt.Fprintf(b, "Goroutine %2d using %p, after %.0fs\n", v, b, time.Since(start).Seconds())
m.Lock()
list[v] = b.Bytes()
m.Unlock()
}(i)
}
wg.Wait()

So, what happens when we print the list of byte slices? We can see this in the following example:


for i := range list {
fmt.Printf("%d - %s", i, list[i])
}

We get an unexpected result as the buffers have been overwritten. That's because the buffers are reusing the same underlying slice and overriding the content with every new usage.

A solution to this problem is usually to execute a copy of the bytes, instead of just assigning them:

m.Lock()
list[v] = make([]byte, b.Len())
copy(list[v], b.Bytes())
m.Unlock()