@Override @Nullable public V invoke(K input) { Object value = cache.get(input); if (value != null && value != NotValue.COMPUTING) return WrappedValues.unescapeExceptionOrNull(value); lock.lock(); try { value = cache.get(input); assert value != NotValue.COMPUTING : "Recursion detected on input: " + input + " under " + LockBasedStorageManager.this; if (value != null) return WrappedValues.unescapeExceptionOrNull(value); AssertionError error = null; try { cache.put(input, NotValue.COMPUTING); V typedValue = compute.invoke(input); Object oldValue = cache.put(input, WrappedValues.escapeNull(typedValue)); // This code effectively asserts that oldValue is null // The trickery is here because below we catch all exceptions thrown here, and this is the // only exception that shouldn't be stored // A seemingly obvious way to come about this case would be to declare a special exception // class, but the problem is that // one memoized function is likely to (indirectly) call another, and if this second one // throws this exception, we are screwed if (oldValue != NotValue.COMPUTING) { error = new AssertionError( "Race condition detected on input " + input + ". Old value is " + oldValue + " under " + LockBasedStorageManager.this); throw error; } return typedValue; } catch (Throwable throwable) { if (throwable == error) throw exceptionHandlingStrategy.handleException(throwable); Object oldValue = cache.put(input, WrappedValues.escapeThrowable(throwable)); assert oldValue == NotValue.COMPUTING : "Race condition detected on input " + input + ". Old value is " + oldValue + " under " + LockBasedStorageManager.this; throw exceptionHandlingStrategy.handleException(throwable); } } finally { lock.unlock(); } }
@Override public T invoke() { Object _value = value; if (!(_value instanceof NotValue)) return WrappedValues.unescapeThrowable(_value); lock.lock(); try { _value = value; if (!(_value instanceof NotValue)) return WrappedValues.unescapeThrowable(_value); if (_value == NotValue.COMPUTING) { value = NotValue.RECURSION_WAS_DETECTED; RecursionDetectedResult<T> result = recursionDetected(/*firstTime = */ true); if (!result.isFallThrough()) { return result.getValue(); } } if (_value == NotValue.RECURSION_WAS_DETECTED) { RecursionDetectedResult<T> result = recursionDetected(/*firstTime = */ false); if (!result.isFallThrough()) { return result.getValue(); } } value = NotValue.COMPUTING; try { T typedValue = computable.invoke(); value = typedValue; postCompute(typedValue); return typedValue; } catch (Throwable throwable) { if (value == NotValue.COMPUTING) { // Store only if it's a genuine result, not something thrown through recursionDetected() value = WrappedValues.escapeThrowable(throwable); } throw exceptionHandlingStrategy.handleException(throwable); } } finally { lock.unlock(); } }