View Javadoc
1   /*
2    * Copyright 2019-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.util;
6   
7   import java.util.concurrent.TimeUnit;
8   import java.util.function.Supplier;
9   
10  import oshi.annotation.concurrent.ThreadSafe;
11  
12  /**
13   * A memoized function stores the output corresponding to some set of specific inputs. Subsequent calls with remembered
14   * inputs return the remembered result rather than recalculating it.
15   */
16  @ThreadSafe
17  public final class Memoizer {
18  
19      private static final Supplier<Long> DEFAULT_EXPIRATION_NANOS = memoize(Memoizer::queryExpirationConfig,
20              TimeUnit.MINUTES.toNanos(1));
21  
22      private Memoizer() {
23      }
24  
25      private static long queryExpirationConfig() {
26          return TimeUnit.MILLISECONDS.toNanos(GlobalConfig.get(GlobalConfig.OSHI_UTIL_MEMOIZER_EXPIRATION, 300));
27      }
28  
29      /**
30       * Default exipiration of memoized values in nanoseconds, which will refresh after this time elapses. Update by
31       * setting {@link GlobalConfig} property <code>oshi.util.memoizer.expiration</code> to a value in milliseconds.
32       *
33       * @return The number of nanoseconds to keep memoized values before refreshing
34       */
35      public static long defaultExpiration() {
36          return DEFAULT_EXPIRATION_NANOS.get();
37      }
38  
39      /**
40       * Store a supplier in a delegate function to be computed once, and only again after time to live (ttl) has expired.
41       *
42       * @param <T>      The type of object supplied
43       * @param original The {@link java.util.function.Supplier} to memoize
44       * @param ttlNanos Time in nanoseconds to retain calculation. If negative, retain indefinitely.
45       * @return A memoized version of the supplier
46       */
47      public static <T> Supplier<T> memoize(Supplier<T> original, long ttlNanos) {
48          // Adapted from Guava's ExpiringMemoizingSupplier
49          return new Supplier<T>() {
50              private final Supplier<T> delegate = original;
51              private volatile T value; // NOSONAR squid:S3077
52              private volatile long expirationNanos;
53  
54              @Override
55              public T get() {
56                  long nanos = expirationNanos;
57                  long now = System.nanoTime();
58                  if (nanos == 0 || (ttlNanos >= 0 && now - nanos >= 0)) {
59                      synchronized (this) {
60                          if (nanos == expirationNanos) { // recheck for lost race
61                              T t = delegate.get();
62                              value = t;
63                              nanos = now + ttlNanos;
64                              expirationNanos = (nanos == 0) ? 1 : nanos;
65                              return t;
66                          }
67                      }
68                  }
69                  return value;
70              }
71          };
72      }
73  
74      /**
75       * Store a supplier in a delegate function to be computed only once.
76       *
77       * @param <T>      The type of object supplied
78       * @param original The {@link java.util.function.Supplier} to memoize
79       * @return A memoized version of the supplier
80       */
81      public static <T> Supplier<T> memoize(Supplier<T> original) {
82          return memoize(original, -1L);
83      }
84  }