thread leaks in tests
[idea/community.git] / java / java-tests / testSrc / com / intellij / psi / PsiConcurrencyStressTest.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * @author max
19  */
20 package com.intellij.psi;
21
22 import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
23 import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator;
24 import com.intellij.codeInsight.daemon.impl.HighlightVisitor;
25 import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.application.Result;
28 import com.intellij.openapi.application.ex.PathManagerEx;
29 import com.intellij.openapi.command.WriteCommandAction;
30 import com.intellij.openapi.editor.Document;
31 import com.intellij.openapi.extensions.Extensions;
32 import com.intellij.openapi.progress.ProgressManager;
33 import com.intellij.openapi.roots.LanguageLevelProjectExtension;
34 import com.intellij.pom.java.LanguageLevel;
35 import com.intellij.psi.search.GlobalSearchScope;
36 import com.intellij.testFramework.IdeaTestUtil;
37 import com.intellij.testFramework.PsiTestUtil;
38 import com.intellij.testFramework.SkipSlowTestLocally;
39 import com.intellij.testFramework.Timings;
40 import com.intellij.util.IncorrectOperationException;
41 import com.intellij.util.containers.ContainerUtil;
42 import org.jetbrains.annotations.NotNull;
43
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.Random;
47 import java.util.concurrent.CountDownLatch;
48 import java.util.concurrent.TimeUnit;
49
50 @SkipSlowTestLocally
51 public class PsiConcurrencyStressTest extends DaemonAnalyzerTestCase {
52   private volatile PsiJavaFile myFile;
53   private volatile boolean writeActionInProgress;
54
55   @Override
56   protected void setUp() throws Exception {
57     super.setUp();
58
59     LanguageLevelProjectExtension.getInstance(myProject).setLanguageLevel(LanguageLevel.JDK_1_5);
60     String root = PathManagerEx.getTestDataPath() + "/psi/repositoryUse/src";
61     PsiTestUtil.removeAllRoots(myModule, IdeaTestUtil.getMockJdk17());
62     PsiTestUtil.createTestProjectStructure(myProject, myModule, root, myFilesToDelete);
63   }
64
65   public void testStress() throws Exception {
66     int numOfThreads = 10;
67     int iterations = Timings.adjustAccordingToMySpeed(20, true);
68     System.out.println("iterations = " + iterations);
69     final int readIterations = iterations * 3;
70
71     synchronized (this) {
72       PsiClass myClass = myJavaFacade.findClass("StressClass", GlobalSearchScope.allScope(myProject));
73       assertNotNull(myClass);
74       myFile = (PsiJavaFile)myClass.getContainingFile();
75     }
76
77     final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject());
78
79     final CountDownLatch reads = new CountDownLatch(numOfThreads);
80     final Random random = new Random();
81     List<Thread> threads = ContainerUtil.map(Collections.nCopies(numOfThreads, ""), i ->
82       new Thread(() -> {
83         for (int i1 = 0; i1 < readIterations; i1++) {
84           if (myPsiManager == null) return;
85           ProgressManager.getInstance().runProcess(() -> ApplicationManager.getApplication().runReadAction(() -> {
86             assertFalse(writeActionInProgress);
87             readStep(random);
88           }), new DaemonProgressIndicator());
89         }
90
91         reads.countDown();
92       }, "stress thread" + i));
93     threads.forEach(Thread::start);
94
95     final Document document = documentManager.getDocument(myFile);
96
97     for (int i = 0; i < iterations; i++) {
98       Thread.sleep(100);
99       new WriteCommandAction(myProject, myFile) {
100         @Override
101         protected void run(@NotNull final Result result) throws Throwable {
102           writeActionInProgress = true;
103           documentManager.commitAllDocuments();
104           writeStep(random);
105           documentManager.commitAllDocuments();
106           assertEquals(document.getText(), myFile.getText());
107           writeActionInProgress = false;
108         }
109       }.execute();
110     }
111
112     assertTrue("Timed out", reads.await(5, TimeUnit.MINUTES));
113     ContainerUtil.process(threads, thread -> {
114       try {
115         thread.join();
116         return true;
117       }
118       catch (InterruptedException e) {
119         throw new RuntimeException(e);
120       }
121     });
122   }
123
124   private static void mark(final String s) {
125     //System.out.print(s);
126     //System.out.flush();
127   }
128
129   private synchronized PsiClass getPsiClass() {
130     assertTrue(myFile.isValid());
131     PsiClass psiClass = myFile.getClasses()[0];
132     assertTrue(psiClass.isValid());
133     return psiClass;
134   }
135
136   private synchronized void writeStep(final Random random) throws IncorrectOperationException {
137     switch (random.nextInt(2)) {
138       case 0 :
139         mark("+");
140         getPsiClass().add(myJavaFacade.getElementFactory().createMethod("foo" + System.currentTimeMillis(), PsiType.FLOAT));
141         break;
142       case 1 :
143         mark("-");
144         final PsiMethod[] psiMethods = getPsiClass().getMethods();
145         if (psiMethods.length > 0) {
146           WriteCommandAction.runWriteCommandAction(null, () -> {
147             psiMethods[random.nextInt(psiMethods.length)].delete();
148           });
149         }
150         break;
151     }
152   }
153
154   private void readStep(final Random random) {
155     PsiClass aClass = getPsiClass();
156     switch (random.nextInt(4)) {
157       case 0:
158         mark("v");
159         aClass.getContainingFile().accept(new PsiRecursiveElementVisitor() {
160         });
161         break;
162
163       case 1:
164         mark("m");
165         for (int offset=0; offset<myFile.getTextLength();offset++) {
166           myFile.findElementAt(offset);
167         }
168         break;
169
170       case 2:
171         mark("h");
172         aClass.accept(new PsiRecursiveElementVisitor() {
173           @Override
174           public void visitElement(final PsiElement element) {
175             super.visitElement(element);
176
177             final HighlightInfoHolder infoHolder = new HighlightInfoHolder(myFile);
178             for (HighlightVisitor visitor : Extensions.getExtensions(HighlightVisitor.EP_HIGHLIGHT_VISITOR, getProject())) {
179               HighlightVisitor v = visitor.clone(); // to avoid race for com.intellij.codeInsight.daemon.impl.DefaultHighlightVisitor.myAnnotationHolder
180               v.analyze(myFile, true, infoHolder, () -> v.visit(element));
181             }
182           }
183         });
184         break;
185
186       case 3:
187         mark("u");
188         for (PsiMethod method : aClass.getMethods()) {
189           method.getName();
190         }
191         break;
192     }
193   }
194
195   @Override
196   protected void invokeTestRunnable(@NotNull final Runnable runnable) throws Exception {
197     runnable.run();
198   }
199 }