You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Björn Kautler (JIRA)" <ji...@apache.org> on 2017/06/01 11:47:04 UTC
[jira] [Created] (GROOVY-8213) Closures are maybe not Threadsafe
Björn Kautler created GROOVY-8213:
-------------------------------------
Summary: Closures are maybe not Threadsafe
Key: GROOVY-8213
URL: https://issues.apache.org/jira/browse/GROOVY-8213
Project: Groovy
Issue Type: Bug
Affects Versions: 2.4.10
Environment: Gradle 3.5
Reporter: Björn Kautler
I just upgraded our Gradle build from 1.12 (including Groovy 1.8.6) to 3.5 (including Groovy 2.4.10).
Now I get a very strange behavior for stuff that is there for years already and it looks to me as if this is a Groovy bug, maybe a non-threadsafeness of Closures.
I have a custom task that contains the following code:
{code}
def sourceFilesSize = getSourceFiles().files.size()
def poolSize = Runtime.runtime.availableProcessors()
def executor = new ThreadPoolExecutor(poolSize, poolSize, 0, SECONDS, new ArrayBlockingQueue<Runnable>([sourceFilesSize, 1].max()))
def tasks = []
inputs.outOfDate { toTransform ->
tasks.add executor.submit {
project.exec {
if (gscPath) { // here starts com.empic.build.tasks.Ghostscript$_exec_closure8$_closure11
environment 'GSC', gscPath
def tempDir = "$temporaryDir/${Thread.currentThread().name}"
project.file(tempDir).mkdirs()
environment 'TEMP', tempDir
}
executable executablePath
def arguments = [toTransform.file.absolutePath]
if ((sourceFilesSize == 1) && getDestinationFile()) {
arguments << getDestinationFile().absolutePath
} else if (destinationDirectory) {
arguments << new File(destinationDirectory, fileNameMapping(toTransform.file.name)).absolutePath
} else {
arguments << new File(toTransform.file.parentFile, fileNameMapping(toTransform.file.name)).absolutePath
}
args arguments // here ends com.empic.build.tasks.Ghostscript$_exec_closure8$_closure11
}
}
}
executor.shutdown()
executor.awaitTermination 1, HOURS
tasks*.get() // here is line 137
{code}
When this task is executed e. g. with five source files, sometimes all works fine, sometime the build fails with
{noformat}
...
Caused by: java.util.concurrent.ExecutionException: java.lang.IllegalStateException: initialize must be called for meta class of class com.empic.build.tasks.Ghostscript$_exec_closure8$_closure11(class org.codehaus.groovy.runtime.metaclass.ClosureMetaClass) to complete initialisation process before any invocation or field/property access can be done
at com.empic.build.tasks.Ghostscript.exec(Ghostscript.groovy:137)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
... 80 more
Caused by: java.lang.IllegalStateException: initialize must be called for meta class of class com.empic.build.tasks.Ghostscript$_exec_closure8$_closure11(class org.codehaus.groovy.runtime.metaclass.ClosureMetaClass) to complete initialisation process before any invocation or field/property access can be done
{noformat}
Actually I was not able to reproduce the problem locally, but if I run the build on our CI server and attach a debugger, I indeed was able to reproduce the problem with a relatively high reproduction rate. (as in every second to third build approximately, opposed to once every 100 builds)
I logged the current thread when the {{initialized}} property is set to {{true}} and when it is requested with non-suspending breakpoints, but this seems to have caused the timing to change enough already that I was not able to reproduce the error within approximately the first two dozens of tries.
I was able to successfully break at the throwing of the {{IllegalStateException}} though. This happens in {{groovy.lang.MetaClassImpl.checkInitalised}}.
The stacktrace at this point is:
{noformat}
"pool-2-thread-3@10709" prio=5 tid=0x47 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at groovy.lang.MetaClassImpl.checkInitalised(MetaClassImpl.java:1647)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:257)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at groovy.lang.Closure.call(Closure.java:414)
at groovy.lang.Closure.call(Closure.java:408)
at groovy.lang.Closure.run(Closure.java:495)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
{noformat}
with the closure for which we are at the {{ClosureMetaClass}} in the topmost frame being the mentioned {{com.empic.build.tasks.Ghostscript$_exec_closure8$_closure11}}.
--
This message was sent by Atlassian JIRA
(v6.3.15#6346)