@@ -18,21 +18,21 @@ const (
18
18
NoEjection = EjectionMode ("no-ejection" )
19
19
)
20
20
21
+ // StateIndex is a type of a state of a placeholder in a pool.
22
+ type StateIndex uint
23
+
24
+ const numberOfStates = 2
25
+ const ( // iota is reset to 0
26
+ stateFree StateIndex = iota
27
+ stateUsed
28
+ )
29
+
21
30
// EIndex is data type representing an entity index in Pool.
22
31
type EIndex uint32
23
32
24
33
// InvalidIndex is used when a link doesnt point anywhere, in other words it is an equivalent of a nil address.
25
34
const InvalidIndex EIndex = math .MaxUint32
26
35
27
- // A type dedicated to describe possible states of placeholders for entities in the pool.
28
- type StateType string
29
-
30
- // A placeholder in a free state can be used to store an entity.
31
- const stateFree StateType = "free-state"
32
-
33
- // A placeholder in a used state stores currently an entity.
34
- const stateUsed StateType = "used-state"
35
-
36
36
// poolEntity represents the data type that is maintained by
37
37
type poolEntity struct {
38
38
PoolEntity
@@ -64,8 +64,7 @@ func (p PoolEntity) Entity() flow.Entity {
64
64
65
65
type Pool struct {
66
66
logger zerolog.Logger
67
- free state // keeps track of free slots.
68
- used state // keeps track of allocated slots to cachedEntities.
67
+ states []state // keeps track of a slot's state
69
68
poolEntities []poolEntity
70
69
ejectionMode EjectionMode
71
70
}
@@ -74,16 +73,8 @@ type Pool struct {
74
73
// logger and a provided fixed size.
75
74
func NewHeroPool (sizeLimit uint32 , ejectionMode EjectionMode , logger zerolog.Logger ) * Pool {
76
75
l := & Pool {
77
- free : state {
78
- head : InvalidIndex ,
79
- tail : InvalidIndex ,
80
- size : 0 ,
81
- },
82
- used : state {
83
- head : InvalidIndex ,
84
- tail : InvalidIndex ,
85
- size : 0 ,
86
- },
76
+ //construcs states initialized to InvalidIndexes
77
+ states : NewStates (numberOfStates ),
87
78
poolEntities : make ([]poolEntity , sizeLimit ),
88
79
ejectionMode : ejectionMode ,
89
80
logger : logger ,
@@ -105,8 +96,16 @@ func (p *Pool) setDefaultNodeLinkValues() {
105
96
106
97
// initFreeEntities initializes the free double linked-list with the indices of all cached entity poolEntities.
107
98
func (p * Pool ) initFreeEntities () {
108
- for i := 0 ; i < len (p .poolEntities ); i ++ {
109
- p .appendEntity (stateFree , EIndex (i ))
99
+ p .states [stateFree ].head = 0
100
+ p .states [stateFree ].tail = 0
101
+ p .poolEntities [p .states [stateFree ].head ].node .prev = InvalidIndex
102
+ p .poolEntities [p .states [stateFree ].tail ].node .next = InvalidIndex
103
+ p .states [stateFree ].size = 1
104
+ for i := 1 ; i < len (p .poolEntities ); i ++ {
105
+ p .connect (p .states [stateFree ].tail , EIndex (i ))
106
+ p .states [stateFree ].tail = EIndex (i )
107
+ p .poolEntities [p .states [stateFree ].tail ].node .next = InvalidIndex
108
+ p .states [stateFree ].size ++
110
109
}
111
110
}
112
111
@@ -124,7 +123,7 @@ func (p *Pool) Add(entityId flow.Identifier, entity flow.Entity, owner uint64) (
124
123
p .poolEntities [entityIndex ].entity = entity
125
124
p .poolEntities [entityIndex ].id = entityId
126
125
p .poolEntities [entityIndex ].owner = owner
127
- p .appendEntity ( stateUsed , entityIndex )
126
+ p .switchState ( stateFree , stateUsed , entityIndex )
128
127
}
129
128
130
129
return entityIndex , slotAvailable , ejectedEntity
@@ -137,10 +136,10 @@ func (p *Pool) Get(entityIndex EIndex) (flow.Identifier, flow.Entity, uint64) {
137
136
138
137
// All returns all stored entities in this pool.
139
138
func (p Pool ) All () []PoolEntity {
140
- all := make ([]PoolEntity , p .used .size )
141
- next := p .used .head
139
+ all := make ([]PoolEntity , p .states [ stateUsed ] .size )
140
+ next := p .states [ stateUsed ] .head
142
141
143
- for i := uint32 (0 ); i < p .used .size ; i ++ {
142
+ for i := uint32 (0 ); i < p .states [ stateUsed ] .size ; i ++ {
144
143
e := p .poolEntities [next ]
145
144
all [i ] = e .PoolEntity
146
145
next = e .node .next
@@ -152,14 +151,17 @@ func (p Pool) All() []PoolEntity {
152
151
// Head returns the head of used items. Assuming no ejection happened and pool never goes beyond limit, Head returns
153
152
// the first inserted element.
154
153
func (p Pool ) Head () (flow.Entity , bool ) {
155
- if p .used .size == 0 {
154
+ if p .states [ stateUsed ] .size == 0 {
156
155
return nil , false
157
156
}
158
- e := p .poolEntities [p .used .head ]
157
+ e := p .poolEntities [p .states [ stateUsed ] .head ]
159
158
return e .Entity (), true
160
159
}
161
160
162
161
// sliceIndexForEntity returns a slice index which hosts the next entity to be added to the list.
162
+ // This index is invalid if there are no available slots or ejection could not be performed.
163
+ // If the valid index is returned then it is guaranteed that it corresponds to a free list head.
164
+ // Thus when filled with a new entity a switchState must be applied.
163
165
//
164
166
// The first boolean return value (hasAvailableSlot) says whether pool has an available slot.
165
167
// Pool goes out of available slots if it is full and no ejection is set.
@@ -171,18 +173,18 @@ func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEn
171
173
// LRU ejection
172
174
// the used head is the oldest entity, so we turn the used head to a free head here.
173
175
invalidatedEntity := p .invalidateUsedHead ()
174
- return p .claimFreeHead () , true , invalidatedEntity
176
+ return p .states [ stateFree ]. head , true , invalidatedEntity
175
177
}
176
178
177
- if p .free .size == 0 {
179
+ if p .states [ stateFree ] .size == 0 {
178
180
// the free list is empty, so we are out of space, and we need to eject.
179
181
switch p .ejectionMode {
180
182
case NoEjection :
181
183
// pool is set for no ejection, hence, no slice index is selected, abort immediately.
182
184
return InvalidIndex , false , nil
183
185
case RandomEjection :
184
186
// we only eject randomly when the pool is full and random ejection is on.
185
- random , err := rand .Uint32n (p .used .size )
187
+ random , err := rand .Uint32n (p .states [ stateUsed ] .size )
186
188
if err != nil {
187
189
p .logger .Fatal ().Err (err ).
188
190
Msg ("hero pool random ejection failed - falling back to LRU ejection" )
@@ -191,45 +193,44 @@ func (p *Pool) sliceIndexForEntity() (i EIndex, hasAvailableSlot bool, ejectedEn
191
193
}
192
194
randomIndex := EIndex (random )
193
195
invalidatedEntity := p .invalidateEntityAtIndex (randomIndex )
194
- return p .claimFreeHead () , true , invalidatedEntity
196
+ return p .states [ stateFree ]. head , true , invalidatedEntity
195
197
case LRUEjection :
196
198
// LRU ejection
197
199
return lruEject ()
198
200
}
199
201
}
200
202
201
- // claiming the head of free list as the slice index for the next entity to be added
202
- return p .claimFreeHead () , true , nil
203
+ // returning the head of free list as the slice index for the next entity to be added
204
+ return p .states [ stateFree ]. head , true , nil
203
205
}
204
206
205
207
// Size returns total number of entities that this list maintains.
206
208
func (p Pool ) Size () uint32 {
207
- return p .used .size
209
+ return p .states [ stateUsed ] .size
208
210
}
209
211
210
212
// getHeads returns entities corresponding to the used and free heads.
211
- func (p * Pool ) getHeads () (* poolEntity , * poolEntity ) {
213
+ func (p Pool ) getHeads () (* poolEntity , * poolEntity ) {
212
214
var usedHead , freeHead * poolEntity
213
- if p .used .size != 0 {
214
- usedHead = & p .poolEntities [p .used .head ]
215
+ if p .states [ stateUsed ] .size != 0 {
216
+ usedHead = & p .poolEntities [p .states [ stateUsed ] .head ]
215
217
}
216
218
217
- if p .free .size != 0 {
218
- freeHead = & p .poolEntities [p .free .head ]
219
+ if p .states [ stateFree ] .size != 0 {
220
+ freeHead = & p .poolEntities [p .states [ stateFree ] .head ]
219
221
}
220
222
221
223
return usedHead , freeHead
222
224
}
223
225
224
226
// getTails returns entities corresponding to the used and free tails.
225
- func (p * Pool ) getTails () (* poolEntity , * poolEntity ) {
227
+ func (p Pool ) getTails () (* poolEntity , * poolEntity ) {
226
228
var usedTail , freeTail * poolEntity
227
- if p .used .size != 0 {
228
- usedTail = & p .poolEntities [p .used .tail ]
229
+ if p .states [ stateUsed ] .size != 0 {
230
+ usedTail = & p .poolEntities [p .states [ stateUsed ] .tail ]
229
231
}
230
-
231
- if p .free .size != 0 {
232
- freeTail = & p .poolEntities [p .free .tail ]
232
+ if p .states [stateFree ].size != 0 {
233
+ freeTail = & p .poolEntities [p .states [stateFree ].tail ]
233
234
}
234
235
235
236
return usedTail , freeTail
@@ -245,18 +246,10 @@ func (p *Pool) connect(prev EIndex, next EIndex) {
245
246
// also removes the entity the invalidated head is presenting and appends the
246
247
// node represented by the used head to the tail of the free list.
247
248
func (p * Pool ) invalidateUsedHead () flow.Entity {
248
- headSliceIndex := p .used .head
249
+ headSliceIndex := p .states [ stateUsed ] .head
249
250
return p .invalidateEntityAtIndex (headSliceIndex )
250
251
}
251
252
252
- // claimFreeHead moves the free head forward, and returns the slice index of the
253
- // old free head to host a new entity.
254
- func (p * Pool ) claimFreeHead () EIndex {
255
- oldFreeHeadIndex := p .free .head
256
- p .removeEntity (stateFree , oldFreeHeadIndex )
257
- return oldFreeHeadIndex
258
- }
259
-
260
253
// Remove removes entity corresponding to given getSliceIndex from the list.
261
254
func (p * Pool ) Remove (sliceIndex EIndex ) flow.Entity {
262
255
return p .invalidateEntityAtIndex (sliceIndex )
@@ -270,11 +263,9 @@ func (p *Pool) invalidateEntityAtIndex(sliceIndex EIndex) flow.Entity {
270
263
if invalidatedEntity == nil {
271
264
panic (fmt .Sprintf ("removing an entity from an empty slot with an index : %d" , sliceIndex ))
272
265
}
273
- p .removeEntity (stateUsed , sliceIndex )
266
+ p .switchState (stateUsed , stateFree , sliceIndex )
274
267
p .poolEntities [sliceIndex ].id = flow .ZeroID
275
268
p .poolEntities [sliceIndex ].entity = nil
276
- p .appendEntity (stateFree , EIndex (sliceIndex ))
277
-
278
269
return invalidatedEntity
279
270
}
280
271
@@ -292,77 +283,46 @@ func (p *Pool) isInvalidated(sliceIndex EIndex) bool {
292
283
return true
293
284
}
294
285
295
- // utility method that removes an entity from one of the states.
296
- // NOTE: a removed entity has to be added to another state.
297
- func (p * Pool ) removeEntity (stateType StateType , entityIndex EIndex ) {
298
- var s * state = nil
299
- switch stateType {
300
- case stateFree :
301
- s = & p .free
302
- case stateUsed :
303
- s = & p .used
304
- default :
305
- panic (fmt .Sprintf ("unknown state type: %s" , stateType ))
306
- }
307
-
308
- if s .size == 0 {
286
+ // switches state of an entity.
287
+ func (p * Pool ) switchState (stateFrom StateIndex , stateTo StateIndex , entityIndex EIndex ) {
288
+ // Remove from stateFrom list
289
+ if p .states [stateFrom ].size == 0 {
309
290
panic ("Removing an entity from an empty list" )
310
- }
311
- if s .size == 1 {
312
- // here set to InvalidIndex
313
- s .head = InvalidIndex
314
- s .tail = InvalidIndex
315
- s .size --
316
- p .poolEntities [entityIndex ].node .next = InvalidIndex
317
- p .poolEntities [entityIndex ].node .prev = InvalidIndex
318
- return
319
- }
320
- node := p .poolEntities [entityIndex ].node
321
-
322
- if entityIndex != s .head && entityIndex != s .tail {
323
- // links next and prev elements for non-head and non-tail element
324
- p .connect (node .prev , node .next )
325
- }
326
-
327
- if entityIndex == s .head {
328
- // moves head forward
329
- s .head = node .next
330
- p .poolEntities [s .head ].node .prev = InvalidIndex
331
- }
291
+ } else if p .states [stateFrom ].size == 1 {
292
+ p .states [stateFrom ].head = InvalidIndex
293
+ p .states [stateFrom ].tail = InvalidIndex
294
+ } else {
295
+ node := p .poolEntities [entityIndex ].node
296
+
297
+ if entityIndex != p .states [stateFrom ].head && entityIndex != p .states [stateFrom ].tail {
298
+ // links next and prev elements for non-head and non-tail element
299
+ p .connect (node .prev , node .next )
300
+ }
332
301
333
- if entityIndex == s .tail {
334
- // moves tail backwards
335
- s .tail = node .prev
336
- p .poolEntities [s .tail ].node .next = InvalidIndex
337
- }
338
- s .size --
339
- p .poolEntities [entityIndex ].node .next = InvalidIndex
340
- p .poolEntities [entityIndex ].node .prev = InvalidIndex
341
- }
302
+ if entityIndex == p .states [stateFrom ].head {
303
+ // moves head forward
304
+ p .states [stateFrom ].head = node .next
305
+ p .poolEntities [p .states [stateFrom ].head ].node .prev = InvalidIndex
306
+ }
342
307
343
- // appends an entity to the tail of the state or creates a first element.
344
- // NOTE: entity should not be in any list before this method is applied
345
- func (p * Pool ) appendEntity (stateType StateType , entityIndex EIndex ) {
346
- var s * state = nil
347
- switch stateType {
348
- case stateFree :
349
- s = & p .free
350
- case stateUsed :
351
- s = & p .used
352
- default :
353
- panic (fmt .Sprintf ("unknown state type: %s" , stateType ))
308
+ if entityIndex == p .states [stateFrom ].tail {
309
+ // moves tail backwards
310
+ p .states [stateFrom ].tail = node .prev
311
+ p .poolEntities [p .states [stateFrom ].tail ].node .next = InvalidIndex
312
+ }
354
313
}
355
-
356
- if s .size == 0 {
357
- s .head = entityIndex
358
- s .tail = entityIndex
359
- p .poolEntities [s .head ].node .prev = InvalidIndex
360
- p .poolEntities [s .tail ].node .next = InvalidIndex
361
- s .size = 1
362
- return
314
+ p .states [stateFrom ].size --
315
+
316
+ // Add to stateTo list
317
+ if p .states [stateTo ].size == 0 {
318
+ p .states [stateTo ].head = entityIndex
319
+ p .states [stateTo ].tail = entityIndex
320
+ p .poolEntities [p .states [stateTo ].head ].node .prev = InvalidIndex
321
+ p .poolEntities [p .states [stateTo ].tail ].node .next = InvalidIndex
322
+ } else {
323
+ p .connect (p .states [stateTo ].tail , entityIndex )
324
+ p .states [stateTo ].tail = entityIndex
325
+ p .poolEntities [p .states [stateTo ].tail ].node .next = InvalidIndex
363
326
}
364
- p .connect (s .tail , entityIndex )
365
- s .size ++
366
- s .tail = entityIndex
367
- p .poolEntities [s .tail ].node .next = InvalidIndex
327
+ p .states [stateTo ].size ++
368
328
}
0 commit comments