Skip to content

Commit

Permalink
fix:mem leak
Browse files Browse the repository at this point in the history
  • Loading branch information
cunla committed Apr 23, 2024
1 parent 4f612b1 commit dacd833
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 30 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

## Unreleased

## 1.21.4

### 🐛 Bug Fixes

- Fix memory leak with log panel

## 1.21.3

### 🐛 Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pluginGroup=com.dsoftware.ghmanager
pluginName=github-actions-manager

# SemVer format -> https://semver.org
pluginVersion=1.21.3
pluginVersion=1.21.4

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild=241.13688.18
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import org.jetbrains.plugins.github.pullrequest.ui.GHLoadingModel


class JobsLoadingModelListener(
workflowRunDisposable: Disposable,
parentDisposable: Disposable,
dataProviderModel: SingleValueModel<JobsDataProvider?>
) : GHLoadingModel.StateChangeListener {
val jobsLoadingModel = GHCompletableFutureLoadingModel<WorkflowRunJobs>(workflowRunDisposable)
val jobsLoadingModel = GHCompletableFutureLoadingModel<WorkflowRunJobs>(parentDisposable)

init {
var listenerDisposable: Disposable? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ enum class LogValueStatus {
data class LogValue(val log: String?, val status: LogValueStatus, val jobName: String? = null)

class LogLoadingModelListener(
workflowRunDisposable: Disposable,
parentDisposable: Disposable,
logDataProvider: SingleValueModel<LogDataProvider?>,
private val jobsSelectionHolder: JobListSelectionHolder,
) : GHLoadingModel.StateChangeListener {
Expand All @@ -26,10 +26,10 @@ class LogLoadingModelListener(
* When the provider is null, it means no workflow-run/job is selected.
*/
val logValueModel = SingleValueModel<LogValue?>(null)
val logsLoadingModel = GHCompletableFutureLoadingModel<String>(workflowRunDisposable)
val logsLoadingModel = GHCompletableFutureLoadingModel<String>(parentDisposable)

init {
jobsSelectionHolder.addSelectionChangeListener(workflowRunDisposable, this::setLogValue)
jobsSelectionHolder.addSelectionChangeListener(parentDisposable, this::setLogValue)
logsLoadingModel.addStateChangeListener(this)
var listenerDisposable: Disposable? = null
logDataProvider.addAndInvokeListener { provider ->
Expand All @@ -41,7 +41,7 @@ class LogLoadingModelListener(
}
provider?.let {
val newDisposable = Disposer.newDisposable("Log listener disposable").apply {
Disposer.register(workflowRunDisposable, this)
Disposer.register(parentDisposable, this)
}
it.addRunChangesListener(newDisposable) {
logsLoadingModel.future = it.processValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,14 @@ class WorkflowRunSelectionContext internal constructor(
setNewLogProvider()
selectedRunDisposable.dispose()
selectedRunDisposable = Disposer.newDisposable("Selected run disposable")
Disposer.register(this, selectedRunDisposable)
}
jobSelectionHolder.addSelectionChangeListener(this) {
LOG.debug("jobSelectionHolder selection change listener")
setNewLogProvider()
selectedJobDisposable.dispose()
selectedJobDisposable = Disposer.newDisposable("Selected job disposable")
Disposer.register(this, selectedJobDisposable)
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import com.intellij.collaboration.api.dto.GraphQLRequestDTO
import com.intellij.collaboration.api.dto.GraphQLResponseDTO
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.State
Expand All @@ -22,7 +24,7 @@ import com.intellij.openapi.util.Disposer
import com.intellij.util.EventDispatcher
import com.intellij.util.ResourceUtil
import com.intellij.util.ThrowableConvertor
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
import kotlinx.serialization.Serializable
import org.jetbrains.plugins.github.api.GithubApiContentHelper
import org.jetbrains.plugins.github.api.GithubApiRequest.Post
Expand All @@ -38,8 +40,13 @@ import java.util.EventListener
import java.util.concurrent.ScheduledFuture

@Service(Service.Level.PROJECT)
@State(name = "GhActionsManagerSettings.ActionsCache", storages = [Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE)])
class GitHubActionCache(private val project: Project) : PersistentStateComponent<GitHubActionCache.State?> {
@State(
name = "GhActionsManagerSettings.ActionsDataCache",
storages = [Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE)]
)
class GitHubActionDataService(
private val project: Project
) : PersistentStateComponent<GitHubActionDataService.State?>, Disposable {
val actionsCache: Cache<String, GitHubAction> = CacheBuilder.newBuilder()
.expireAfterWrite(Duration.ofHours(1))
.maximumSize(200)
Expand Down Expand Up @@ -86,7 +93,6 @@ class GitHubActionCache(private val project: Project) : PersistentStateComponent
}
actionsToResolve.add(actionName)
addListener(listenerMethod)
resolveActions()
}

fun whenActionsLoaded(listenerMethod: () -> Unit) {
Expand All @@ -95,7 +101,6 @@ class GitHubActionCache(private val project: Project) : PersistentStateComponent
return
}
addListener(listenerMethod)
resolveActions()
}

fun getAction(fullActionName: String): GitHubAction? {
Expand All @@ -110,15 +115,19 @@ class GitHubActionCache(private val project: Project) : PersistentStateComponent

private fun addListener(listenerMethod: () -> Unit) {
val listenerDisposable = Disposer.newDisposable()
Disposer.register(this, listenerDisposable)
actionsLoadedEventDispatcher.addListener(object : ActionsLoadedListener {
override fun actionsLoaded() {
listenerMethod()
runReadAction(listenerMethod)
listenerDisposable.dispose() // Ensure listener will only run once
}
}, listenerDisposable)
}

private fun resolveActions() {
if (actionsToResolve.isEmpty()) {
return
}
actionsToResolve.removeAll(actionsCache.asMap().keys)
actionsToResolve.forEach {
resolveGithubAction(it)
Expand All @@ -138,7 +147,7 @@ class GitHubActionCache(private val project: Project) : PersistentStateComponent
}
}

@RequiresEdt
@RequiresBackgroundThread
private fun resolveGithubAction(fullActionName: String) {
if (actionsCache.getIfPresent(fullActionName) != null) {
LOG.debug("Action $fullActionName already resolved")
Expand Down Expand Up @@ -250,6 +259,10 @@ class GitHubActionCache(private val project: Project) : PersistentStateComponent


companion object {
private val LOG = logger<GitHubActionCache>()
private val LOG = logger<GitHubActionDataService>()
}

override fun dispose() {
task.cancel(true)
}
}
17 changes: 6 additions & 11 deletions src/main/kotlin/com/dsoftware/ghmanager/psi/HighlightAnnotator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,23 @@ class HighlightAnnotator : Annotator {
}

private fun highlightAction(yamlKeyValue: YAMLKeyValue, holder: AnnotationHolder) {
val gitHubActionCache = yamlKeyValue.project.service<GitHubActionCache>()
val gitHubActionDataService = yamlKeyValue.project.service<GitHubActionDataService>()
val actionName = yamlKeyValue.valueText.split("@").firstOrNull() ?: return
val currentVersion = yamlKeyValue.valueText.split("@").getOrNull(1) ?: return
gitHubActionCache.whenActionsLoaded {
val latestVersion = gitHubActionCache.getAction(actionName)?.latestVersion
gitHubActionDataService.whenActionLoaded(actionName) {
val latestVersion = gitHubActionDataService.getAction(actionName)?.latestVersion
if (VersionCompareTools.isActionOutdated(currentVersion, latestVersion)) {
val message = "$currentVersion is outdated. Latest version is $latestVersion"
val startIndex = yamlKeyValue.textRange.startOffset + yamlKeyValue.text.indexOf("@") + 1
val annotationBuilder = holder
.newAnnotation(HighlightSeverity.WARNING, message)
.range(
TextRange.create(
yamlKeyValue.textRange.startOffset
+ yamlKeyValue.text.indexOf("@") + 1,
yamlKeyValue.textRange.endOffset
)
)
.range(TextRange.create(startIndex, yamlKeyValue.textRange.endOffset))
val inspectionManager = yamlKeyValue.project.service<InspectionManager>()
val quickfix = UpdateActionVersionFix(actionName, latestVersion!!)
val problemDescriptor = inspectionManager.createProblemDescriptor(
yamlKeyValue, message, quickfix,
ProblemHighlightType.WEAK_WARNING, true
);
)
annotationBuilder
.newLocalQuickFix(quickfix, problemDescriptor)
.registerFix()
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/com/dsoftware/ghmanager/psi/ProjectStartup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ class ProjectStartup : ProjectActivity {
}
runReadAction {
val psiManager = project.service<PsiManager>()
val gitHubActionCache = project.service<GitHubActionCache>()
val gitHubActionDataService = project.service<GitHubActionDataService>()
psiManager.findFile(workflowFile)?.let {
val actionNames = Tools.getYamlElementsWithKey(it, FIELD_USES).map { yamlKeyValue ->
yamlKeyValue.valueText.split("@").firstOrNull() ?: return@map null
}.filterNotNull()
gitHubActionCache.actionsToResolve.addAll(actionNames)
gitHubActionDataService.actionsToResolve.addAll(actionNames)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class RepoTabController(
private fun createLogPanel(selectedRunContext: WorkflowRunSelectionContext): JComponent {
LOG.debug("Create log panel")
val model = LogLoadingModelListener(
selectedRunContext.selectedJobDisposable,
selectedRunContext,
selectedRunContext.logDataProviderLoadModel,
selectedRunContext.jobSelectionHolder
)
Expand All @@ -139,7 +139,7 @@ class RepoTabController(

private fun createJobsPanel(selectedRunContext: WorkflowRunSelectionContext): JComponent {
val jobsLoadingModel = JobsLoadingModelListener(
selectedRunContext.selectedRunDisposable, selectedRunContext.jobDataProviderLoadModel
selectedRunContext, selectedRunContext.jobDataProviderLoadModel
)

val jobsPanel = GHLoadingPanelFactory(
Expand Down

0 comments on commit dacd833

Please sign in to comment.