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:16:00 UTC

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

    [ https://issues.apache.org/jira/browse/GROOVY-9397?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17035836#comment-17035836 ] 

Paul King commented on GROOVY-9397:
-----------------------------------

I cloned the issue and will close the original. It will make it easier to understand existing commits in the codebase that use the old issue number. We still need to do further investigation. That should happen using this new issue.

> 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)