[GRADLE-2957] finalizedBy may cause IndexOutOfBundsException Created: 19/Nov/13  Updated: 03/Oct/14  Resolved: 03/Oct/14

Status: Resolved
Project: Gradle
Affects Version/s: None
Fix Version/s: 2.2-rc-1

Type: Bug
Reporter: Szczepan Faber Assignee: Marcin Erdmann
Resolution: Fixed Votes: 2

Attachments: File finalizedByBug.gradle    
Issue Links:
Related
Related to GRADLE-2983 finalizer tasks may incur IndexOutOfB... Resolved

 Description   

Some legal combination of finalizedBy and dependsOn may cause following exception:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.onOrderingCycle(DefaultTaskExecutionPlan.java:255)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.determineExecutionPlan(DefaultTaskExecutionPlan.java:211)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.ensurePopulated(DefaultTaskGraphExecuter.java:148)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:82)
	at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
	at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
	at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
	at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:54)
	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:166)
	at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:113)
	at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:81)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:64)
	at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:33)
	at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:24)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:35)
	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:45)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:42)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:24)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.StartStopIfBuildAndStop.execute(StartStopIfBuildAndStop.java:33)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.ReturnResult.execute(ReturnResult.java:34)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:71)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:69)
	at org.gradle.util.Swapper.swap(Swapper.java:38)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:69)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:60)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:45)
	at org.gradle.launcher.daemon.server.DaemonStateCoordinator.runCommand(DaemonStateCoordinator.java:186)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy.doBuild(StartBuildOrRespondWithBusy.java:49)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.HandleStop.execute(HandleStop.java:36)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.DaemonHygieneAction.execute(DaemonHygieneAction.java:36)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.CatchAndForwardDaemonFailure.execute(CatchAndForwardDaemonFailure.java:32)
	at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
	at org.gradle.launcher.daemon.server.exec.DefaultDaemonCommandExecuter.executeCommand(DefaultDaemonCommandExecuter.java:51)
	at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.handleCommand(DefaultIncomingConnectionHandler.java:155)
	at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.receiveAndHandleCommand(DefaultIncomingConnectionHandler.java:128)
	at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.run(DefaultIncomingConnectionHandler.java:116)
	at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:66)

I can repro this but it's really tangled and I don't have a small repro sample to attach.



 Comments   
Comment by Daniel C. Greene [ 18/Mar/14 ]

I'm able to reproduce this issue with the attached gradle file.
It's expected to have a run order of:
taskOne, buggyTask, taskTwo, finalTask

Comment by Damien Coraboeuf [ 17/Sep/14 ]

I have this very same issue with Gradle 1.12.

After some investigation, the error seems located in https://github.com/gradle/gradle/blob/RB_1.12/subprojects/core/src/main/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionPlan.java, line 335:

final List<TaskInfo> firstCycle = new ArrayList<TaskInfo>(graphWalker.findCycles().get(0));

The graphWalker.findCycles() returns an empty collection.

Comment by Szczepan Faber [ 17/Sep/14 ]

Can you try Gradle 2.0? I could swear I fixed this issue (though I don't remember why the ticket was not closed).

Comment by Damien Coraboeuf [ 18/Sep/14 ]

I'll try with Gradle 2.0 and 2.1, with the attached finalizedByBug.gradle file.

Comment by Damien Coraboeuf [ 18/Sep/14 ]

Same error with Gradle 2.0:

./gradlew --version -b finalizedByBug.gradle 

------------------------------------------------------------
Gradle 2.0
------------------------------------------------------------

Build time:   2014-07-01 07:45:34 UTC
Build number: none
Revision:     b6ead6fa452dfdadec484059191eb641d817226c

Groovy:       2.3.3
Ant:          Apache Ant(TM) version 1.9.3 compiled on December 23 2013
JVM:          1.8.0_05 (Oracle Corporation 25.5-b02)
OS:           Mac OS X 10.9.3 x86_64
./gradlew -b finalizedByBug.gradle --stacktrace

FAILURE: Build failed with an exception.

* What went wrong:
Index: 0, Size: 0

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.onOrderingCycle(DefaultTaskExecutionPlan.java:335)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.determineExecutionPlan(DefaultTaskExecutionPlan.java:231)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.ensurePopulated(DefaultTaskGraphExecuter.java:148)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:82)
Comment by Damien Coraboeuf [ 18/Sep/14 ]

Same error with Gradle 2.1:

./gradlew --version -b finalizedByBug.gradle 

------------------------------------------------------------
Gradle 2.1
------------------------------------------------------------

Build time:   2014-09-08 10:40:39 UTC
Build number: none
Revision:     e6cf70745ac11fa943e19294d19a2c527a669a53

Groovy:       2.3.6
Ant:          Apache Ant(TM) version 1.9.3 compiled on December 23 2013
JVM:          1.8.0_05 (Oracle Corporation 25.5-b02)
OS:           Mac OS X 10.9.3 x86_64
./gradlew -b finalizedByBug.gradle --stacktrace

FAILURE: Build failed with an exception.

* What went wrong:
Index: 0, Size: 0

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.onOrderingCycle(DefaultTaskExecutionPlan.java:342)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.determineExecutionPlan(DefaultTaskExecutionPlan.java:238)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.ensurePopulated(DefaultTaskGraphExecuter.java:150)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:84)
	at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
Comment by Damien Coraboeuf [ 18/Sep/14 ]

Again, I've had this error with our real big project but this can be easily reproduced using the attached finalizedByBug.gradle file.

Comment by Szczepan Faber [ 18/Sep/14 ]

Thanks a lot for investigation!

Comment by Damien Coraboeuf [ 18/Sep/14 ]

The following test in DefaultTaskExecutionPlanTest allows to reproduce the problem:

    @Issue("GRADLE-2957")
    def "dependent tasks with the same finalizer"() {
        // Finalizer task
        Task finalTask = task('finalTask')

        // Task with this finalizer
        Task taskOne = task('taskOne', finalizedBy: [finalTask])
        Task taskTwo = task('taskTwo', finalizedBy: [finalTask])

        // Task to call, with the same finalizer than one of its dependencies
        Task buggyTask = task('buggyTask', dependsOn: [taskOne], finalizedBy: [taskTwo])

        when:
        addToGraphAndPopulate([buggyTask])

        then:
        executes(taskOne, buggyTask, taskTwo, finalTask)
    }
Comment by Damien Coraboeuf [ 18/Sep/14 ]

When debugging the test, I saw that taskTwo and taskOne were considered must successors to the finalTask. It looks a bit strange to me. Any idea?

It seems to me that this should be the other way round, that a finalizer should be considered a successor of the finalized task in the graph, and not the other way round.

Comment by Szczepan Faber [ 18/Sep/14 ]

Nice debugging! You seem to be close to get it fixed I look into this problem when I have some spare cycles. In meantime, we would love a PR from you

Comment by Damien Coraboeuf [ 18/Sep/14 ]

Coming soon...

Comment by Damien Coraboeuf [ 18/Sep/14 ]

PR at https://github.com/gradle/gradle/pull/333

I was able:

  1. to design a test that reproduces the issue
  2. to fix the issue by ignoring faulty cycle detections

Damien.

Comment by Szczepan Faber [ 18/Sep/14 ]

Thanks so much! We'll look into the PR.

Generated at Wed Jun 30 12:35:59 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.