You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@lucene.apache.org by "Lucene/Solr QA (Jira)" <ji...@apache.org> on 2019/12/07 20:45:00 UTC

[jira] [Commented] (LUCENE-9084) circular synchronization wait (potential deadlock) in AnalyzingInfixSuggester

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

Lucene/Solr QA commented on LUCENE-9084:
----------------------------------------

| (x) *{color:red}-1 overall{color}* |
\\
\\
|| Vote || Subsystem || Runtime || Comment ||
|| || || || {color:brown} Prechecks {color} ||
| {color:red}-1{color} | {color:red} test4tests {color} | {color:red}  0m  0s{color} | {color:red} The patch doesn't appear to include any new or modified tests. Please justify why no new tests are needed for this patch. Also please list what manual steps were performed to verify this patch. {color} |
|| || || || {color:brown} master Compile Tests {color} ||
| {color:green}+1{color} | {color:green} compile {color} | {color:green}  0m 17s{color} | {color:green} master passed {color} |
|| || || || {color:brown} Patch Compile Tests {color} ||
| {color:green}+1{color} | {color:green} compile {color} | {color:green}  0m 19s{color} | {color:green} the patch passed {color} |
| {color:green}+1{color} | {color:green} javac {color} | {color:green}  0m 19s{color} | {color:green} the patch passed {color} |
| {color:green}+1{color} | {color:green} Release audit (RAT) {color} | {color:green}  0m 19s{color} | {color:green} the patch passed {color} |
| {color:green}+1{color} | {color:green} Check forbidden APIs {color} | {color:green}  0m 19s{color} | {color:green} the patch passed {color} |
| {color:green}+1{color} | {color:green} Validate source patterns {color} | {color:green}  0m 19s{color} | {color:green} the patch passed {color} |
|| || || || {color:brown} Other Tests {color} ||
| {color:green}+1{color} | {color:green} unit {color} | {color:green}  0m 57s{color} | {color:green} suggest in the patch passed. {color} |
| {color:black}{color} | {color:black} {color} | {color:black}  3m  0s{color} | {color:black} {color} |
\\
\\
|| Subsystem || Report/Notes ||
| JIRA Issue | LUCENE-9084 |
| JIRA Patch URL | https://issues.apache.org/jira/secure/attachment/12987797/LUCENE-9084.patch |
| Optional Tests |  compile  javac  unit  ratsources  checkforbiddenapis  validatesourcepatterns  |
| uname | Linux lucene1-us-west 4.15.0-54-generic #58-Ubuntu SMP Mon Jun 24 10:55:24 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux |
| Build tool | ant |
| Personality | /home/jenkins/jenkins-slave/workspace/PreCommit-LUCENE-Build/sourcedir/dev-tools/test-patch/lucene-solr-yetus-personality.sh |
| git revision | master / a1e51cd7778 |
| ant | version: Apache Ant(TM) version 1.10.5 compiled on March 28 2019 |
| Default Java | LTS |
|  Test Results | https://builds.apache.org/job/PreCommit-LUCENE-Build/241/testReport/ |
| modules | C: lucene/suggest U: lucene/suggest |
| Console output | https://builds.apache.org/job/PreCommit-LUCENE-Build/241/console |
| Powered by | Apache Yetus 0.7.0   http://yetus.apache.org |


This message was automatically generated.



