private void cacheCommit(final int commitId, long taskNumber) {
   // fill the cache with temporary "Loading" values to avoid producing queries for each commit
   // that has not been cached yet,
   // even if it will be loaded within a previous query
   if (!myCache.isKeyCached(commitId)) {
     myCache.put(commitId, (T) new IndexedDetails(myIndex, myHashMap, commitId, taskNumber));
   }
 }
 @Nullable
 private T getFromCache(@NotNull Integer commitId) {
   T details = myCache.get(commitId);
   if (details != null) {
     if (details instanceof LoadingDetails) {
       if (((LoadingDetails) details).getLoadingTaskIndex()
           <= myCurrentTaskIndex - MAX_LOADING_TASKS) {
         // don't let old "loading" requests stay in the cache forever
         myCache.remove(commitId);
         return null;
       }
     }
     return details;
   }
   return getFromAdditionalCache(commitId);
 }
 public void saveInCache(@NotNull TIntObjectHashMap<T> details) {
   UIUtil.invokeAndWaitIfNeeded(
       (Runnable)
           () ->
               details.forEachEntry(
                   (key, value) -> {
                     myCache.put(key, value);
                     return true;
                   }));
 }
  @Override
  @NotNull
  public T getCommitData(@NotNull Integer hash, @NotNull Iterable<Integer> neighbourHashes) {
    assert EventQueue.isDispatchThread();
    T details = getFromCache(hash);
    if (details != null) {
      return details;
    }

    runLoadCommitsData(neighbourHashes);

    T result = myCache.get(hash);
    assert result
        != null; // now it is in the cache as "Loading Details" (runLoadCommitsData puts it there)
    return result;
  }