1
1
package io .github .xanthic .cache .provider .ehcache ;
2
2
3
- import io .github .xanthic .cache .core .LockedAbstractCache ;
4
- import lombok .EqualsAndHashCode ;
3
+ import io .github .xanthic .cache .api .Cache ;
5
4
import lombok .Value ;
5
+ import org .jetbrains .annotations .ApiStatus ;
6
6
import org .jetbrains .annotations .NotNull ;
7
+ import org .jetbrains .annotations .Nullable ;
7
8
8
9
import java .util .Map ;
9
10
import java .util .function .BiConsumer ;
11
+ import java .util .function .BiFunction ;
12
+ import java .util .function .Function ;
10
13
11
14
@ Value
12
- @ EqualsAndHashCode ( callSuper = false )
15
+ @ ApiStatus . Internal
13
16
@ SuppressWarnings ("unchecked" )
14
- public class EhcacheDelegate <K , V > extends LockedAbstractCache <K , V > {
17
+ public class EhcacheDelegate <K , V > implements Cache <K , V > {
15
18
org .ehcache .Cache <Object , Object > cache ;
16
19
17
20
@ Override
18
- protected V getUnlocked (@ NotNull K key ) {
21
+ public @ Nullable V get (@ NotNull K key ) {
19
22
return (V ) cache .get (key );
20
23
}
21
24
22
25
@ Override
23
- protected void putUnlocked (@ NotNull K key , @ NotNull V value ) {
24
- cache .put (key , value );
26
+ public @ Nullable V put (@ NotNull K key , @ NotNull V value ) {
27
+ Object prev = cache .get (key );
28
+ while (true ) {
29
+ if (prev == null ) {
30
+ Object latest = cache .putIfAbsent (key , value );
31
+ if (latest == null ) {
32
+ return null ;
33
+ }
34
+ prev = latest ;
35
+ } else {
36
+ if (cache .replace (key , prev , value )) {
37
+ return (V ) prev ;
38
+ }
39
+ prev = cache .get (key );
40
+ }
41
+ }
25
42
}
26
43
27
44
@ Override
28
- protected void removeUnlocked (@ NotNull K key ) {
29
- cache .remove (key );
45
+ public @ Nullable V remove (@ NotNull K key ) {
46
+ while (true ) {
47
+ Object prev = cache .get (key );
48
+ if (prev == null ) {
49
+ return null ;
50
+ }
51
+
52
+ if (cache .remove (key , prev )) {
53
+ return (V ) prev ;
54
+ }
55
+ }
30
56
}
31
57
32
58
@ Override
33
- protected void clearUnlocked () {
59
+ public void clear () {
34
60
cache .clear ();
35
61
}
36
62
37
63
@ Override
38
- protected long sizeUnlocked () {
64
+ public long size () {
39
65
long n = 0 ;
40
66
for (org .ehcache .Cache .Entry <Object , Object > ignored : cache ) {
41
67
n ++;
@@ -44,35 +70,116 @@ protected long sizeUnlocked() {
44
70
}
45
71
46
72
@ Override
47
- public V putIfAbsent (@ NotNull K key , @ NotNull V value ) {
48
- return read (() -> (V ) cache .putIfAbsent (key , value ));
73
+ public @ Nullable V compute (@ NotNull K key , @ NotNull BiFunction <? super K , ? super V , ? extends V > computeFunc ) {
74
+ Object old = cache .get (key );
75
+ while (true ) {
76
+ V computed = computeFunc .apply (key , (V ) old );
77
+ if (computed == null ) {
78
+ if (old == null || cache .remove (key , old )) {
79
+ return null ;
80
+ }
81
+ } else {
82
+ if (old == null ) {
83
+ Object latest = cache .putIfAbsent (key , computed );
84
+ if (latest == null ) {
85
+ return computed ;
86
+ } else {
87
+ old = latest ;
88
+ continue ;
89
+ }
90
+ } else {
91
+ if (cache .replace (key , old , computed )) {
92
+ return computed ;
93
+ }
94
+ }
95
+ }
96
+ old = cache .get (key );
97
+ }
49
98
}
50
99
51
100
@ Override
52
- public void putAll (@ NotNull Map <? extends K , ? extends V > map ) {
53
- read (() -> {
54
- cache .putAll (map );
55
- return Void .TYPE ;
56
- });
101
+ public V computeIfAbsent (@ NotNull K key , @ NotNull Function <K , V > computeFunc ) {
102
+ Object initial = cache .get (key );
103
+ if (initial != null ) {
104
+ return (V ) initial ;
105
+ }
106
+
107
+ V computed = computeFunc .apply (key );
108
+ if (computed == null ) {
109
+ return null ;
110
+ }
111
+
112
+ Object previous = cache .putIfAbsent (key , computed );
113
+ if (previous == null ) {
114
+ return computed ;
115
+ } else {
116
+ return (V ) previous ;
117
+ }
118
+ }
119
+
120
+ @ Override
121
+ public @ Nullable V computeIfPresent (@ NotNull K key , @ NotNull BiFunction <? super K , ? super V , ? extends V > computeFunc ) {
122
+ while (true ) {
123
+ Object oldValue = cache .get (key );
124
+ if (oldValue == null ) {
125
+ return null ;
126
+ }
127
+ V computed = computeFunc .apply (key , (V ) oldValue );
128
+ if (computed == null ) {
129
+ if (cache .remove (key , oldValue ))
130
+ return null ;
131
+ } else {
132
+ if (cache .replace (key , oldValue , computed )) {
133
+ return computed ;
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ @ Override
140
+ public @ Nullable V putIfAbsent (@ NotNull K key , @ NotNull V value ) {
141
+ return (V ) cache .putIfAbsent (key , value );
142
+ }
143
+
144
+ @ Override
145
+ public V merge (@ NotNull K key , @ NotNull V value , @ NotNull BiFunction <V , V , V > mergeFunc ) {
146
+ Object oldValue = cache .get (key );
147
+ while (true ) {
148
+ if (oldValue == null ) {
149
+ Object latest = cache .putIfAbsent (key , value );
150
+ if (latest == null ) {
151
+ return value ;
152
+ } else {
153
+ oldValue = latest ;
154
+ }
155
+ } else {
156
+ V merged = mergeFunc .apply ((V ) oldValue , value );
157
+ if (cache .replace (key , oldValue , merged )) {
158
+ return merged ;
159
+ } else {
160
+ oldValue = cache .get (key );
161
+ }
162
+ }
163
+ }
57
164
}
58
165
59
166
@ Override
60
167
public boolean replace (@ NotNull K key , @ NotNull V value ) {
61
- return read (() -> cache .replace (key , value ) != null ) ;
168
+ return cache .replace (key , value ) != null ;
62
169
}
63
170
64
171
@ Override
65
172
public boolean replace (@ NotNull K key , @ NotNull V oldValue , @ NotNull V newValue ) {
66
- return read (() -> cache .replace (key , oldValue , newValue ));
173
+ return cache .replace (key , oldValue , newValue );
174
+ }
175
+
176
+ @ Override
177
+ public void putAll (@ NotNull Map <? extends K , ? extends V > map ) {
178
+ cache .putAll (map );
67
179
}
68
180
69
181
@ Override
70
182
public void forEach (@ NotNull BiConsumer <? super K , ? super V > action ) {
71
- // We can't guarantee that users won't attempt to mutate the cache from within the action
72
- // So, we incur a performance penalty to acquire a write (rather than read) lock in order to avoid deadlocking
73
- write (() -> {
74
- cache .forEach (e -> action .accept ((K ) e .getKey (), (V ) e .getValue ()));
75
- return Void .TYPE ;
76
- });
183
+ cache .forEach (e -> action .accept ((K ) e .getKey (), (V ) e .getValue ()));
77
184
}
78
185
}
0 commit comments