-
Notifications
You must be signed in to change notification settings - Fork 150
/
object.go
213 lines (187 loc) · 5.5 KB
/
object.go
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package pool
import (
"fmt"
"sync"
"time"
"github.com/jolestar/go-commons-pool/v2/collections"
)
// PooledObjectState is PooledObjectState enum const
type PooledObjectState int
const (
// StateIdle in the queue, not in use. default value.
StateIdle PooledObjectState = iota
// StateAllocated in use.
StateAllocated
// StateEviction in the queue, currently being tested for possible eviction.
StateEviction
// StateEvictionReturnToHead not in the queue, currently being tested for possible eviction. An
// attempt to borrow the object was made while being tested which removed it
// from the queue. It should be returned to the head of the queue once
// eviction testing completes.
StateEvictionReturnToHead
// StateInvalid failed maintenance (e.g. eviction test or validation) and will be / has
// been destroyed
StateInvalid
// StateAbandoned Deemed abandoned, to be invalidated.
StateAbandoned
// StateReturning Returning to the pool
StateReturning
)
// TrackedUse allows pooled objects to make information available about when
// and how they were used available to the object pool. The object pool may, but
// is not required, to use this information to make more informed decisions when
// determining the state of a pooled object - for instance whether or not the
// object has been abandoned.
type TrackedUse interface {
// GetLastUsed Get the last time o object was used in ms.
GetLastUsed() time.Time
}
// PooledObject is the wrapper of origin object that is used to track the additional information,
// such as state, for the pooled objects.
type PooledObject struct {
// Object must be a pointer
Object interface{}
CreateTime time.Time
LastBorrowTime time.Time
LastReturnTime time.Time
//init equals CreateTime
LastUseTime time.Time
state PooledObjectState
BorrowedCount int32
lock sync.Mutex
}
// NewPooledObject return new init PooledObject
func NewPooledObject(object interface{}) *PooledObject {
time := time.Now()
return &PooledObject{Object: object, state: StateIdle, CreateTime: time, LastUseTime: time, LastBorrowTime: time, LastReturnTime: time}
}
// GetActiveTime return the time that this object last spent in the the active state
func (o *PooledObject) GetActiveTime() time.Duration {
// Take copies to avoid concurrent issues
rTime := o.LastReturnTime
bTime := o.LastBorrowTime
if rTime.After(bTime) {
return rTime.Sub(bTime)
}
return time.Since(bTime)
}
// GetIdleTime the time that this object last spend in the the idle state
func (o *PooledObject) GetIdleTime() time.Duration {
elapsed := time.Since(o.LastReturnTime)
// elapsed may be negative if:
// - another goroutine updates lastReturnTime during the calculation window
if elapsed >= 0 {
return elapsed
}
return 0
}
// GetLastUsedTime return an estimate of the last time this object was used.
func (o *PooledObject) GetLastUsedTime() time.Time {
trackedUse, ok := o.Object.(TrackedUse)
if ok && trackedUse.GetLastUsed().After(o.LastUseTime) {
return trackedUse.GetLastUsed()
}
return o.LastUseTime
}
func (o *PooledObject) doAllocate() bool {
if o.state == StateIdle {
o.state = StateAllocated
o.LastBorrowTime = time.Now()
o.LastUseTime = o.LastBorrowTime
o.BorrowedCount++
//if (logAbandoned) {
//borrowedBy = new AbandonedObjectCreatedException();
//}
return true
} else if o.state == StateEviction {
// TODO Allocate anyway and ignore eviction test
o.state = StateEvictionReturnToHead
return false
}
// TODO if validating and testOnBorrow == true then pre-allocate for
// performance
return false
}
// Allocate this object
func (o *PooledObject) Allocate() bool {
o.lock.Lock()
result := o.doAllocate()
o.lock.Unlock()
return result
}
func (o *PooledObject) doDeallocate() bool {
if o.state == StateAllocated ||
o.state == StateReturning {
o.state = StateIdle
o.LastReturnTime = time.Now()
//borrowedBy = nil;
return true
}
return false
}
// Deallocate this object
func (o *PooledObject) Deallocate() bool {
o.lock.Lock()
result := o.doDeallocate()
o.lock.Unlock()
return result
}
// Invalidate this object
func (o *PooledObject) Invalidate() {
o.lock.Lock()
o.invalidate()
o.lock.Unlock()
}
func (o *PooledObject) invalidate() {
o.state = StateInvalid
}
// GetState return current state of this object
func (o *PooledObject) GetState() PooledObjectState {
o.lock.Lock()
defer o.lock.Unlock()
return o.state
}
// MarkAbandoned mark this object to Abandoned state
func (o *PooledObject) MarkAbandoned() {
o.lock.Lock()
o.markAbandoned()
o.lock.Unlock()
}
func (o *PooledObject) markAbandoned() {
o.state = StateAbandoned
}
// MarkReturning mark this object to Returning state
func (o *PooledObject) MarkReturning() {
o.lock.Lock()
o.markReturning()
o.lock.Unlock()
}
func (o *PooledObject) markReturning() {
o.state = StateReturning
}
// StartEvictionTest attempt to place the pooled object in the EVICTION state
func (o *PooledObject) StartEvictionTest() bool {
o.lock.Lock()
defer o.lock.Unlock()
if o.state == StateIdle {
o.state = StateEviction
return true
}
return false
}
// EndEvictionTest called to inform the object that the eviction test has ended.
func (o *PooledObject) EndEvictionTest(idleQueue *collections.LinkedBlockingDeque) bool {
o.lock.Lock()
defer o.lock.Unlock()
if o.state == StateEviction {
o.state = StateIdle
return true
} else if o.state == StateEvictionReturnToHead {
o.state = StateIdle
if !idleQueue.OfferFirst(o) {
// TODO - Should never happen
panic(fmt.Errorf("Should never happen"))
}
}
return false
}