/** * Returns the value for the given key. If a value already exists in the cache, then it * is returned immediately. Otherwise the {@code creator.call()} method is invoked and * its result is saved in this cache for future reuse. * * @param key the key for which to get the cached or created value. * @param creator a method for creating a value, to be invoked only if no value are cached for the given key. * @return the value for the given key, which may have been created as a result of this method call. * @throws Exception if an exception occurred during the execution of {@code creator.call()}. */ public V getOrCreate(final K key, final Callable<? extends V> creator) throws Exception { V value = peek(key); if (value == null) { final Handler<V> handler = lock(key); try { value = handler.peek(); if (value == null) { value = creator.call(); } } finally { handler.putAndUnlock(value); } } return value; }
/** * Completes the string representation of this factory for debugging purpose only. * The string formatted by this method may change in any future SIS version. */ @Debug @Override final void appendStringTo(final StringBuilder buffer) { buffer.append(", cache=").append(cache.size()).append(", DAO="); synchronized (availableDAOs) { buffer.append(availableDAOs.size()); if (remainingDAOs <= 0) { buffer.append(" (limit reached)"); } } } }
public static String getSrsName(final CoordinateReferenceSystem crs) { String srsName = null; if (crs != null) { try { srsName = CoordinateReferenceSystemAdapter.cachedIdentifier.get(crs); if (srsName == null && !CoordinateReferenceSystemAdapter.cachedIdentifier.containsKey(crs)) { srsName = org.apache.sis.referencing.IdentifiedObjects.lookupURN(crs, null); if (srsName == null) { srsName = IdentifiedObjects.getIdentifierOrName(crs); } CoordinateReferenceSystemAdapter.cachedIdentifier.put(crs, srsName); } } catch (FactoryException ex) { Logging.getLogger("org.geotoolkit.referencing").log(Level.WARNING, null, ex); } } return srsName; } }
/** * If no value is already mapped and no value is under computation for the given key, puts the given value * in the cache. Otherwise returns the current value (potentially blocking until the computation finishes). * A null {@code value} argument is equivalent to a no-op. Otherwise a {@code null} return value means that * the given {@code value} has been stored in the {@code Cache}. * * @param key the key to associate with a value. * @param value the value to associate with the given key if no value already exists, or {@code null}. * @return the existing value mapped to the given key, or {@code null} if none existed before this method call. * * @see #get(Object) * @see #computeIfAbsent(Object, Function) * * @since 1.0 */ @Override public V putIfAbsent(final K key, final V value) { if (value == null) { return null; } ensureValidType(value); final Object previous = map.putIfAbsent(key, value); if (previous == null) { // A non-null value means that 'putIfAbsent' did nothing. notifyChange(key, value); } return valueOf(previous); }
/** * Puts the given value in cache and immediately returns the old value. * A null {@code value} argument removes the entry. If a different value is under computation in another thread, * then the other thread may fail with an {@link IllegalStateException} unless {@link #isKeyCollisionAllowed()} * returns {@code true}. For more safety, consider using {@link #putIfAbsent putIfAbsent(…)} instead. * * @param key the key to associate with a value. * @param value the value to associate with the given key, or {@code null} for removing the mapping. * @return the value previously mapped to the given key, or {@code null} if no value existed before this * method call or if the value was under computation in another thread. * * @see #get(Object) * @see #putIfAbsent(Object, Object) */ @Override public V put(final K key, final V value) { ensureValidType(value); final Object previous = (value != null) ? map.put(key, value) : map.remove(key); if (previous != value) { notifyChange(key, value); } return immediateValueOf(previous); }
/** * If the given key is mapped to the given old value, replaces that value with the given new value. * Otherwise does nothing. A null {@code value} argument removes the entry if the condition matches. * If a value is under computation in another thread, then this method unconditionally returns {@code false}. * * @param key key of the value to replace. * @param oldValue previous value expected to be mapped to the given key. * @param newValue the new value to put if the condition matches, or {@code null} for removing the mapping. * @return {@code true} if the value has been replaced, {@code false} otherwise. * * @since 1.0 */ @Override public boolean replace(final K key, final V oldValue, final V newValue) { ensureValidType(newValue); final boolean done; if (oldValue != null) { done = (newValue != null) ? map.replace(key, oldValue, newValue) : map.remove(key, oldValue); } else { done = (newValue != null) && map.putIfAbsent(key, newValue) == null; } if (done) { notifyChange(key, newValue); } return done; }
/** * Tests adding a single value using the {@link Cache.Handler#putAndUnlock(Object)} method. * This method does all the operations in a single thread. */ @Test public void testPutAndUnlock() { final String key = "The key"; final String value = "The value"; final Cache<String,String> cache = new Cache<>(); assertTrue("No initial value expected.", cache.isEmpty()); assertNull("No initial value expected.", cache.peek(key)); final Cache.Handler<String> handler = cache.lock(key); assertNull("No initial value expected.", handler.peek()); handler.putAndUnlock(value); assertEquals(1, cache.size()); assertEquals(value, cache.peek(key)); assertEquals(singleton(key), cache.keySet()); assertEquals(singleton(new SimpleEntry<>(key, value)), cache.entrySet()); }
final String keyByOtherThread = "keyByOtherThread"; final String valueByOtherThread = "valueByOtherThread"; final Cache<String,String> cache = new Cache<>(); final class OtherThread extends Thread { final Cache.Handler<String> handler = cache.lock(keyByMainThread); assertTrue(handler instanceof Cache<?,?>.Work); final OtherThread thread = new OtherThread(); thread.start(); TestUtilities.waitForBlockedState(thread); assertNull("The blocked thread shall not have added a value.", cache.peek(keyByOtherThread));
public void stress() throws InterruptedException { final int count = 5000; final Cache<Integer,Integer> cache = new Cache<>(); final AtomicReference<Throwable> failures = new AtomicReference<>(); final class WriterThread extends Thread { assertTrue("Should not have more entries than what we put in.", cache.size() <= count); assertFalse("Some entries should be retained by strong references.", cache.isEmpty()); for (int i=0; i<10; i++) { final long t = System.nanoTime(); out.printf("Cache size: %4d (after %3d ms)%n", cache.size(), round((t - time) / (double) StandardDateFormat.NANOS_PER_MILLISECOND)); time = t;
cache = new Cache<>(20, maxStrongReferences, false); cache.setKeyCollisionAllowed(true);
final CoordinateOperation op = factorySIS.cache.peek(key); if (op != null) return asList(op); // Must be a modifiable list as per this method contract.
cache = new Cache<>(12, 50, true);
int cost = cost(value); synchronized (costs) { // Should not be needed, but done as a safety. final Integer old = costs.put(key, cost); final K oldKey = entry.getKey(); final Object oldValue = map.get(oldKey); if (oldValue != null && !isReservedType(oldValue)) { @SuppressWarnings("unchecked") final Reference<V> ref = soft ? new Soft<>(map, oldKey, (V) oldValue)
ensureValidType(value); final Object newValue = map.merge(key, value, adapter); Deferred.notifyChanges(this, adapter.changes); return valueOf(newValue);
/** * If the given key is mapped to any value, replaces that value with the given new value. * Otherwise does nothing. A null {@code value} argument removes the entry. * If a different value is under computation in another thread, then the other thread may fail with * an {@link IllegalStateException} unless {@link #isKeyCollisionAllowed()} returns {@code true}. * * @param key key of the value to replace. * @param value the new value to use in replacement of the previous one, or {@code null} for removing the mapping. * @return the value previously mapped to the given key, or {@code null} if no value existed before this * method call or if the value was under computation in another thread. * * @see #replace(Object, Object, Object) * * @since 1.0 */ @Override public V replace(final K key, final V value) { ensureValidType(value); final Object previous = (value != null) ? map.replace(key, value) : map.remove(key); if (previous != null) { // A null value means that 'replace' did nothing. notifyChange(key, value); } return immediateValueOf(previous); }
cache = new Cache<>(20, maxStrongReferences, false); cache.setKeyCollisionAllowed(true);
final CoordinateOperation op = factorySIS.cache.peek(key); if (op != null) return op;
cache = new Cache<>(12, 50, true);
int cost = (value != null) ? cost(value) : 0; synchronized (costs) { final Integer old = costs.put(key, cost); final K oldKey = entry.getKey(); final Object oldValue = map.get(oldKey); if (oldValue != null && !isReservedType(oldValue)) { @SuppressWarnings("unchecked") final Reference<V> ref = soft ? new Soft<>(map, oldKey, (V) oldValue)