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