xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerParallelizer.java (revision b93c48943c452edcd01170fc7cff56485276e385)
1b5840353SAdam Hornáček /*
2b5840353SAdam Hornáček  * CDDL HEADER START
3b5840353SAdam Hornáček  *
4b5840353SAdam Hornáček  * The contents of this file are subject to the terms of the
5b5840353SAdam Hornáček  * Common Development and Distribution License (the "License").
6b5840353SAdam Hornáček  * You may not use this file except in compliance with the License.
7b5840353SAdam Hornáček  *
8b5840353SAdam Hornáček  * See LICENSE.txt included in this distribution for the specific
9b5840353SAdam Hornáček  * language governing permissions and limitations under the License.
10b5840353SAdam Hornáček  *
11b5840353SAdam Hornáček  * When distributing Covered Code, include this CDDL HEADER in each
12b5840353SAdam Hornáček  * file and include the License file at LICENSE.txt.
13b5840353SAdam Hornáček  * If applicable, add the following below this CDDL HEADER, with the
14b5840353SAdam Hornáček  * fields enclosed by brackets "[]" replaced with your own identifying
15b5840353SAdam Hornáček  * information: Portions Copyright [yyyy] [name of copyright owner]
16b5840353SAdam Hornáček  *
17b5840353SAdam Hornáček  * CDDL HEADER END
18b5840353SAdam Hornáček  */
19b5840353SAdam Hornáček 
20b5840353SAdam Hornáček /*
215d9f3aa0SAdam Hornáček  * Copyright (c) 2017, 2020, Chris Fraire <cfraire@me.com>.
22e92cec67SVladimir Kotal  * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
23b5840353SAdam Hornáček  */
249805b761SAdam Hornáček package org.opengrok.indexer.index;
25b5840353SAdam Hornáček 
26b5840353SAdam Hornáček import java.util.concurrent.ExecutorService;
27b5840353SAdam Hornáček import java.util.concurrent.Executors;
28b5840353SAdam Hornáček import java.util.concurrent.ForkJoinPool;
29b124dbe6SVladimir Kotal import java.util.concurrent.ScheduledThreadPoolExecutor;
306de4f5aaSChris Fraire 
319805b761SAdam Hornáček import org.opengrok.indexer.analysis.Ctags;
329805b761SAdam Hornáček import org.opengrok.indexer.analysis.CtagsValidator;
339805b761SAdam Hornáček import org.opengrok.indexer.configuration.RuntimeEnvironment;
349805b761SAdam Hornáček import org.opengrok.indexer.util.BoundedBlockingObjectPool;
352d875ddeSChris Fraire import org.opengrok.indexer.util.CtagsUtil;
36e829566cSChris Fraire import org.opengrok.indexer.util.LazilyInstantiate;
379805b761SAdam Hornáček import org.opengrok.indexer.util.ObjectFactory;
389805b761SAdam Hornáček import org.opengrok.indexer.util.ObjectPool;
39b5840353SAdam Hornáček 
40b5840353SAdam Hornáček /**
41b5840353SAdam Hornáček  * Represents a container for executors that enable parallelism for indexing
42b5840353SAdam Hornáček  * across projects and repositories and also within any {@link IndexDatabase}
43b5840353SAdam Hornáček  * instance -- with global limits for all execution.
44b5840353SAdam Hornáček  * <p>A fixed-thread pool is used for parallelism across repositories, and a
45b5840353SAdam Hornáček  * work-stealing {@link ForkJoinPool} is used for parallelism within any
46b5840353SAdam Hornáček  * {@link IndexDatabase}. Threads in the former pool are customers of the
4734eac45cSChris Fraire  * latter, and the bulk of work is done in the latter pool. The work-stealing
4834eac45cSChris Fraire  * {@link ForkJoinPool} makes use of a corresponding fixed pool of {@link Ctags}
4934eac45cSChris Fraire  * instances.
5034eac45cSChris Fraire  * <p>Additionally there are pools for executing for history, for renamings in
5134eac45cSChris Fraire  * history, and for watching the {@link Ctags} instances for timing purposes.
52b5840353SAdam Hornáček  */
53b5840353SAdam Hornáček public class IndexerParallelizer implements AutoCloseable {
54b5840353SAdam Hornáček 
55e829566cSChris Fraire     private final RuntimeEnvironment env;
56e829566cSChris Fraire     private final int indexingParallelism;
57e829566cSChris Fraire 
58e829566cSChris Fraire     private LazilyInstantiate<ForkJoinPool> lzForkJoinPool;
59e829566cSChris Fraire     private LazilyInstantiate<ObjectPool<Ctags>> lzCtagsPool;
60e829566cSChris Fraire     private LazilyInstantiate<ExecutorService> lzFixedExecutor;
61e829566cSChris Fraire     private LazilyInstantiate<ExecutorService> lzHistoryExecutor;
62dc2490c1SVladimir Kotal     private LazilyInstantiate<ExecutorService> lzHistoryFileExecutor;
63b124dbe6SVladimir Kotal     private LazilyInstantiate<ExecutorService> lzCtagsWatcherExecutor;
64e92cec67SVladimir Kotal     private LazilyInstantiate<ExecutorService> lzXrefWatcherExecutor;
65b124dbe6SVladimir Kotal 
66b5840353SAdam Hornáček     /**
67b5840353SAdam Hornáček      * Initializes a new instance using settings from the specified environment
68b5840353SAdam Hornáček      * instance.
69b5840353SAdam Hornáček      * @param env a defined instance
70b5840353SAdam Hornáček      */
IndexerParallelizer(RuntimeEnvironment env)71b5840353SAdam Hornáček     public IndexerParallelizer(RuntimeEnvironment env) {
72e829566cSChris Fraire         if (env == null) {
73e829566cSChris Fraire             throw new IllegalArgumentException("env is null");
74e829566cSChris Fraire         }
75e829566cSChris Fraire         this.env = env;
76e829566cSChris Fraire         /*
77e829566cSChris Fraire          * Save the following value explicitly because it must not change for
78e829566cSChris Fraire          * an IndexerParallelizer instance.
79e829566cSChris Fraire          */
80e829566cSChris Fraire         this.indexingParallelism = env.getIndexingParallelism();
81b5840353SAdam Hornáček 
82e829566cSChris Fraire         createLazyForkJoinPool();
83e829566cSChris Fraire         createLazyCtagsPool();
84e829566cSChris Fraire         createLazyFixedExecutor();
85e829566cSChris Fraire         createLazyHistoryExecutor();
86dc607a84SVladimir Kotal         createLazyHistoryFileExecutor();
87b124dbe6SVladimir Kotal         createLazyCtagsWatcherExecutor();
88e92cec67SVladimir Kotal         createLazyXrefWatcherExecutor();
89b5840353SAdam Hornáček     }
90b5840353SAdam Hornáček 
91b5840353SAdam Hornáček     /**
92b5840353SAdam Hornáček      * @return the fixedExecutor
93b5840353SAdam Hornáček      */
getFixedExecutor()94b5840353SAdam Hornáček     public ExecutorService getFixedExecutor() {
9534eac45cSChris Fraire         return lzFixedExecutor.get();
96b5840353SAdam Hornáček     }
97b5840353SAdam Hornáček 
98b5840353SAdam Hornáček     /**
99b5840353SAdam Hornáček      * @return the forkJoinPool
100b5840353SAdam Hornáček      */
getForkJoinPool()101b5840353SAdam Hornáček     public ForkJoinPool getForkJoinPool() {
10234eac45cSChris Fraire         return lzForkJoinPool.get();
103b5840353SAdam Hornáček     }
104b5840353SAdam Hornáček 
105b5840353SAdam Hornáček     /**
106b5840353SAdam Hornáček      * @return the ctagsPool
107b5840353SAdam Hornáček      */
getCtagsPool()108b5840353SAdam Hornáček     public ObjectPool<Ctags> getCtagsPool() {
10934eac45cSChris Fraire         return lzCtagsPool.get();
110b5840353SAdam Hornáček     }
111b5840353SAdam Hornáček 
112e829566cSChris Fraire     /**
113dc2490c1SVladimir Kotal      * @return the ExecutorService used for history parallelism (repository level)
114e829566cSChris Fraire      */
getHistoryExecutor()115e829566cSChris Fraire     public ExecutorService getHistoryExecutor() {
11634eac45cSChris Fraire         return lzHistoryExecutor.get();
117e829566cSChris Fraire     }
118e829566cSChris Fraire 
119e829566cSChris Fraire     /**
120dc2490c1SVladimir Kotal      * @return the ExecutorService used for history parallelism (file level)
121e829566cSChris Fraire      */
getHistoryFileExecutor()122dc2490c1SVladimir Kotal     public ExecutorService getHistoryFileExecutor() {
123dc2490c1SVladimir Kotal         return lzHistoryFileExecutor.get();
124e829566cSChris Fraire     }
125e829566cSChris Fraire 
126e829566cSChris Fraire     /**
127b124dbe6SVladimir Kotal      * @return the Executor used for ctags parallelism
128b124dbe6SVladimir Kotal      */
getCtagsWatcherExecutor()129b124dbe6SVladimir Kotal     public ExecutorService getCtagsWatcherExecutor() {
13034eac45cSChris Fraire         return lzCtagsWatcherExecutor.get();
131b124dbe6SVladimir Kotal     }
132b124dbe6SVladimir Kotal 
133b124dbe6SVladimir Kotal     /**
134e92cec67SVladimir Kotal      * @return the Executor used for enforcing xref timeouts.
135e92cec67SVladimir Kotal      */
getXrefWatcherExecutor()136e92cec67SVladimir Kotal     public ExecutorService getXrefWatcherExecutor() {
137e92cec67SVladimir Kotal         return lzXrefWatcherExecutor.get();
138e92cec67SVladimir Kotal     }
139e92cec67SVladimir Kotal 
140e92cec67SVladimir Kotal     /**
141e829566cSChris Fraire      * Calls {@link #bounce()}, which prepares for -- but does not start -- new
142e829566cSChris Fraire      * pools.
143e829566cSChris Fraire      */
144b5840353SAdam Hornáček     @Override
close()145a72324b1SAdam Hornáček     public void close() {
146e829566cSChris Fraire         bounce();
147a72324b1SAdam Hornáček     }
148e829566cSChris Fraire 
149e829566cSChris Fraire     /**
15034eac45cSChris Fraire      * Shuts down the instance's executors if any of the getters were called,
15134eac45cSChris Fraire      * releasing all resources; and prepares them to be called again to return
15234eac45cSChris Fraire      * new instances.
153e829566cSChris Fraire      * <p>
154e829566cSChris Fraire      * N.b. this method is not thread-safe w.r.t. the getters, so care must be
155e829566cSChris Fraire      * taken that any scheduled work has been completed and that no other
156e829566cSChris Fraire      * thread might call those methods simultaneously with this method.
157e829566cSChris Fraire      * <p>
158e829566cSChris Fraire      * The JVM will await any instantiated thread pools until they are
159e829566cSChris Fraire      * explicitly shut down. A principle intention of this method is to
160e829566cSChris Fraire      * facilitate OpenGrok test classes that run serially. The non-test
161e829566cSChris Fraire      * processes using {@link IndexerParallelizer} -- i.e. {@code opengrok.jar}
162e829566cSChris Fraire      * indexer or opengrok-web -- would only need a one-way shutdown; but they
163e829566cSChris Fraire      * call this method satisfactorily too.
164e829566cSChris Fraire      */
bounce()165e829566cSChris Fraire     public void bounce() {
1668df660a0SVladimir Kotal         bounceForkJoinPool();
1678df660a0SVladimir Kotal         bounceFixedExecutor();
1688df660a0SVladimir Kotal         bounceCtagsPool();
1698df660a0SVladimir Kotal         bounceHistoryExecutor();
1708df660a0SVladimir Kotal         bounceHistoryRenamedExecutor();
1718df660a0SVladimir Kotal         bounceCtagsWatcherExecutor();
172e92cec67SVladimir Kotal         bounceXrefWatcherExecutor();
1738df660a0SVladimir Kotal     }
1748df660a0SVladimir Kotal 
bounceForkJoinPool()1758df660a0SVladimir Kotal     private void bounceForkJoinPool() {
17634eac45cSChris Fraire         if (lzForkJoinPool.isActive()) {
17734eac45cSChris Fraire             ForkJoinPool formerForkJoinPool = lzForkJoinPool.get();
178e829566cSChris Fraire             createLazyForkJoinPool();
179e829566cSChris Fraire             formerForkJoinPool.shutdown();
180a72324b1SAdam Hornáček         }
1818df660a0SVladimir Kotal     }
182e829566cSChris Fraire 
bounceFixedExecutor()1838df660a0SVladimir Kotal     private void bounceFixedExecutor() {
18434eac45cSChris Fraire         if (lzFixedExecutor.isActive()) {
18534eac45cSChris Fraire             ExecutorService formerFixedExecutor = lzFixedExecutor.get();
186e829566cSChris Fraire             createLazyFixedExecutor();
187e829566cSChris Fraire             formerFixedExecutor.shutdown();
188a72324b1SAdam Hornáček         }
1898df660a0SVladimir Kotal     }
190e829566cSChris Fraire 
bounceCtagsPool()1918df660a0SVladimir Kotal     private void bounceCtagsPool() {
19234eac45cSChris Fraire         if (lzCtagsPool.isActive()) {
19334eac45cSChris Fraire             ObjectPool<Ctags> formerCtagsPool = lzCtagsPool.get();
194e829566cSChris Fraire             createLazyCtagsPool();
195e829566cSChris Fraire             formerCtagsPool.shutdown();
196e829566cSChris Fraire         }
1978df660a0SVladimir Kotal     }
198e829566cSChris Fraire 
bounceHistoryExecutor()1998df660a0SVladimir Kotal     private void bounceHistoryExecutor() {
20034eac45cSChris Fraire         if (lzHistoryExecutor.isActive()) {
20134eac45cSChris Fraire             ExecutorService formerHistoryExecutor = lzHistoryExecutor.get();
202e829566cSChris Fraire             createLazyHistoryExecutor();
20334eac45cSChris Fraire             formerHistoryExecutor.shutdown();
204e829566cSChris Fraire         }
2058df660a0SVladimir Kotal     }
206e829566cSChris Fraire 
bounceHistoryRenamedExecutor()2078df660a0SVladimir Kotal     private void bounceHistoryRenamedExecutor() {
208dc2490c1SVladimir Kotal         if (lzHistoryFileExecutor.isActive()) {
209dc2490c1SVladimir Kotal             ExecutorService formerHistoryRenamedExecutor = lzHistoryFileExecutor.get();
210dc607a84SVladimir Kotal             createLazyHistoryFileExecutor();
21134eac45cSChris Fraire             formerHistoryRenamedExecutor.shutdown();
212e829566cSChris Fraire         }
2138df660a0SVladimir Kotal     }
214b124dbe6SVladimir Kotal 
bounceCtagsWatcherExecutor()2158df660a0SVladimir Kotal     private void bounceCtagsWatcherExecutor() {
21634eac45cSChris Fraire         if (lzCtagsWatcherExecutor.isActive()) {
21734eac45cSChris Fraire             ExecutorService formerCtagsWatcherExecutor = lzCtagsWatcherExecutor.get();
218b124dbe6SVladimir Kotal             createLazyCtagsWatcherExecutor();
219b124dbe6SVladimir Kotal             formerCtagsWatcherExecutor.shutdown();
220b124dbe6SVladimir Kotal         }
221e829566cSChris Fraire     }
222e829566cSChris Fraire 
bounceXrefWatcherExecutor()223e92cec67SVladimir Kotal     private void bounceXrefWatcherExecutor() {
224e92cec67SVladimir Kotal         if (lzXrefWatcherExecutor.isActive()) {
225e92cec67SVladimir Kotal             ExecutorService formerXrefWatcherExecutor = lzXrefWatcherExecutor.get();
226e92cec67SVladimir Kotal             createLazyXrefWatcherExecutor();
227e92cec67SVladimir Kotal             formerXrefWatcherExecutor.shutdown();
228e92cec67SVladimir Kotal         }
229e92cec67SVladimir Kotal     }
230e92cec67SVladimir Kotal 
createLazyForkJoinPool()231e829566cSChris Fraire     private void createLazyForkJoinPool() {
232e829566cSChris Fraire         lzForkJoinPool = LazilyInstantiate.using(() ->
233e829566cSChris Fraire                 new ForkJoinPool(indexingParallelism));
234e829566cSChris Fraire     }
235e829566cSChris Fraire 
createLazyCtagsPool()236e829566cSChris Fraire     private void createLazyCtagsPool() {
237e829566cSChris Fraire         lzCtagsPool = LazilyInstantiate.using(() ->
238e829566cSChris Fraire                 new BoundedBlockingObjectPool<>(indexingParallelism,
2392d875ddeSChris Fraire                         new CtagsValidator(), new CtagsObjectFactory()));
240e829566cSChris Fraire     }
241e829566cSChris Fraire 
createLazyCtagsWatcherExecutor()242b124dbe6SVladimir Kotal     private void createLazyCtagsWatcherExecutor() {
243b124dbe6SVladimir Kotal         lzCtagsWatcherExecutor = LazilyInstantiate.using(() ->
244b124dbe6SVladimir Kotal                 new ScheduledThreadPoolExecutor(1, runnable -> {
245b124dbe6SVladimir Kotal                     Thread thread = Executors.defaultThreadFactory().newThread(runnable);
246b124dbe6SVladimir Kotal                     thread.setName("ctags-watcher-" + thread.getId());
247b124dbe6SVladimir Kotal                     return thread;
248b124dbe6SVladimir Kotal                 }));
249b124dbe6SVladimir Kotal     }
250b124dbe6SVladimir Kotal 
createLazyXrefWatcherExecutor()251e92cec67SVladimir Kotal     private void createLazyXrefWatcherExecutor() {
252e92cec67SVladimir Kotal         lzXrefWatcherExecutor = LazilyInstantiate.using(() ->
253e92cec67SVladimir Kotal                 new ScheduledThreadPoolExecutor(1, runnable -> {
254e92cec67SVladimir Kotal                     Thread thread = Executors.defaultThreadFactory().newThread(runnable);
255e92cec67SVladimir Kotal                     thread.setName("xref-watcher-" + thread.getId());
256e92cec67SVladimir Kotal                     return thread;
257e92cec67SVladimir Kotal                 }));
258e92cec67SVladimir Kotal     }
259e92cec67SVladimir Kotal 
createLazyFixedExecutor()260e829566cSChris Fraire     private void createLazyFixedExecutor() {
261e829566cSChris Fraire         lzFixedExecutor = LazilyInstantiate.using(() ->
262e829566cSChris Fraire                 Executors.newFixedThreadPool(indexingParallelism));
263e829566cSChris Fraire     }
264e829566cSChris Fraire 
createLazyHistoryExecutor()265e829566cSChris Fraire     private void createLazyHistoryExecutor() {
266e829566cSChris Fraire         lzHistoryExecutor = LazilyInstantiate.using(() ->
267*b93c4894SVladimir Kotal                 Executors.newFixedThreadPool(env.getHistoryParallelism(), runnable -> {
268*b93c4894SVladimir Kotal                         Thread thread = Executors.defaultThreadFactory().newThread(runnable);
269*b93c4894SVladimir Kotal                         thread.setName("history-" + thread.getId());
270*b93c4894SVladimir Kotal                         return thread;
271*b93c4894SVladimir Kotal                 }));
272e829566cSChris Fraire     }
273e829566cSChris Fraire 
createLazyHistoryFileExecutor()274dc607a84SVladimir Kotal     private void createLazyHistoryFileExecutor() {
275dc2490c1SVladimir Kotal         lzHistoryFileExecutor = LazilyInstantiate.using(() ->
276*b93c4894SVladimir Kotal                 Executors.newFixedThreadPool(env.getHistoryFileParallelism(), runnable -> {
277*b93c4894SVladimir Kotal                     Thread thread = Executors.defaultThreadFactory().newThread(runnable);
278*b93c4894SVladimir Kotal                     thread.setName("history-file-" + thread.getId());
279*b93c4894SVladimir Kotal                     return thread;
280*b93c4894SVladimir Kotal                 }));
281b5840353SAdam Hornáček     }
282b5840353SAdam Hornáček 
28377feb578SChris Fraire     private class CtagsObjectFactory implements ObjectFactory<Ctags> {
284b5840353SAdam Hornáček 
createNew()285b5840353SAdam Hornáček         public Ctags createNew() {
28677feb578SChris Fraire             return CtagsUtil.newInstance(env);
287b5840353SAdam Hornáček         }
288b5840353SAdam Hornáček     }
289b5840353SAdam Hornáček }
290