> circular synchronization wait (potential deadlock) in AnalyzingInfixSuggester
> -----------------------------------------------------------------------------
>
>                 Key: LUCENE-9084
>                 URL: https://issues.apache.org/jira/browse/LUCENE-9084
>             Project: Lucene - Core
>          Issue Type: Bug
>          Components: core/search
>            Reporter: Paul Ward
>            Priority: Major
>         Attachments: LUCENE-9084.patch
>
>
> h1. Github Pull Request  
> I created a pull request on github for this:
> [https://github.com/apache/lucene-solr/pull/1064]
> h1. Bug Description
> Detailed code (snippets and links) are in the sections after this overview (section **Detailed Code** and **This Patch's Code**).
> Method {{ensureOpen()}} is {{synchronized}} (acquires {{this}}) and its body contains a {{synchronized (searcherMgrLock)}} block (i.e., then acquires {{searcherMgrLock}}).
> Method {{ensureOpen()}} is called two times from public methods {{add()}} and {{update()}}.
> A thread calling public methods {{add()}} or {{update()}} will acquire locks in order:
> {code:java}
> this -> searcherMgrLock
> {code}
> Public method {{build()}} has a {{synchronized (searcherMgrLock)}} block in which it calls method {{add()}}. Method {{add()}}, as described above, calls method {{ensureOpen()}}.
> Therefore, a thread calling public method {{build()}} will acquire locks in order:
> {code:java}
> searcherMgrLock -> this -> searcherMgrLock
> {code}
> 2 threads can acquire locks in different order which may cause a circular wait (deadlock).
> I do not know which threads call these methods, but there is a lot of synchronization in these methods and in this file, so I think these methods must be called concurrently.
> One thread can acquire:
> {{this -> searcherMgrLock}} (the first order above)
> and the other thread can acquire:
> {{searcherMgrLock -> this}} (the second order above).
> Note how the above 2 orders lead to a circular wait.
> h1. Detailed Code
> Method {{ensureOpen()}} is {{synchronized}} and its body contains a {{synchronized (searcherMgrLock)}}:
> {code:java}
>   private synchronized void ensureOpen() throws IOException { <<<<<<<<<<< see the synchronized keyword
>     if (writer == null) {
>       if (DirectoryReader.indexExists(dir)) {
>         // Already built; open it:
>         writer = new IndexWriter(dir, getIndexWriterConfig(getGramAnalyzer(), IndexWriterConfig.OpenMode.APPEND));
>       } else {
>         writer = new IndexWriter(dir, getIndexWriterConfig(getGramAnalyzer(), IndexWriterConfig.OpenMode.CREATE));
>       }
>       synchronized (searcherMgrLock) { <<<<<<<<<<<<<<<<<<<<<<<
> {code}
> [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L371-L379]
> Method {{ensureOpen()}} is called two times from public methods {{add()}} and {{update()}}:
> {code:java}
>   public void add(BytesRef text, Set<BytesRef> contexts, long weight, BytesRef payload) throws IOException {
>     ensureOpen(); <<<<<<<<<<<<<<<<<<<<<<
> {code}
> [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L394-L395]
> {code:java}
>   public void update(BytesRef text, Set<BytesRef> contexts, long weight, BytesRef payload) throws IOException {
>     ensureOpen(); <<<<<<<<<<<<<<<<<<<<
> {code}
> [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L406-L407]
> Public method {{build()}} has a {{synchronized (searcherMgrLock)}} block in which it calls method {{add()}}:
> {code:java}
>   @Override
>   public void build(InputIterator iter) throws IOException {
>     
>     synchronized (searcherMgrLock) { <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>       if (searcherMgr != null) {
>         searcherMgr.close();
>         searcherMgr = null;
>       }
>       if (writer != null) {
>         writer.close();
>         writer = null;
>       }
>       boolean success = false;
>       try {
>         // First pass: build a temporary normal Lucene index,
>         // just indexing the suggestions as they iterate:
>         writer = new IndexWriter(dir,
>             getIndexWriterConfig(getGramAnalyzer(), IndexWriterConfig.OpenMode.CREATE));
>         //long t0 = System.nanoTime();
>         // TODO: use threads?
>         BytesRef text;
>         while ((text = iter.next()) != null) {
>           BytesRef payload;
>           if (iter.hasPayloads()) {
>             payload = iter.payload();
>           } else {
>             payload = null;
>           }
>           add(text, iter.contexts(), iter.weight(), payload);  <<<<<<<<<<<<<<<<<<<<<
> {code}
> [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L278-L310]
> Method {{add()}} is the same one I linked above.
> h1. This Patch's Code
> Note that method {{ensureOpen()}} (inlined above) is the *only* place (method or synchronization block) that is {{synchronized}} on {{this}}.
> *All* the other synchronizations in this file are on {{searcherMgrLock}}.
> This CR removes the {{synchronized}} on {{this}} (again, being the only {{synchronized}} on {{this}}, we can do this change safely).
> And moves {{synchronized (searcherMgrLock)}} a few lines above, to protect the entire code (that otherwise was protected by {{synchronized}} on {{this}}).
> The above breaks the lock cycle I described earlier.
> The fix looks big because it changes indentation.
> But only one line is moved by a few lines up.
> I.e., from this:
> {code:java}
>   private synchronized void ensureOpen() throws IOException {
>     if (writer == null) {
>       if (DirectoryReader.indexExists(dir)) {
>         // Already built; open it:
>         writer = new IndexWriter(dir, getIndexWriterConfig(getGramAnalyzer(), IndexWriterConfig.OpenMode.APPEND));
>       } else {
>         writer = new IndexWriter(dir, getIndexWriterConfig(getGramAnalyzer(), IndexWriterConfig.OpenMode.CREATE));
>       }
>       synchronized (searcherMgrLock) { <<<<<<<<<<<<<<<<<<<<<< move from here
>         SearcherManager oldSearcherMgr = searcherMgr;
>         searcherMgr = new SearcherManager(writer, null);
>         if (oldSearcherMgr != null) {
>           oldSearcherMgr.close();
>         }
>       }
>     }
>   }
> {code}
> To this:
> {code:java}
>   private void ensureOpen() throws IOException { <<<<<<<<<<<<<<<<<< remove synchronized --- can lead to circular wait --- and legal to remove
>     synchronized (searcherMgrLock) { <<<<<<<<<<<<<<<<<<<<< move to here
>       if (writer == null) {
>         if (DirectoryReader.indexExists(dir)) {
>           // Already built; open it:
>           writer = new IndexWriter(dir, getIndexWriterConfig(getGramAnalyzer(), IndexWriterConfig.OpenMode.APPEND));
>         } else {
>           writer = new IndexWriter(dir, getIndexWriterConfig(getGramAnalyzer(), IndexWriterConfig.OpenMode.CREATE));
>         }
>         SearcherManager oldSearcherMgr = searcherMgr;
>         searcherMgr = new SearcherManager(writer, null);
>         if (oldSearcherMgr != null) {
>           oldSearcherMgr.close();
>         }
>       }
>     }
>   }
> {code}
> Here are all the places where {{synchronized (searcherMgrLock)}} appears in this file (and again, no other {{synchronized}} on other objects is done):
>  - [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L281-L332]
>  - [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L379-L385]
>  - [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L654-L657]
>  - [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L870-L873]
>  - [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L898-L901]
>  - [https://github.com/apache/lucene-solr/blob/master/lucene/suggest/src/java/org/apache/lucene/search/suggest/analyzing/AnalyzingInfixSuggester.java#L926-L929]
> I.e., doing the synchronization like above is safe and consistent with the rest of the file.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@lucene.apache.org
For additional commands, e-mail: issues-help@lucene.apache.org