public synchronized KeyedFuture<K, V> get(K key, Function<K, CompletableFuture<V>> func) {
		/**
		 * NOTE: I must populate the key's entry before invoking the function. Since the lock is
		 * re-entrant, it's possible (likely even) that the same thread comes back around for the
		 * same entry. If the key is not associated with a pending future, that will cause the
		 * function to be re-invoked.
		 */
		// Don't refactor as put, since I need to know whether or not it existed
		KeyedFuture<K, V> future = futures.get(key);
		if (future != null) {
			if (!future.isDone() || !forgetValues.test(key, future.getNow(null))) {
				return future;
			}
		}
		final KeyedFuture<K, V> f = new KeyedFuture<>(key);
		putFuture(key, f);
		CompletableFuture<V> dep = func.apply(key);
		f.future = dep;
		dep.handle(AsyncUtils.copyTo(f));
		return f;
	}
Request the value for a given key, using an alternative computation

<p>
If this is called before any other get or put, the given function is launched for the given
key. A {@link CompletableFuture} is returned immediately. Subsequent gets or puts on the same
key will return the same future without starting any new computation.

@param key the key
@param func an alternative computation function, given a key
@return a future, possibly already completed, for the key's value
Ghidra/Framework/Generic/src/main/java/ghidra/async/AsyncLazyMap.java