You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-issues@jackrabbit.apache.org by "Thomas Mueller (JIRA)" <ji...@apache.org> on 2016/07/05 14:39:10 UTC

[jira] [Commented] (OAK-4538) IndexDefinition.createCodec class loading deadlock

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

Thomas Mueller commented on OAK-4538:
-------------------------------------

Suggested patch (Oak 1.4):

{noformat}
--- src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java	(revision 1748577)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java	(working copy)
@@ -1296,7 +1296,13 @@
         String codecName = getOptionalValue(definition, LuceneIndexConstants.CODEC_NAME, null);
         Codec codec = null;
         if (codecName != null) {
+            // prevent LUCENE-6482
+            OakCodec ensureLucene46CodecLoaded = new OakCodec();
+            // to ensure the JVM doesn't optimize away object creation
+            // (probably not really needed; just to be save)
+            log.debug("Lucene46Codec is loaded: {}", ensureLucene46CodecLoaded);
             codec = Codec.forName(codecName);
+            log.debug("Codec is loaded: {}", codecName);
         } else if (fullTextEnabled) {
             codec = new OakCodec();
         }
{noformat}

> IndexDefinition.createCodec class loading deadlock
> --------------------------------------------------
>
>                 Key: OAK-4538
>                 URL: https://issues.apache.org/jira/browse/OAK-4538
>             Project: Jackrabbit Oak
>          Issue Type: Bug
>          Components: lucene, query
>            Reporter: Thomas Mueller
>            Assignee: Thomas Mueller
>
> Sometimes, when initializing an Oak Lucene index, a class loading deadlock can occur. Unfortunately, no deadlock is reported by the JVM when creating a full thread dump, but the thread dump typically shows the threads below. The root cause seems to be LUCENE-6482, and the reason for that is described in http://ternarysearch.blogspot.it/2013/07/static-initialization-deadlock.html
> I have created a simple, reproducible test case, and found a simple workaround in Oak, which is to load the OakCodec before a custom codec. This ensures the class OakCodec, and all superclasses, are loaded before the static initializer of Codec is run. Test case see below (un-commenting the commented line will make it work, otherwise the test results in a deadlock most of the time).
> {noformat}
> java.lang.Thread.State: RUNNABLE
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.createCodec(IndexDefinition.java:1301)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.<init>(IndexDefinition.java:260)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.<init>(IndexDefinition.java:228)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexNode.open(IndexNode.java:48)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker.findIndexNode(IndexTracker.java:179)
> 	- locked <0x00000007ff915448> (a org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker.acquireIndexNode(IndexTracker.java:154)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.LucenePropertyIndex.getPlans(LucenePropertyIndex.java:250)
> 	at org.apache.jackrabbit.oak.query.QueryImpl.getBestSelectorExecutionPlan(QueryImpl.java:1016)
> 	at org.apache.jackrabbit.oak.query.QueryImpl.getBestSelectorExecutionPlan(QueryImpl.java:949)
> 	at org.apache.jackrabbit.oak.query.ast.SelectorImpl.prepare(SelectorImpl.java:288)
> 	at org.apache.jackrabbit.oak.query.QueryImpl.prepare(QueryImpl.java:631)
> 	at org.apache.jackrabbit.oak.query.QueryEngineImpl.prepareAndSelect(QueryEngineImpl.java:298)
> 	at org.apache.jackrabbit.oak.query.QueryEngineImpl.executeQuery(QueryEngineImpl.java:273)
> 	at org.apache.jackrabbit.oak.query.QueryEngineImpl.executeQuery(QueryEngineImpl.java:233)
> 	at org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager.resolveUUID(IdentifierManager.java:314)
> 	at org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager.resolveUUID(IdentifierManager.java:308)
> 	at org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager.resolveUUID(IdentifierManager.java:304)
> 	at org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager.getTree(IdentifierManager.java:133)
> 	at org.apache.jackrabbit.oak.security.authentication.token.TokenProviderImpl.getTokenInfo(TokenProviderImpl.java:250)
> 	at org.apache.jackrabbit.oak.security.authentication.token.TokenAuthentication.validateCredentials(TokenAuthentication.java:81)
> "aysnc-index-update-fulltext-async" prio=5 tid=0x00007fe845e51800 nid=0xb407 in Object.wait() [0x0000700005f2f000]
>    java.lang.Thread.State: RUNNABLE
> 	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
> 	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
> 	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
> 	at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
> 	at java.lang.Class.newInstance(Class.java:379)
> 	at org.apache.lucene.util.NamedSPILoader.reload(NamedSPILoader.java:67)
> 	- locked <0x00000007d2ba9120> (a org.apache.lucene.util.NamedSPILoader)
> 	at org.apache.lucene.util.NamedSPILoader.<init>(NamedSPILoader.java:45)
> 	at org.apache.lucene.util.NamedSPILoader.<init>(NamedSPILoader.java:37)
> 	at org.apache.lucene.codecs.Codec.<clinit>(Codec.java:41)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.createCodec(IndexDefinition.java:1299)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.<init>(IndexDefinition.java:260)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.<init>(IndexDefinition.java:224)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorContext.<init>(LuceneIndexEditorContext.java:170)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditor.<init>(LuceneIndexEditor.java:132)
> 	at org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider.getIndexEditor(LuceneIndexEditorProvider.java:72)
> 	at org.apache.jackrabbit.oak.plugins.index.CompositeIndexEditorProvider.getIndexEditor(CompositeIndexEditorProvider.java:74)
> 	at org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardIndexEditorProvider.getIndexEditor(WhiteboardIndexEditorProvider.java:52)
> 	at org.apache.jackrabbit.oak.plugins.index.IndexUpdate.collectIndexEditors(IndexUpdate.java:201)
> 	at org.apache.jackrabbit.oak.plugins.index.IndexUpdate.enter(IndexUpdate.java:144)
> 	at org.apache.jackrabbit.oak.spi.commit.VisibleEditor.enter(VisibleEditor.java:57)
> 	at org.apache.jackrabbit.oak.spi.commit.EditorDiff.process(EditorDiff.java:49)
> 	at org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate.updateIndex(AsyncIndexUpdate.java:510)
> 	at org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate.runWhenPermitted(AsyncIndexUpdate.java:439)
> {noformat}
> Test case:
> {noformat}
> public class StaticInitDeadlock {
>     public static class Codec {
>         static {
>             debug("<clinit>");
>             forName("OtherCodec");
>             forName("OakCodec");
>             debug("<clinit> done");
>         }
>         static Codec forName(String name) {
>             debug("Codec.forName(" + name + ")");
>             try {
>                 return (Codec) Class.forName("StaticInitDeadlock$" + name)
>                         .newInstance();
>             } catch (Exception e) {
>                 e.printStackTrace();
>                 return null;
>             }
>         }
>     }
>     static class OtherCodec extends Codec {
>         static {
>             debug("OtherCodec <clinit>");
>         }
>     }
>     static class OakCodec extends Codec {
>         static {
>             debug("OakCodec <clinit>");
>         }
>     }
>     static void createCodec(String codecName) {
>         debug("createCodec (" + codecName + ")");
>         if (codecName != null) {
>             // new OakCodec();
>             Codec.forName(codecName);
>         } else {
>             new OakCodec();
>         }
>     }
>     public static void main(String[] args) throws InterruptedException {
>         Thread thread = new Thread("Thread 2") {
>             @Override
>             public void run() {
>                 debug("loading other codec...");
>                 createCodec("OtherCodec");
>                 debug("loading other codec done!");
>             }
>         };
>         thread.start();
>         Thread.currentThread().setName("Thread 1");
>         debug("loading default (oak) codec...");
>         createCodec(null);
>         debug("joining thread...");
>         thread.join();
>         debug("done!");
>     }
>     static void debug(String msg) {
>         Thread t = Thread.currentThread();
>         StackTraceElement[] st = t.getStackTrace();
>         int depth = st.length;
>         String indent = new String(new char[depth]).replace("\0", " ");
>         System.out.println(t.getName() + ": " + indent + st[2] + " " + msg);
>     }
> }
> {noformat}
> Output when deadlocked:
> {noformat}
> Thread 2:    StaticInitDeadlock$1.run(StaticInitDeadlock.java:56) loading other codec...
> Thread 1:    StaticInitDeadlock.main(StaticInitDeadlock.java:63) loading default (oak) codec...
> Thread 2:     StaticInitDeadlock.createCodec(StaticInitDeadlock.java:43) createCodec (OtherCodec)
> Thread 1:     StaticInitDeadlock.createCodec(StaticInitDeadlock.java:43) createCodec (null)
> Thread 2:      StaticInitDeadlock$Codec.<clinit>(StaticInitDeadlock.java:11) <clinit>
> Thread 2:       StaticInitDeadlock$Codec.forName(StaticInitDeadlock.java:18) Codec.forName(OtherCodec)
> Thread 2:          StaticInitDeadlock$OtherCodec.<clinit>(StaticInitDeadlock.java:32) OtherCodec <clinit>
> Thread 2:       StaticInitDeadlock$Codec.forName(StaticInitDeadlock.java:18) Codec.forName(OakCodec)
> {noformat}
> Output without deadlock:
> {noformat}
> Thread 1:    StaticInitDeadlock.main(StaticInitDeadlock.java:63) loading default (oak) codec...
> Thread 2:    StaticInitDeadlock$1.run(StaticInitDeadlock.java:56) loading other codec...
> Thread 1:     StaticInitDeadlock.createCodec(StaticInitDeadlock.java:43) createCodec (null)
> Thread 2:     StaticInitDeadlock.createCodec(StaticInitDeadlock.java:43) createCodec (OtherCodec)
> Thread 1:      StaticInitDeadlock$Codec.<clinit>(StaticInitDeadlock.java:11) <clinit>
> Thread 1:       StaticInitDeadlock$Codec.forName(StaticInitDeadlock.java:18) Codec.forName(OtherCodec)
> Thread 1:          StaticInitDeadlock$OtherCodec.<clinit>(StaticInitDeadlock.java:32) OtherCodec <clinit>
> Thread 1:       StaticInitDeadlock$Codec.forName(StaticInitDeadlock.java:18) Codec.forName(OakCodec)
> Thread 1:      StaticInitDeadlock$Codec.<clinit>(StaticInitDeadlock.java:14) <clinit> done
> Thread 1:      StaticInitDeadlock$OakCodec.<clinit>(StaticInitDeadlock.java:38) OakCodec <clinit>
> Thread 1:    StaticInitDeadlock.main(StaticInitDeadlock.java:65) joining thread...
> Thread 2:      StaticInitDeadlock$Codec.forName(StaticInitDeadlock.java:18) Codec.forName(OtherCodec)
> Thread 2:    StaticInitDeadlock$1.run(StaticInitDeadlock.java:58) loading other codec done!
> Thread 1:    StaticInitDeadlock.main(StaticInitDeadlock.java:67) done!
> {noformat}



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)