11package io .github .xanthic .cache .provider .ehcache ;
22
3- import io .github .xanthic .cache .core .LockedAbstractCache ;
4- import lombok .EqualsAndHashCode ;
3+ import io .github .xanthic .cache .api .Cache ;
54import lombok .Value ;
5+ import org .jetbrains .annotations .ApiStatus ;
66import org .jetbrains .annotations .NotNull ;
7+ import org .jetbrains .annotations .Nullable ;
78
89import java .util .Map ;
910import java .util .function .BiConsumer ;
11+ import java .util .function .BiFunction ;
12+ import java .util .function .Function ;
1013
1114@ Value
12- @ EqualsAndHashCode ( callSuper = false )
15+ @ ApiStatus . Internal
1316@ SuppressWarnings ("unchecked" )
14- public class EhcacheDelegate <K , V > extends LockedAbstractCache <K , V > {
17+ public class EhcacheDelegate <K , V > implements Cache <K , V > {
1518 org .ehcache .Cache <Object , Object > cache ;
1619
1720 @ Override
18- protected V getUnlocked (@ NotNull K key ) {
21+ public @ Nullable V get (@ NotNull K key ) {
1922 return (V ) cache .get (key );
2023 }
2124
2225 @ 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+ }
2542 }
2643
2744 @ 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+ }
3056 }
3157
3258 @ Override
33- protected void clearUnlocked () {
59+ public void clear () {
3460 cache .clear ();
3561 }
3662
3763 @ Override
38- protected long sizeUnlocked () {
64+ public long size () {
3965 long n = 0 ;
4066 for (org .ehcache .Cache .Entry <Object , Object > ignored : cache ) {
4167 n ++;
@@ -44,35 +70,116 @@ protected long sizeUnlocked() {
4470 }
4571
4672 @ 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+ }
4998 }
5099
51100 @ 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+ }
57164 }
58165
59166 @ Override
60167 public boolean replace (@ NotNull K key , @ NotNull V value ) {
61- return read (() -> cache .replace (key , value ) != null ) ;
168+ return cache .replace (key , value ) != null ;
62169 }
63170
64171 @ Override
65172 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 );
67179 }
68180
69181 @ Override
70182 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 ()));
77184 }
78185}
0 commit comments