groovy-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Jochen Theodorou (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (GROOVY-8213) Closures are maybe not Threadsafe
Date Mon, 14 Aug 2017 20:49:00 GMT

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

Jochen Theodorou commented on GROOVY-8213:
------------------------------------------

I agree. If Thread T1 creates and initializes the meta class and Thread T2 then tries to call
a method using this meta class, then it is possible that T2 will not see the initialized=true.


We could think of just making it volatile, which is a performance penalty for every invokeMethod,
setProperty and getProperty, that goes through a meta class, especially bad for Closure. So
maybe we should invest more effort and recheck if initialized is false using a synchronized
block.

Now the question is what will happen when we change from initialized=true to false (for example
in EMC) and the other Thread is not notified? If that means that T2 may or may not see the
modification, then this is fine with me. Such causes are supposed to require user-side synchronization.


> 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.4.14#64029)

Mime
View raw message