File: allocator_test.go

package info (click to toggle)
golang-github-pkg-sftp 1.13.6-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid, trixie
  • size: 876 kB
  • sloc: makefile: 27
file content (135 lines) | stat: -rw-r--r-- 5,656 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package sftp

import (
	"strconv"
	"sync/atomic"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestAllocator(t *testing.T) {
	allocator := newAllocator()
	// get a page for request order id 1
	page := allocator.GetPage(1)
	page[1] = uint8(1)
	assert.Equal(t, maxMsgLength, len(page))
	assert.Equal(t, 1, allocator.countUsedPages())
	// get another page for request order id 1, we now have 2 used pages
	page = allocator.GetPage(1)
	page[0] = uint8(2)
	assert.Equal(t, 2, allocator.countUsedPages())
	// get another page for request order id 1, we now have 3 used pages
	page = allocator.GetPage(1)
	page[2] = uint8(3)
	assert.Equal(t, 3, allocator.countUsedPages())
	// release the page for request order id 1, we now have 3 available pages
	allocator.ReleasePages(1)
	assert.NotContains(t, allocator.used, 1)
	assert.Equal(t, 3, allocator.countAvailablePages())
	// get a page for request order id 2
	// we get the latest released page, let's verify that by checking the previously written values
	// so we are sure we are reusing a previously allocated page
	page = allocator.GetPage(2)
	assert.Equal(t, uint8(3), page[2])
	assert.Equal(t, 2, allocator.countAvailablePages())
	assert.Equal(t, 1, allocator.countUsedPages())
	page = allocator.GetPage(2)
	assert.Equal(t, uint8(2), page[0])
	assert.Equal(t, 1, allocator.countAvailablePages())
	assert.Equal(t, 2, allocator.countUsedPages())
	page = allocator.GetPage(2)
	assert.Equal(t, uint8(1), page[1])
	// we now have 3 used pages for request order id 2 and no available pages
	assert.Equal(t, 0, allocator.countAvailablePages())
	assert.Equal(t, 3, allocator.countUsedPages())
	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
	// release some request order id with no allocated pages, should have no effect
	allocator.ReleasePages(1)
	allocator.ReleasePages(3)
	assert.Equal(t, 0, allocator.countAvailablePages())
	assert.Equal(t, 3, allocator.countUsedPages())
	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
	// now get some pages for another request order id
	allocator.GetPage(3)
	// we now must have 3 used pages for request order id 2 and 1 used page for request order id 3
	assert.Equal(t, 0, allocator.countAvailablePages())
	assert.Equal(t, 4, allocator.countUsedPages())
	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
	assert.True(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be used")
	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
	// get another page for request order id 3
	allocator.GetPage(3)
	assert.Equal(t, 0, allocator.countAvailablePages())
	assert.Equal(t, 5, allocator.countUsedPages())
	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
	assert.True(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be used")
	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
	// now release the pages for request order id 3
	allocator.ReleasePages(3)
	assert.Equal(t, 2, allocator.countAvailablePages())
	assert.Equal(t, 3, allocator.countUsedPages())
	assert.True(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be used")
	assert.False(t, allocator.isRequestOrderIDUsed(1), "page with request order id 1 must be not used")
	assert.False(t, allocator.isRequestOrderIDUsed(3), "page with request order id 3 must be not used")
	// again check we are reusing previously allocated pages.
	// We have written nothing to the 2 last requested page so release them and get the third one
	allocator.ReleasePages(2)
	assert.Equal(t, 5, allocator.countAvailablePages())
	assert.Equal(t, 0, allocator.countUsedPages())
	assert.False(t, allocator.isRequestOrderIDUsed(2), "page with request order id 2 must be not used")
	allocator.GetPage(4)
	allocator.GetPage(4)
	page = allocator.GetPage(4)
	assert.Equal(t, uint8(3), page[2])
	assert.Equal(t, 2, allocator.countAvailablePages())
	assert.Equal(t, 3, allocator.countUsedPages())
	assert.True(t, allocator.isRequestOrderIDUsed(4), "page with request order id 4 must be used")
	// free the allocator
	allocator.Free()
	assert.Equal(t, 0, allocator.countAvailablePages())
	assert.Equal(t, 0, allocator.countUsedPages())
}

func BenchmarkAllocatorSerial(b *testing.B) {
	allocator := newAllocator()
	for i := 0; i < b.N; i++ {
		benchAllocator(allocator, uint32(i))
	}
}

func BenchmarkAllocatorParallel(b *testing.B) {
	var counter uint32
	allocator := newAllocator()
	for i := 1; i <= 8; i *= 2 {
		b.Run(strconv.Itoa(i), func(b *testing.B) {
			b.SetParallelism(i)
			b.RunParallel(func(pb *testing.PB) {
				for pb.Next() {
					benchAllocator(allocator, atomic.AddUint32(&counter, 1))
				}
			})
		})
	}
}

func benchAllocator(allocator *allocator, requestOrderID uint32) {
	// simulates the page requested in recvPacket
	allocator.GetPage(requestOrderID)
	// simulates the page requested in fileget for downloads
	allocator.GetPage(requestOrderID)
	// release the allocated pages
	allocator.ReleasePages(requestOrderID)
}

// useful for debug
func printAllocatorContents(allocator *allocator) {
	for o, u := range allocator.used {
		debug("used order id: %v, values: %+v", o, u)
	}
	for _, v := range allocator.available {
		debug("available, values: %+v", v)
	}
}