mirror of https://github.com/sbt/sbt.git
Automatically run GC/finalization after each task execution.
Fixes #1223. * Add a new key to disable forcing the garbage collector to run after each task-executioin * Add a new flag to disable forcing the garbage collector to run after each task-exeuction * Add a hook into EvalauteTask to run System.gc/System.runFinalization after each task execution Review by @eed3si9n
This commit is contained in:
parent
3aa70b914a
commit
e076bfa970
|
|
@ -80,8 +80,16 @@ sealed trait EvaluateTaskConfig {
|
||||||
def checkCycles: Boolean
|
def checkCycles: Boolean
|
||||||
def progressReporter: ExecuteProgress[Task]
|
def progressReporter: ExecuteProgress[Task]
|
||||||
def cancelStrategy: TaskCancellationStrategy
|
def cancelStrategy: TaskCancellationStrategy
|
||||||
|
/** If true, we force a finalizer/gc run (or two) after task execution completes.
|
||||||
|
* This helps in instances where
|
||||||
|
*/
|
||||||
|
def forceGarbageCollection: Boolean
|
||||||
}
|
}
|
||||||
final object EvaluateTaskConfig {
|
final object EvaluateTaskConfig {
|
||||||
|
// Returns the default force garbage collection flag,
|
||||||
|
// as specified by system properties.
|
||||||
|
private[sbt] def defaultForceGarbageCollection: Boolean =
|
||||||
|
sys.props.get("sbt.task.forcegc").map(java.lang.Boolean.parseBoolean).getOrElse(true)
|
||||||
/** Pulls in the old configuration format. */
|
/** Pulls in the old configuration format. */
|
||||||
def apply(old: EvaluateConfig): EvaluateTaskConfig = {
|
def apply(old: EvaluateConfig): EvaluateTaskConfig = {
|
||||||
object AdaptedTaskConfig extends EvaluateTaskConfig {
|
object AdaptedTaskConfig extends EvaluateTaskConfig {
|
||||||
|
|
@ -91,6 +99,7 @@ final object EvaluateTaskConfig {
|
||||||
def cancelStrategy: TaskCancellationStrategy =
|
def cancelStrategy: TaskCancellationStrategy =
|
||||||
if(old.cancelable) TaskCancellationStrategy.Signal
|
if(old.cancelable) TaskCancellationStrategy.Signal
|
||||||
else TaskCancellationStrategy.Null
|
else TaskCancellationStrategy.Null
|
||||||
|
def forceGarbageCollection = defaultForceGarbageCollection
|
||||||
}
|
}
|
||||||
AdaptedTaskConfig
|
AdaptedTaskConfig
|
||||||
}
|
}
|
||||||
|
|
@ -98,16 +107,19 @@ final object EvaluateTaskConfig {
|
||||||
def apply(restrictions: Seq[Tags.Rule],
|
def apply(restrictions: Seq[Tags.Rule],
|
||||||
checkCycles: Boolean,
|
checkCycles: Boolean,
|
||||||
progressReporter: ExecuteProgress[Task],
|
progressReporter: ExecuteProgress[Task],
|
||||||
cancelStrategy: TaskCancellationStrategy): EvaluateTaskConfig = {
|
cancelStrategy: TaskCancellationStrategy,
|
||||||
|
forceGarbageCollection: Boolean): EvaluateTaskConfig = {
|
||||||
val r = restrictions
|
val r = restrictions
|
||||||
val check = checkCycles
|
val check = checkCycles
|
||||||
val cs = cancelStrategy
|
val cs = cancelStrategy
|
||||||
val pr = progressReporter
|
val pr = progressReporter
|
||||||
|
val fgc = forceGarbageCollection
|
||||||
object SimpleEvaluateTaskConfig extends EvaluateTaskConfig {
|
object SimpleEvaluateTaskConfig extends EvaluateTaskConfig {
|
||||||
def restrictions = r
|
def restrictions = r
|
||||||
def checkCycles = check
|
def checkCycles = check
|
||||||
def progressReporter = pr
|
def progressReporter = pr
|
||||||
def cancelStrategy = cs
|
def cancelStrategy = cs
|
||||||
|
def forceGarbageCollection = fgc
|
||||||
}
|
}
|
||||||
SimpleEvaluateTaskConfig
|
SimpleEvaluateTaskConfig
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +181,8 @@ object EvaluateTask
|
||||||
val rs = restrictions(extracted, structure)
|
val rs = restrictions(extracted, structure)
|
||||||
val canceller = cancelStrategy(extracted, structure, state)
|
val canceller = cancelStrategy(extracted, structure, state)
|
||||||
val progress = executeProgress(extracted, structure, state)
|
val progress = executeProgress(extracted, structure, state)
|
||||||
EvaluateTaskConfig(rs, false, progress, canceller)
|
val fgc = forcegc(extracted, structure)
|
||||||
|
EvaluateTaskConfig(rs, false, progress, canceller, fgc)
|
||||||
}
|
}
|
||||||
|
|
||||||
def defaultRestrictions(maxWorkers: Int) = Tags.limitAll(maxWorkers) :: Nil
|
def defaultRestrictions(maxWorkers: Int) = Tags.limitAll(maxWorkers) :: Nil
|
||||||
|
|
@ -198,6 +211,9 @@ object EvaluateTask
|
||||||
val maker: State => Keys.TaskProgress = getSetting(Keys.executeProgress, const(new Keys.TaskProgress(defaultProgress)), extracted, structure)
|
val maker: State => Keys.TaskProgress = getSetting(Keys.executeProgress, const(new Keys.TaskProgress(defaultProgress)), extracted, structure)
|
||||||
maker(state).progress
|
maker(state).progress
|
||||||
}
|
}
|
||||||
|
// TODO - Should this pull from Global or from the project itself?
|
||||||
|
private[sbt] def forcegc(extracted: Extracted, structure: BuildStructure): Boolean =
|
||||||
|
getSetting(Keys.forcegc in Global, EvaluateTaskConfig.defaultForceGarbageCollection, extracted, structure)
|
||||||
|
|
||||||
def getSetting[T](key: SettingKey[T], default: T, extracted: Extracted, structure: BuildStructure): T =
|
def getSetting[T](key: SettingKey[T], default: T, extracted: Extracted, structure: BuildStructure): T =
|
||||||
key in extracted.currentRef get structure.data getOrElse default
|
key in extracted.currentRef get structure.data getOrElse default
|
||||||
|
|
@ -301,8 +317,21 @@ object EvaluateTask
|
||||||
val log = state.log
|
val log = state.log
|
||||||
log.debug("Running task... Cancel: " + config.cancelStrategy + ", check cycles: " + config.checkCycles)
|
log.debug("Running task... Cancel: " + config.cancelStrategy + ", check cycles: " + config.checkCycles)
|
||||||
val tags = tagged[Task[_]](_.info get tagsKey getOrElse Map.empty, Tags.predicate(config.restrictions))
|
val tags = tagged[Task[_]](_.info get tagsKey getOrElse Map.empty, Tags.predicate(config.restrictions))
|
||||||
val (service, shutdown) = completionService[Task[_], Completed](tags, (s: String) => log.warn(s))
|
val (service, shutdownThreads) = completionService[Task[_], Completed](tags, (s: String) => log.warn(s))
|
||||||
|
|
||||||
|
def shutdown(): Unit = {
|
||||||
|
// First ensure that all threads are stopped for task execution.
|
||||||
|
shutdownThreads()
|
||||||
|
// Now we run the gc cleanup to force finalizers to clear out file handles (yay GC!)
|
||||||
|
if(config.forceGarbageCollection) {
|
||||||
|
// Force the detection of finalizers for scala.reflect weakhashsets
|
||||||
|
System.gc()
|
||||||
|
// Force finalizers to run.
|
||||||
|
System.runFinalization()
|
||||||
|
// Force actually cleaning the weak hash maps.
|
||||||
|
System.gc()
|
||||||
|
}
|
||||||
|
}
|
||||||
// propagate the defining key for reporting the origin
|
// propagate the defining key for reporting the origin
|
||||||
def overwriteNode(i: Incomplete): Boolean = i.node match {
|
def overwriteNode(i: Incomplete): Boolean = i.node match {
|
||||||
case Some(t: Task[_]) => transformNode(t).isEmpty
|
case Some(t: Task[_]) => transformNode(t).isEmpty
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,7 @@ object Keys
|
||||||
val tags = SettingKey[Seq[(Tags.Tag,Int)]]("tags", ConcurrentRestrictions.tagsKey.label, BSetting)
|
val tags = SettingKey[Seq[(Tags.Tag,Int)]]("tags", ConcurrentRestrictions.tagsKey.label, BSetting)
|
||||||
val concurrentRestrictions = SettingKey[Seq[Tags.Rule]]("concurrent-restrictions", "Rules describing restrictions on concurrent task execution.", BSetting)
|
val concurrentRestrictions = SettingKey[Seq[Tags.Rule]]("concurrent-restrictions", "Rules describing restrictions on concurrent task execution.", BSetting)
|
||||||
val cancelable = SettingKey[Boolean]("cancelable", "Enables (true) or disables (false) the ability to interrupt task execution with CTRL+C.", BMinusSetting)
|
val cancelable = SettingKey[Boolean]("cancelable", "Enables (true) or disables (false) the ability to interrupt task execution with CTRL+C.", BMinusSetting)
|
||||||
|
val forcegc = SettingKey[Boolean]("forcegc", "Enables (true) or disables (false) forcing garbage collection after each task run.", BMinusSetting)
|
||||||
val settingsData = std.FullInstance.settingsData
|
val settingsData = std.FullInstance.settingsData
|
||||||
val streams = TaskKey[TaskStreams]("streams", "Provides streams for logging and persisting data.", DTask)
|
val streams = TaskKey[TaskStreams]("streams", "Provides streams for logging and persisting data.", DTask)
|
||||||
val taskDefinitionKey = Def.taskDefinitionKey
|
val taskDefinitionKey = Def.taskDefinitionKey
|
||||||
|
|
|
||||||
|
|
@ -510,7 +510,8 @@ object Project extends ProjectExtra
|
||||||
val ch = EvaluateTask.cancelStrategy(extracted, extracted.structure, state)
|
val ch = EvaluateTask.cancelStrategy(extracted, extracted.structure, state)
|
||||||
val p = EvaluateTask.executeProgress(extracted, extracted.structure, state)
|
val p = EvaluateTask.executeProgress(extracted, extracted.structure, state)
|
||||||
val r = EvaluateTask.restrictions(state)
|
val r = EvaluateTask.restrictions(state)
|
||||||
runTask(taskKey, state, EvaluateTaskConfig(r, checkCycles, p, ch))
|
val fgc = EvaluateTask.forcegc(extracted, extracted.structure)
|
||||||
|
runTask(taskKey, state, EvaluateTaskConfig(r, checkCycles, p, ch, fgc))
|
||||||
}
|
}
|
||||||
@deprecated("Use EvalauteTaskConfig option instead.", "0.13.5")
|
@deprecated("Use EvalauteTaskConfig option instead.", "0.13.5")
|
||||||
def runTask[T](taskKey: ScopedKey[Task[T]], state: State, config: EvaluateConfig): Option[(State, Result[T])] =
|
def runTask[T](taskKey: ScopedKey[Task[T]], state: State, config: EvaluateConfig): Option[(State, Result[T])] =
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue