You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Paul King (Jira)" <ji...@apache.org> on 2020/02/13 01:14:00 UTC

[jira] [Updated] (GROOVY-9397) CLONE - Closures are maybe not Threadsafe

     [ https://issues.apache.org/jira/browse/GROOVY-9397?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Paul King updated GROOVY-9397:
------------------------------
    Fix Version/s:     (was: 2.4.13)

> CLONE - Closures are maybe not Threadsafe
> -----------------------------------------
>
>                 Key: GROOVY-9397
>                 URL: https://issues.apache.org/jira/browse/GROOVY-9397
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 2.4.10
>         Environment: Gradle 3.5
>            Reporter: Björn Kautler
>            Priority: Major
>
> 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
(v8.3.4#803005)