13164: Remove locking. Clean up after races in background instead.
[arvados.git] / services / keepstore / bufferpool_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "time"
9
10         . "gopkg.in/check.v1"
11 )
12
13 var _ = Suite(&BufferPoolSuite{})
14
15 type BufferPoolSuite struct{}
16
17 // Initialize a default-sized buffer pool for the benefit of test
18 // suites that don't run main().
19 func init() {
20         bufs = newBufferPool(theConfig.MaxBuffers, BlockSize)
21 }
22
23 // Restore sane default after bufferpool's own tests
24 func (s *BufferPoolSuite) TearDownTest(c *C) {
25         bufs = newBufferPool(theConfig.MaxBuffers, BlockSize)
26 }
27
28 func (s *BufferPoolSuite) TestBufferPoolBufSize(c *C) {
29         bufs := newBufferPool(2, 10)
30         b1 := bufs.Get(1)
31         bufs.Get(2)
32         bufs.Put(b1)
33         b3 := bufs.Get(3)
34         c.Check(len(b3), Equals, 3)
35 }
36
37 func (s *BufferPoolSuite) TestBufferPoolUnderLimit(c *C) {
38         bufs := newBufferPool(3, 10)
39         b1 := bufs.Get(10)
40         bufs.Get(10)
41         testBufferPoolRace(c, bufs, b1, "Get")
42 }
43
44 func (s *BufferPoolSuite) TestBufferPoolAtLimit(c *C) {
45         bufs := newBufferPool(2, 10)
46         b1 := bufs.Get(10)
47         bufs.Get(10)
48         testBufferPoolRace(c, bufs, b1, "Put")
49 }
50
51 func testBufferPoolRace(c *C, bufs *bufferPool, unused []byte, expectWin string) {
52         race := make(chan string)
53         go func() {
54                 bufs.Get(10)
55                 time.Sleep(time.Millisecond)
56                 race <- "Get"
57         }()
58         go func() {
59                 time.Sleep(10 * time.Millisecond)
60                 bufs.Put(unused)
61                 race <- "Put"
62         }()
63         c.Check(<-race, Equals, expectWin)
64         c.Check(<-race, Not(Equals), expectWin)
65         close(race)
66 }
67
68 func (s *BufferPoolSuite) TestBufferPoolReuse(c *C) {
69         bufs := newBufferPool(2, 10)
70         bufs.Get(10)
71         last := bufs.Get(10)
72         // The buffer pool is allowed to throw away unused buffers
73         // (e.g., during sync.Pool's garbage collection hook, in the
74         // the current implementation). However, if unused buffers are
75         // getting thrown away and reallocated more than {arbitrary
76         // frequency threshold} during a busy loop, it's not acting
77         // much like a buffer pool.
78         allocs := 1000
79         reuses := 0
80         for i := 0; i < allocs; i++ {
81                 bufs.Put(last)
82                 next := bufs.Get(10)
83                 copy(last, []byte("last"))
84                 copy(next, []byte("next"))
85                 if last[0] == 'n' {
86                         reuses++
87                 }
88                 last = next
89         }
90         c.Check(reuses > allocs*95/100, Equals, true)
91 }