1
1
package pool
2
2
3
3
import (
4
- "container/list"
5
4
"runtime"
6
5
"sort"
7
6
"sync"
8
7
9
8
"github.com/bodgit/sevenzip/internal/util"
9
+ lru "github.com/hashicorp/golang-lru/v2"
10
10
)
11
11
12
12
// Pooler is the interface implemented by a pool.
@@ -34,48 +34,54 @@ func (noopPool) Put(_ int64, rc util.SizeReadSeekCloser) (bool, error) {
34
34
}
35
35
36
36
type pool struct {
37
- mutex sync.Mutex
38
- size int
39
- evictList * list.List
40
- items map [int64 ]* list.Element
41
- }
42
-
43
- type entry struct {
44
- key int64
45
- value util.SizeReadSeekCloser
37
+ mutex sync.Mutex
38
+ errs chan error
39
+ cache * lru.Cache [int64 , util.SizeReadSeekCloser ]
46
40
}
47
41
48
42
// NewPool returns a Pooler that uses a LRU strategy to maintain a fixed pool
49
43
// of util.SizeReadSeekCloser's keyed by their stream offset.
50
44
func NewPool () (Pooler , error ) {
45
+ errs := make (chan error )
46
+
47
+ cache , err := lru .NewWithEvict [int64 , util.SizeReadSeekCloser ](
48
+ runtime .NumCPU (),
49
+ func (_ int64 , value util.SizeReadSeekCloser ) {
50
+ if err := value .Close (); err != nil {
51
+ errs <- err
52
+ }
53
+ })
54
+ if err != nil {
55
+ return nil , err
56
+ }
57
+
51
58
return & pool {
52
- size : runtime .NumCPU (),
53
- evictList : list .New (),
54
- items : make (map [int64 ]* list.Element ),
59
+ errs : errs ,
60
+ cache : cache ,
55
61
}, nil
56
62
}
57
63
58
64
func (p * pool ) Get (offset int64 ) (util.SizeReadSeekCloser , bool ) {
59
65
p .mutex .Lock ()
60
66
defer p .mutex .Unlock ()
61
67
62
- if ent , ok := p .items [ offset ] ; ok {
63
- _ = p .removeElement ( ent , false )
68
+ if reader , ok := p .cache . Get ( offset ) ; ok {
69
+ _ = p .cache . RemoveWithoutEvict ( offset )
64
70
65
- return ent . Value .( * entry ). value , true //nolint:forcetypeassert
71
+ return reader , true
66
72
}
67
73
68
74
// Sort keys in descending order
69
- keys := p .keys ()
75
+ keys := p .cache . Keys ()
70
76
sort .Slice (keys , func (i , j int ) bool { return keys [i ] > keys [j ] })
71
77
72
78
for _ , k := range keys {
73
79
// First key less than offset is the closest
74
80
if k < offset {
75
- ent := p .items [ k ]
76
- _ = p .removeElement ( ent , false )
81
+ reader , _ := p .cache . Get ( k )
82
+ _ = p .cache . RemoveWithoutEvict ( k )
77
83
78
- return ent . Value .( * entry ). value , true //nolint:forcetypeassert
84
+ return reader , true
79
85
}
80
86
}
81
87
@@ -86,52 +92,13 @@ func (p *pool) Put(offset int64, rc util.SizeReadSeekCloser) (bool, error) {
86
92
p .mutex .Lock ()
87
93
defer p .mutex .Unlock ()
88
94
89
- if _ , ok := p .items [offset ]; ok {
90
- return false , nil
91
- }
92
-
93
- ent := & entry {offset , rc }
94
- entry := p .evictList .PushFront (ent )
95
- p .items [offset ] = entry
96
-
97
- var err error
98
-
99
- evict := p .evictList .Len () > p .size
100
- if evict {
101
- err = p .removeOldest ()
102
- }
103
-
104
- return evict , err
105
- }
106
-
107
- func (p * pool ) keys () []int64 {
108
- keys := make ([]int64 , len (p .items ))
109
- i := 0
110
-
111
- for ent := p .evictList .Back (); ent != nil ; ent = ent .Prev () {
112
- keys [i ] = ent .Value .(* entry ).key //nolint:forcetypeassert
113
- i ++
114
- }
115
-
116
- return keys
117
- }
118
-
119
- func (p * pool ) removeOldest () error {
120
- if ent := p .evictList .Back (); ent != nil {
121
- return p .removeElement (ent , true )
122
- }
123
-
124
- return nil
125
- }
126
-
127
- func (p * pool ) removeElement (e * list.Element , cb bool ) error {
128
- p .evictList .Remove (e )
129
- kv := e .Value .(* entry ) //nolint:forcetypeassert
130
- delete (p .items , kv .key )
95
+ _ , ok := p .cache .ContainsOrAdd (offset , rc )
131
96
132
- if cb {
133
- return kv .value .Close ()
97
+ select {
98
+ case err := <- p .errs :
99
+ return ok , err
100
+ default :
134
101
}
135
102
136
- return nil
103
+ return ok , nil
137
104
}
0 commit comments