1 /*
2  * Written by Doug Lea with assistance from members of JCP JSR-166
3  * Expert Group and released to the domain, as explained at
4  * http://creativecommons.org/publicdomain/zero/1.0/
5  */
6 
7 module hunt.concurrency.ConcurrentMap;
8 
9 import hunt.collection.Map;
10 import hunt.Functions;
11 
12 
13 /**
14  * A {@link Map} providing thread safety and atomicity guarantees.
15  *
16  * <p>To maintain the specified guarantees, default implementations of
17  * methods including {@link #putIfAbsent} inherited from {@link Map}
18  * must be overridden by implementations of this interface. Similarly,
19  * implementations of the collections returned by methods {@link
20  * #keySet}, {@link #values}, and {@link #entrySet} must override
21  * methods such as {@code removeIf} when necessary to
22  * preserve atomicity guarantees.
23  *
24  * <p>Memory consistency effects: As with other concurrent
25  * collections, actions in a thread prior to placing an object into a
26  * {@code ConcurrentMap} as a key or value
27  * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
28  * actions subsequent to the access or removal of that object from
29  * the {@code ConcurrentMap} in another thread.
30  *
31  * <p>This interface is a member of the
32  * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
33  * Java Collections Framework</a>.
34  *
35  * @author Doug Lea
36  * @param <K> the type of keys maintained by this map
37  * @param <V> the type of mapped values
38  */
39 interface ConcurrentMap(K, V) : Map!(K, V) {
40 
41     /**
42      * {@inheritDoc}
43      *
44      * @implNote This implementation assumes that the ConcurrentMap cannot
45      * contain null values and {@code get()} returning null unambiguously means
46      * the key is absent. Implementations which support null values
47      * <strong>must</strong> override this default implementation.
48      *
49      * @throws ClassCastException {@inheritDoc}
50      * @throws NullPointerException {@inheritDoc}
51      */
52     // default V getOrDefault(Object key, V defaultValue) {
53     //     V v;
54     //     return ((v = get(key)) != null) ? v : defaultValue;
55     // }
56 
57     /**
58      * {@inheritDoc}
59      *
60      * @implSpec The default implementation is equivalent to, for this
61      * {@code map}:
62      * <pre> {@code
63      * for (Map.Entry!(K, V) entry : map.entrySet()) {
64      *   action.accept(entry.getKey(), entry.getValue());
65      * }}</pre>
66      *
67      * @implNote The default implementation assumes that
68      * {@code IllegalStateException} thrown by {@code getKey()} or
69      * {@code getValue()} indicates that the entry has been removed and cannot
70      * be processed. Operation continues for subsequent entries.
71      *
72      * @throws NullPointerException {@inheritDoc}
73      */
74     // default void forEach(BiConsumer<? super K, ? super V> action) {
75     //     Objects.requireNonNull(action);
76     //     for (Map.Entry!(K, V) entry : entrySet()) {
77     //         K k;
78     //         V v;
79     //         try {
80     //             k = entry.getKey();
81     //             v = entry.getValue();
82     //         } catch (IllegalStateException ise) {
83     //             // this usually means the entry is no longer in the map.
84     //             continue;
85     //         }
86     //         action.accept(k, v);
87     //     }
88     // }
89 
90     /**
91      * If the specified key is not already associated
92      * with a value, associates it with the given value.
93      * This is equivalent to, for this {@code map}:
94      * <pre> {@code
95      * if (!map.containsKey(key))
96      *   return map.put(key, value);
97      * else
98      *   return map.get(key);}</pre>
99      *
100      * except that the action is performed atomically.
101      *
102      * @implNote This implementation intentionally re-abstracts the
103      * inappropriate default provided in {@code Map}.
104      *
105      * @param key key with which the specified value is to be associated
106      * @param value value to be associated with the specified key
107      * @return the previous value associated with the specified key, or
108      *         {@code null} if there was no mapping for the key.
109      *         (A {@code null} return can also indicate that the map
110      *         previously associated {@code null} with the key,
111      *         if the implementation supports null values.)
112      * @throws UnsupportedOperationException if the {@code put} operation
113      *         is not supported by this map
114      * @throws ClassCastException if the class of the specified key or value
115      *         prevents it from being stored in this map
116      * @throws NullPointerException if the specified key or value is null,
117      *         and this map does not permit null keys or values
118      * @throws IllegalArgumentException if some property of the specified key
119      *         or value prevents it from being stored in this map
120      */
121     V putIfAbsent(K key, V value);
122 
123     /**
124      * Removes the entry for a key only if currently mapped to a given value.
125      * This is equivalent to, for this {@code map}:
126      * <pre> {@code
127      * if (map.containsKey(key)
128      *     && Objects.equals(map.get(key), value)) {
129      *   map.remove(key);
130      *   return true;
131      * } else {
132      *   return false;
133      * }}</pre>
134      *
135      * except that the action is performed atomically.
136      *
137      * @implNote This implementation intentionally re-abstracts the
138      * inappropriate default provided in {@code Map}.
139      *
140      * @param key key with which the specified value is associated
141      * @param value value expected to be associated with the specified key
142      * @return {@code true} if the value was removed
143      * @throws UnsupportedOperationException if the {@code remove} operation
144      *         is not supported by this map
145      * @throws ClassCastException if the key or value is of an inappropriate
146      *         type for this map
147      * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
148      * @throws NullPointerException if the specified key or value is null,
149      *         and this map does not permit null keys or values
150      * (<a href="{@docRoot}/java.base/java/util/Collection.html#optional-restrictions">optional</a>)
151      */
152     bool remove(K key, V value);
153 
154     /**
155      * Replaces the entry for a key only if currently mapped to a given value.
156      * This is equivalent to, for this {@code map}:
157      * <pre> {@code
158      * if (map.containsKey(key)
159      *     && Objects.equals(map.get(key), oldValue)) {
160      *   map.put(key, newValue);
161      *   return true;
162      * } else {
163      *   return false;
164      * }}</pre>
165      *
166      * except that the action is performed atomically.
167      *
168      * @implNote This implementation intentionally re-abstracts the
169      * inappropriate default provided in {@code Map}.
170      *
171      * @param key key with which the specified value is associated
172      * @param oldValue value expected to be associated with the specified key
173      * @param newValue value to be associated with the specified key
174      * @return {@code true} if the value was replaced
175      * @throws UnsupportedOperationException if the {@code put} operation
176      *         is not supported by this map
177      * @throws ClassCastException if the class of a specified key or value
178      *         prevents it from being stored in this map
179      * @throws NullPointerException if a specified key or value is null,
180      *         and this map does not permit null keys or values
181      * @throws IllegalArgumentException if some property of a specified key
182      *         or value prevents it from being stored in this map
183      */
184     bool replace(K key, V oldValue, V newValue);
185 
186     /**
187      * Replaces the entry for a key only if currently mapped to some value.
188      * This is equivalent to, for this {@code map}:
189      * <pre> {@code
190      * if (map.containsKey(key))
191      *   return map.put(key, value);
192      * else
193      *   return null;}</pre>
194      *
195      * except that the action is performed atomically.
196      *
197      * @implNote This implementation intentionally re-abstracts the
198      * inappropriate default provided in {@code Map}.
199      *
200      * @param key key with which the specified value is associated
201      * @param value value to be associated with the specified key
202      * @return the previous value associated with the specified key, or
203      *         {@code null} if there was no mapping for the key.
204      *         (A {@code null} return can also indicate that the map
205      *         previously associated {@code null} with the key,
206      *         if the implementation supports null values.)
207      * @throws UnsupportedOperationException if the {@code put} operation
208      *         is not supported by this map
209      * @throws ClassCastException if the class of the specified key or value
210      *         prevents it from being stored in this map
211      * @throws NullPointerException if the specified key or value is null,
212      *         and this map does not permit null keys or values
213      * @throws IllegalArgumentException if some property of the specified key
214      *         or value prevents it from being stored in this map
215      */
216     V replace(K key, V value);
217 
218     /**
219      * {@inheritDoc}
220      *
221      * @implSpec
222      * <p>The default implementation is equivalent to, for this {@code map}:
223      * <pre> {@code
224      * for (Map.Entry!(K, V) entry : map.entrySet()) {
225      *   K k;
226      *   V v;
227      *   do {
228      *     k = entry.getKey();
229      *     v = entry.getValue();
230      *   } while (!map.replace(k, v, function.apply(k, v)));
231      * }}</pre>
232      *
233      * The default implementation may retry these steps when multiple
234      * threads attempt updates including potentially calling the function
235      * repeatedly for a given key.
236      *
237      * <p>This implementation assumes that the ConcurrentMap cannot contain null
238      * values and {@code get()} returning null unambiguously means the key is
239      * absent. Implementations which support null values <strong>must</strong>
240      * override this default implementation.
241      *
242      * @throws UnsupportedOperationException {@inheritDoc}
243      * @throws NullPointerException {@inheritDoc}
244      * @throws ClassCastException {@inheritDoc}
245      * @throws IllegalArgumentException {@inheritDoc}
246      */
247     // @Override
248     // default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
249     //     Objects.requireNonNull(function);
250     //     forEach((k,v) -> {
251     //         while (!replace(k, v, function.apply(k, v))) {
252     //             // v changed or k is gone
253     //             if ( (v = get(k)) == null) {
254     //                 // k is no longer in the map.
255     //                 break;
256     //             }
257     //         }
258     //     });
259     // }
260 
261     /**
262      * {@inheritDoc}
263      *
264      * @implSpec
265      * The default implementation is equivalent to the following steps for this
266      * {@code map}:
267      *
268      * <pre> {@code
269      * V oldValue, newValue;
270      * return ((oldValue = map.get(key)) == null
271      *         && (newValue = mappingFunction.apply(key)) != null
272      *         && (oldValue = map.putIfAbsent(key, newValue)) == null)
273      *   ? newValue
274      *   : oldValue;}</pre>
275      *
276      * <p>This implementation assumes that the ConcurrentMap cannot contain null
277      * values and {@code get()} returning null unambiguously means the key is
278      * absent. Implementations which support null values <strong>must</strong>
279      * override this default implementation.
280      *
281      * @throws UnsupportedOperationException {@inheritDoc}
282      * @throws ClassCastException {@inheritDoc}
283      * @throws NullPointerException {@inheritDoc}
284      * @throws IllegalArgumentException {@inheritDoc}
285      */
286     // @Override
287     // default V computeIfAbsent(K key,
288     //         Function<? super K, ? extends V> mappingFunction) {
289     //     Objects.requireNonNull(mappingFunction);
290     //     V oldValue, newValue;
291     //     return ((oldValue = get(key)) == null
292     //             && (newValue = mappingFunction.apply(key)) != null
293     //             && (oldValue = putIfAbsent(key, newValue)) == null)
294     //         ? newValue
295     //         : oldValue;
296     // }
297 
298     /**
299      * {@inheritDoc}
300      *
301      * @implSpec
302      * The default implementation is equivalent to performing the following
303      * steps for this {@code map}:
304      *
305      * <pre> {@code
306      * for (V oldValue; (oldValue = map.get(key)) != null; ) {
307      *   V newValue = remappingFunction.apply(key, oldValue);
308      *   if ((newValue == null)
309      *       ? map.remove(key, oldValue)
310      *       : map.replace(key, oldValue, newValue))
311      *     return newValue;
312      * }
313      * return null;}</pre>
314      * When multiple threads attempt updates, map operations and the
315      * remapping function may be called multiple times.
316      *
317      * <p>This implementation assumes that the ConcurrentMap cannot contain null
318      * values and {@code get()} returning null unambiguously means the key is
319      * absent. Implementations which support null values <strong>must</strong>
320      * override this default implementation.
321      *
322      * @throws UnsupportedOperationException {@inheritDoc}
323      * @throws ClassCastException {@inheritDoc}
324      * @throws NullPointerException {@inheritDoc}
325      * @throws IllegalArgumentException {@inheritDoc}
326      */
327     // default V computeIfPresent(K key,
328     //         BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
329     //     Objects.requireNonNull(remappingFunction);
330     //     for (V oldValue; (oldValue = get(key)) != null; ) {
331     //         V newValue = remappingFunction.apply(key, oldValue);
332     //         if ((newValue == null)
333     //             ? remove(key, oldValue)
334     //             : replace(key, oldValue, newValue))
335     //             return newValue;
336     //     }
337     //     return null;
338     // }
339 
340     /**
341      * {@inheritDoc}
342      *
343      * @implSpec
344      * The default implementation is equivalent to performing the following
345      * steps for this {@code map}:
346      *
347      * <pre> {@code
348      * for (;;) {
349      *   V oldValue = map.get(key);
350      *   V newValue = remappingFunction.apply(key, oldValue);
351      *   if (newValue != null) {
352      *     if ((oldValue != null)
353      *       ? map.replace(key, oldValue, newValue)
354      *       : map.putIfAbsent(key, newValue) == null)
355      *       return newValue;
356      *   } else if (oldValue == null || map.remove(key, oldValue)) {
357      *     return null;
358      *   }
359      * }}</pre>
360      * When multiple threads attempt updates, map operations and the
361      * remapping function may be called multiple times.
362      *
363      * <p>This implementation assumes that the ConcurrentMap cannot contain null
364      * values and {@code get()} returning null unambiguously means the key is
365      * absent. Implementations which support null values <strong>must</strong>
366      * override this default implementation.
367      *
368      * @throws UnsupportedOperationException {@inheritDoc}
369      * @throws ClassCastException {@inheritDoc}
370      * @throws NullPointerException {@inheritDoc}
371      * @throws IllegalArgumentException {@inheritDoc}
372      */
373     // default V compute(K key,
374     //                   BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
375     //     retry: for (;;) {
376     //         V oldValue = get(key);
377     //         // if putIfAbsent fails, opportunistically use its return value
378     //         haveOldValue: for (;;) {
379     //             V newValue = remappingFunction.apply(key, oldValue);
380     //             if (newValue != null) {
381     //                 if (oldValue != null) {
382     //                     if (replace(key, oldValue, newValue))
383     //                         return newValue;
384     //                 }
385     //                 else if ((oldValue = putIfAbsent(key, newValue)) == null)
386     //                     return newValue;
387     //                 else continue haveOldValue;
388     //             } else if (oldValue == null || remove(key, oldValue)) {
389     //                 return null;
390     //             }
391     //             continue retry;
392     //         }
393     //     }
394     // }
395 
396     /**
397      * {@inheritDoc}
398      *
399      * @implSpec
400      * The default implementation is equivalent to performing the following
401      * steps for this {@code map}:
402      *
403      * <pre> {@code
404      * for (;;) {
405      *   V oldValue = map.get(key);
406      *   if (oldValue != null) {
407      *     V newValue = remappingFunction.apply(oldValue, value);
408      *     if (newValue != null) {
409      *       if (map.replace(key, oldValue, newValue))
410      *         return newValue;
411      *     } else if (map.remove(key, oldValue)) {
412      *       return null;
413      *     }
414      *   } else if (map.putIfAbsent(key, value) == null) {
415      *     return value;
416      *   }
417      * }}</pre>
418      * When multiple threads attempt updates, map operations and the
419      * remapping function may be called multiple times.
420      *
421      * <p>This implementation assumes that the ConcurrentMap cannot contain null
422      * values and {@code get()} returning null unambiguously means the key is
423      * absent. Implementations which support null values <strong>must</strong>
424      * override this default implementation.
425      *
426      * @throws UnsupportedOperationException {@inheritDoc}
427      * @throws ClassCastException {@inheritDoc}
428      * @throws NullPointerException {@inheritDoc}
429      * @throws IllegalArgumentException {@inheritDoc}
430      */
431     // default V merge(K key, V value,
432     //         BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
433     //     Objects.requireNonNull(remappingFunction);
434     //     Objects.requireNonNull(value);
435     //     retry: for (;;) {
436     //         V oldValue = get(key);
437     //         // if putIfAbsent fails, opportunistically use its return value
438     //         haveOldValue: for (;;) {
439     //             if (oldValue != null) {
440     //                 V newValue = remappingFunction.apply(oldValue, value);
441     //                 if (newValue != null) {
442     //                     if (replace(key, oldValue, newValue))
443     //                         return newValue;
444     //                 } else if (remove(key, oldValue)) {
445     //                     return null;
446     //                 }
447     //                 continue retry;
448     //             } else {
449     //                 if ((oldValue = putIfAbsent(key, value)) == null)
450     //                     return value;
451     //                 continue haveOldValue;
452     //             }
453     //         }
454     //     }
455     // }
456 }