You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/04/08 09:12:42 UTC
[groovy] branch GROOVY_3_0_X updated: GROOVY-9397: Make respondsTo
thread safe
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
new f3d4005 GROOVY-9397: Make respondsTo thread safe
f3d4005 is described below
commit f3d400574e43df7bbdc0e9136f13e756a3596266
Author: Benjamin Roe <be...@cirrus.com>
AuthorDate: Thu Mar 11 11:37:05 2021 +0000
GROOVY-9397: Make respondsTo thread safe
By changing the initialized state of the Closure's Metaclass without any
locking, if the Closure is visible to another thread then any call to
invokeMethod can see an uninitialized closure, causing an exception.
This fix removes the need to change the initialized state of the
closure.
(cherry picked from commit be55ede40d9ab1b60c4ac9074ef3bce25a6db74e)
---
src/main/java/groovy/lang/MetaClassImpl.java | 26 +++++++++++++---------
.../groovy/runtime/metaclass/ClosureMetaClass.java | 4 +---
src/test/groovy/ClosureMethodCallTest.groovy | 19 ++++++++++++++++
3 files changed, 35 insertions(+), 14 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index a01022f..1b71bc0 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -3432,18 +3432,22 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
*/
public synchronized void initialize() {
if (!isInitialized()) {
- fillMethodIndex();
- try {
- addProperties();
- } catch (Throwable e) {
- if (!AndroidSupport.isRunningAndroid()) {
- UncheckedThrow.rethrow(e);
- }
- // Introspection failure...
- // May happen in Android
- }
- setInitialized(true);
+ reinitialize();
+ }
+ }
+
+ protected synchronized void reinitialize() {
+ fillMethodIndex();
+ try {
+ addProperties();
+ } catch (Throwable e) {
+ if (!AndroidSupport.isRunningAndroid()) {
+ UncheckedThrow.rethrow(e);
}
+ // Introspection failure...
+ // May happen in Android
+ }
+ setInitialized(true);
}
private void addProperties() {
diff --git a/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java b/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
index af0bf2b..fafb786 100644
--- a/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
+++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
@@ -696,9 +696,7 @@ public final class ClosureMetaClass extends MetaClassImpl {
private synchronized void loadMetaInfo() {
if (metaMethodIndex.isEmpty()) {
- initialized = false;
- super.initialize();
- initialized = true;
+ reinitialize();
}
}
diff --git a/src/test/groovy/ClosureMethodCallTest.groovy b/src/test/groovy/ClosureMethodCallTest.groovy
index d442373..feac6bf 100644
--- a/src/test/groovy/ClosureMethodCallTest.groovy
+++ b/src/test/groovy/ClosureMethodCallTest.groovy
@@ -20,6 +20,9 @@ package groovy
import groovy.test.GroovyTestCase
+import java.util.concurrent.Executor
+import java.util.concurrent.Executors
+
class ClosureMethodCallTest extends GroovyTestCase {
void testCallingClosureWithMultipleArguments() {
@@ -119,4 +122,20 @@ class ClosureMethodCallTest extends GroovyTestCase {
assert ref(1) == 1
'''
}
+
+ //GROOVY-9397
+ void testRespondsToIsThreadSafe() {
+ final Executor executor = Executors.newCachedThreadPool()
+ try {
+ final Closure action = { -> }
+ // ensure that executing the closure and calling respondsTo
+ // concurrently doesn't throw an exception.
+ for (int i = 0; i < 500; ++i) {
+ executor.execute(action)
+ executor.execute(() -> action.respondsTo('test'))
+ }
+ } finally {
+ executor.shutdownNow();
+ }
+ }
}