From 9232b39bffdfc76d2a8b4a5b1e1fb7c94ac9e0d0 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Sun, 24 Jun 2018 04:33:22 +0300 Subject: [PATCH] [vcs-log] correctly rebuild user registry on error VcsUserRegistry is a component independent from log. It should rebuild itself on errors and send rebuild notifications. Notifications are used to mark index corrupted, since it depends on user registry. --- .../vcs/log/data/VcsUserRegistryImpl.kt | 61 ++++++++++++++++--- .../intellij/vcs/log/impl/VcsLogManager.java | 13 ++-- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.kt b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.kt index f5c6200d908d..b339a8bf01b5 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.kt +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.kt @@ -16,9 +16,11 @@ package com.intellij.vcs.log.data import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.PathManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project +import com.intellij.util.EventDispatcher import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.Interner import com.intellij.util.io.* @@ -29,31 +31,42 @@ import java.io.DataInput import java.io.DataOutput import java.io.File import java.io.IOException +import java.util.* +import java.util.concurrent.atomic.AtomicReference /** * */ class VcsUserRegistryImpl internal constructor(project: Project) : Disposable, VcsUserRegistry { + private val _persistentEnumerator = AtomicReference?>() private val persistentEnumerator: PersistentEnumeratorBase? + get() = _persistentEnumerator.get() private val interner: Interner + private val mapFile = File(USER_CACHE_APP_DIR, project.locationHash + "." + STORAGE_VERSION) + private val eventDispatcher = EventDispatcher.create(VcsUserRegistryListener::class.java) init { - val mapFile = File(USER_CACHE_APP_DIR, project.locationHash + "." + STORAGE_VERSION) - persistentEnumerator = initEnumerator(mapFile) + initEnumerator() interner = Interner() } - private fun initEnumerator(mapFile: File): PersistentEnumeratorBase? { - return try { - IOUtil.openCleanOrResetBroken({ - PersistentBTreeEnumerator(mapFile, MyDescriptor(), Page.PAGE_SIZE, null, - STORAGE_VERSION) - }, mapFile) + private fun initEnumerator(): Boolean { + try { + val enumerator = IOUtil.openCleanOrResetBroken({ + PersistentBTreeEnumerator(mapFile, MyDescriptor(), Page.PAGE_SIZE, null, + STORAGE_VERSION) + }, mapFile) + val wasSet = _persistentEnumerator.compareAndSet(null, enumerator) + if (!wasSet) { + LOG.error("Could not assign newly opened enumerator") + enumerator?.close() + } + return wasSet } catch (e: IOException) { LOG.warn(e) - null } + return false } override fun createUser(name: String, email: String): VcsUser { @@ -68,8 +81,8 @@ class VcsUserRegistryImpl internal constructor(project: Project) : Disposable, V } catch (e: IOException) { LOG.warn(e) + rebuild(e) } - } fun addUsers(users: Collection) { @@ -84,10 +97,34 @@ class VcsUserRegistryImpl internal constructor(project: Project) : Disposable, V } catch (e: IOException) { LOG.warn(e) + rebuild(e) emptySet() } } + private fun rebuild(t: Throwable) { + if (persistentEnumerator?.isCorrupted == true) { + _persistentEnumerator.getAndSet(null)?.let { oldEnumerator -> + ApplicationManager.getApplication().executeOnPooledThread { + try { + oldEnumerator.close() + } + catch (_: IOException) { + } + finally { + if (initEnumerator()) { + eventDispatcher.multicaster.onRebuild(t) + } + } + } + } + } + } + + fun addRebuildListener(listener: VcsUserRegistryListener, disposable: Disposable) { + eventDispatcher.addListener(listener, disposable) + } + fun flush() { persistentEnumerator?.force() } @@ -140,3 +177,7 @@ class VcsUserRegistryImpl internal constructor(project: Project) : Disposable, V private const val STORAGE_VERSION = 2 } } + +interface VcsUserRegistryListener : EventListener { + fun onRebuild(t: Throwable) +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java index eda5125b545e..f88a22daf71b 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java @@ -17,6 +17,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcs.log.VcsLogRefresher; +import com.intellij.vcs.log.VcsUserRegistry; import com.intellij.vcs.log.data.VcsLogData; import com.intellij.vcs.log.data.VcsLogStorage; import com.intellij.vcs.log.ui.AbstractVcsLogUi; @@ -59,13 +60,15 @@ public class VcsLogManager implements Disposable { myRecreateMainLogHandler = recreateHandler; Map logProviders = findLogProviders(roots, myProject); - myLogData = new VcsLogData(myProject, logProviders, new MyFatalErrorsHandler(), this); + MyFatalErrorsHandler fatalErrorsHandler = new MyFatalErrorsHandler(); + myLogData = new VcsLogData(myProject, logProviders, fatalErrorsHandler, this); myPostponableRefresher = new PostponableLogRefresher(myLogData); myTabsLogRefresher = new VcsLogTabsWatcher(myProject, myPostponableRefresher); refreshLogOnVcsEvents(logProviders, myPostponableRefresher, myLogData); myColorManager = new VcsLogColorManagerImpl(logProviders.keySet()); + myLogData.getUserRegistry().addRebuildListener(t -> fatalErrorsHandler.consume(myLogData.getUserRegistry(), t), this); if (scheduleRefreshImmediately) { scheduleInitialization(); @@ -130,7 +133,7 @@ public class VcsLogManager implements Disposable { MultiMap providers2roots = MultiMap.create(); logProviders.forEach((key, value) -> providers2roots.putValue(value, key)); - for (Map.Entry> entry : providers2roots.entrySet()) { + for (Map.Entry> entry: providers2roots.entrySet()) { Disposable disposable = entry.getKey().subscribeToRootRefreshEvents(entry.getValue(), refresher); Disposer.register(disposableParent, disposable); } @@ -140,7 +143,7 @@ public class VcsLogManager implements Disposable { public static Map findLogProviders(@NotNull Collection roots, @NotNull Project project) { Map logProviders = ContainerUtil.newHashMap(); VcsLogProvider[] allLogProviders = Extensions.getExtensions(VcsLogProvider.LOG_PROVIDER_EP, project); - for (VcsRoot root : roots) { + for (VcsRoot root: roots) { AbstractVcs vcs = root.getVcs(); VirtualFile path = root.getPath(); if (vcs == null || path == null) { @@ -148,7 +151,7 @@ public class VcsLogManager implements Disposable { continue; } - for (VcsLogProvider provider : allLogProviders) { + for (VcsLogProvider provider: allLogProviders) { if (provider.getSupportedVcs().equals(vcs.getKeyInstanceMethod())) { logProviders.put(path, provider); break; @@ -207,7 +210,7 @@ public class VcsLogManager implements Disposable { LOG.error(e); } - if (source instanceof VcsLogStorage) { + if (source instanceof VcsLogStorage || source instanceof VcsUserRegistry) { myLogData.getIndex().markCorrupted(); } }