Skip to content

Commit 68603e4

Browse files
authored
Merge pull request #2405 from digma-ai/fix-no-insights-yet-notification
fix disposing in scheduled tasks Closes #2400
2 parents 3a592ad + 3efe865 commit 68603e4

File tree

2 files changed

+39
-17
lines changed

2 files changed

+39
-17
lines changed

ide-common/src/main/kotlin/org/digma/intellij/plugin/scheduling/schedulers.kt

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -317,13 +317,25 @@ fun Disposable.disposingPeriodicTask(name: String, startupDelay: Long, period: L
317317
return false
318318
}
319319

320-
Disposer.register(this) {
321-
Log.log(logger::trace, "disposing periodic task {}", name)
320+
//it may happen that 'this' is already disposed before registering a disposer.
321+
//for example tasks that dispose themselves. see for example startNoInsightsYetNotificationTimer,
322+
// in a matter of microseconds it may happen that the task will run and dispose itself before we had a chance to register a disposer,
323+
//if that happens the task will never be canceled.
324+
//or 'this' may be disposed very quickly by some other component.
325+
//in any case, if Disposer.register fails for any reason then cancel the task immediately, otherwise the task will never be canceled.
326+
//we send an error event, although it is not really an error because it is probably an ok flow, but maybe we want to know about
327+
// these cases and improve if necessary.
328+
try {
329+
Disposer.register(this) {
330+
Log.log(logger::trace, "disposing periodic task {}", name)
331+
future.cancel(true)
332+
}
333+
} catch (e: Throwable) {
322334
future.cancel(true)
335+
Log.warnWithException(logger, e, "could not register disposer for task {}", name)
336+
ErrorReporter.getInstance().reportError("org.digma.scheduler.disposingPeriodicTask", e, mapOf("task.name" to name))
323337
}
324338
return true
325-
326-
327339
}
328340

329341
/**
@@ -337,7 +349,7 @@ fun Disposable.disposingOneShotDelayedTask(name: String, delay: Long, block: ()
337349
consider this:
338350
calling disposingOneShotDelayedTask on a Disposable ,lets say a project service. and we would just register
339351
a child disposable on the project service to cancel the task.
340-
if the project is closed before the task is executed the parent disposable, the service, will be disposed with all its children
352+
if the project is closed before the task is executed, the parent disposable, the service, will be disposed with all its children
341353
and the task will be canceled.
342354
but if the task did execute and the project service was not disposed yet, we are left with a disposable child that will be disposed
343355
only when the service is disposed.
@@ -347,14 +359,14 @@ fun Disposable.disposingOneShotDelayedTask(name: String, delay: Long, block: ()
347359
the solution:
348360
calling disposingOneShotDelayedTask on a Disposable project service. the parent.
349361
we create here a new disposable,the child, register it as child of the parent.
350-
register the task on the child disposable and in disposingOneShotDelayedTask0 we register a child for the child.
362+
register the task on the child disposable and in disposingOneShotDelayedTaskImpl we register a child for the child.
351363
if the parent is disposed before the task is executed, the first child will be disposed, its child will be disposed and the task
352364
will be canceled.
353365
if the task executed and completed, the task itself disposes the first child, which will dispose its child. and now the parent
354366
doesn't have a leftover child.
355367
356-
Notice that we execute disposingOneShotDelayedTask0 on the child disposable, its private and should only be used internally.
357-
in disposingOneShotDelayedTask0 when the task completes it disposes this in the finally block.
368+
Notice that we execute disposingOneShotDelayedTaskImpl on the child disposable, its private and should only be used internally.
369+
in disposingOneShotDelayedTaskImpl when the task completes it disposes 'this' in the finally block.
358370
see unit tests:
359371
testDisposingOneShotDelayedTaskParentDisposableHasNoChildren
360372
testDisposingOneShotDelayedTaskCanceledParentDisposableHasNoChildren
@@ -404,14 +416,26 @@ private fun Disposable.disposingOneShotDelayedTaskImpl(name: String, delay: Long
404416
return false
405417
}
406418

407-
408-
//this will run when parent disposable is disposed which will dispose this disposable and its children,if not disposed already.
409-
Disposer.register(this) {
410-
Log.log(logger::trace, "disposing delayed task {}", name)
419+
//it may happen that 'this' is already disposed before registering a disposer.
420+
//for example tasks that dispose themselves. see for example startNoInsightsYetNotificationTimer,
421+
// in a matter of microseconds it may happen that the task will run and dispose itself before we had a chance to register a disposer,
422+
//if that happens the task will never be canceled.
423+
//or 'this' may be disposed very quickly by some other component.
424+
//in any case, if Disposer.register fails for any reason then cancel the task immediately, otherwise the task will never be canceled.
425+
//we send an error event, although it is not really an error because it is probably an ok flow, but maybe we want to know about
426+
// these cases and improve if necessary.
427+
try {
428+
Disposer.register(this) {
429+
Log.log(logger::trace, "disposing delayed task {}", name)
430+
future.cancel(true)
431+
}
432+
} catch (e: Throwable) {
411433
future.cancel(true)
434+
Log.warnWithException(logger, e, "could not register disposer for task {}", name)
435+
ErrorReporter.getInstance().reportError("org.digma.scheduler.disposingPeriodicTask", e, mapOf("task.name" to name))
412436
}
413-
414437
return true
438+
415439
}
416440

417441
/**

src/main/kotlin/org/digma/intellij/plugin/ui/notificationcenter/NoInsightsYetNotification.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import org.digma.intellij.plugin.scheduling.disposingPeriodicTask
1515
import java.time.Instant
1616
import kotlin.time.Duration.Companion.minutes
1717

18-
18+
//parentDisposable is the service
1919
fun startNoInsightsYetNotificationTimer(parentDisposable: Disposable) {
2020

2121
if (service<PersistenceService>().isFirstInsightReceived()) {
@@ -44,9 +44,7 @@ private fun scheduleShowNotification(parentDisposable: Disposable, firstConnecti
4444
firstConnectionTime
4545
)
4646

47-
val disposable = Disposer.newDisposable()
48-
Disposer.register(parentDisposable, disposable)
49-
disposable.disposingOneShotDelayedTask("NoInsightsYetNotificationTimer.showNoInsightsYetNotification", 30.minutes.inWholeMilliseconds) {
47+
parentDisposable.disposingOneShotDelayedTask("NoInsightsYetNotificationTimer.showNoInsightsYetNotification", 30.minutes.inWholeMilliseconds) {
5048
if (!service<PersistenceService>().isFirstInsightReceived()) {
5149
Log.log(AppNotificationCenter.logger::info, "in NoInsightsYetNotificationTimer, showing notification")
5250
showNoInsightsYetNotification()

0 commit comments

Comments
 (0)