11package pool
22
33import (
4- "container/list"
54 "runtime"
65 "sort"
76 "sync"
87
98 "github.com/bodgit/sevenzip/internal/util"
9+ lru "github.com/hashicorp/golang-lru/v2"
1010)
1111
1212// Pooler is the interface implemented by a pool.
@@ -34,48 +34,54 @@ func (noopPool) Put(_ int64, rc util.SizeReadSeekCloser) (bool, error) {
3434}
3535
3636type 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 ]
4640}
4741
4842// NewPool returns a Pooler that uses a LRU strategy to maintain a fixed pool
4943// of util.SizeReadSeekCloser's keyed by their stream offset.
5044func 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+
5158 return & pool {
52- size : runtime .NumCPU (),
53- evictList : list .New (),
54- items : make (map [int64 ]* list.Element ),
59+ errs : errs ,
60+ cache : cache ,
5561 }, nil
5662}
5763
5864func (p * pool ) Get (offset int64 ) (util.SizeReadSeekCloser , bool ) {
5965 p .mutex .Lock ()
6066 defer p .mutex .Unlock ()
6167
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 )
6470
65- return ent . Value .( * entry ). value , true //nolint:forcetypeassert
71+ return reader , true
6672 }
6773
6874 // Sort keys in descending order
69- keys := p .keys ()
75+ keys := p .cache . Keys ()
7076 sort .Slice (keys , func (i , j int ) bool { return keys [i ] > keys [j ] })
7177
7278 for _ , k := range keys {
7379 // First key less than offset is the closest
7480 if k < offset {
75- ent := p .items [ k ]
76- _ = p .removeElement ( ent , false )
81+ reader , _ := p .cache . Get ( k )
82+ _ = p .cache . RemoveWithoutEvict ( k )
7783
78- return ent . Value .( * entry ). value , true //nolint:forcetypeassert
84+ return reader , true
7985 }
8086 }
8187
@@ -86,52 +92,13 @@ func (p *pool) Put(offset int64, rc util.SizeReadSeekCloser) (bool, error) {
8692 p .mutex .Lock ()
8793 defer p .mutex .Unlock ()
8894
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 )
13196
132- if cb {
133- return kv .value .Close ()
97+ select {
98+ case err := <- p .errs :
99+ return ok , err
100+ default :
134101 }
135102
136- return nil
103+ return ok , nil
137104}
0 commit comments