001/*-
002 * #%L
003 * HAPI FHIR Storage api
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.util;
021
022import ca.uhn.fhir.jpa.search.reindex.BlockPolicy;
023import jakarta.annotation.Nonnull;
024import org.apache.commons.lang3.Validate;
025import org.springframework.core.task.TaskDecorator;
026import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
027
028import java.util.concurrent.RejectedExecutionHandler;
029
030public final class ThreadPoolUtil {
031        private ThreadPoolUtil() {}
032
033        /**
034         * Creates a fixed-size thread pool with {@literal thePoolSize} threads
035         * and an unlimited-length work queue.
036         *
037         * @param thePoolSize The number of threads in the pool
038         * @param theThreadNamePrefix Threads in the pool will be named with this prefix followed by a dash and a number
039         *
040         * @since 8.4.0
041         */
042        @Nonnull
043        public static ThreadPoolTaskExecutor newThreadPool(int thePoolSize, String theThreadNamePrefix) {
044                return newThreadPool(thePoolSize, thePoolSize, theThreadNamePrefix, 0);
045        }
046
047        @Nonnull
048        public static ThreadPoolTaskExecutor newThreadPool(
049                        int theCorePoolSize, int theMaxPoolSize, String theThreadNamePrefix) {
050                return newThreadPool(theCorePoolSize, theMaxPoolSize, theThreadNamePrefix, 0);
051        }
052
053        @Nonnull
054        public static ThreadPoolTaskExecutor newThreadPool(
055                        int theCorePoolSize, int theMaxPoolSize, String theThreadNamePrefix, int theQueueCapacity) {
056                return newThreadPool(
057                                theCorePoolSize, theMaxPoolSize, theThreadNamePrefix, theQueueCapacity, null, new BlockPolicy());
058        }
059
060        @Nonnull
061        public static ThreadPoolTaskExecutor newThreadPool(
062                        int theCorePoolSize,
063                        int theMaxPoolSize,
064                        String theThreadNamePrefix,
065                        int theQueueCapacity,
066                        RejectedExecutionHandler theRejectedExecutionHandler) {
067                return newThreadPool(
068                                theCorePoolSize,
069                                theMaxPoolSize,
070                                theThreadNamePrefix,
071                                theQueueCapacity,
072                                null,
073                                theRejectedExecutionHandler);
074        }
075
076        @Nonnull
077        public static ThreadPoolTaskExecutor newThreadPool(
078                        int theCorePoolSize,
079                        int theMaxPoolSize,
080                        String theThreadNamePrefix,
081                        int theQueueCapacity,
082                        TaskDecorator taskDecorator,
083                        RejectedExecutionHandler theRejectedExecutionHandler) {
084                Validate.isTrue(
085                                theCorePoolSize == theMaxPoolSize || theQueueCapacity == 0,
086                                "If the queue capacity is greater than 0, core pool size needs to match max pool size or the system won't grow the queue");
087                Validate.notBlank(theThreadNamePrefix, "Thread name prefix must not be blank");
088                String threadNamePrefix = theThreadNamePrefix;
089                if (!threadNamePrefix.endsWith("-")) {
090                        threadNamePrefix = threadNamePrefix + "-";
091                }
092                ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
093                asyncTaskExecutor.setCorePoolSize(theCorePoolSize);
094                asyncTaskExecutor.setMaxPoolSize(theMaxPoolSize);
095                asyncTaskExecutor.setQueueCapacity(theQueueCapacity);
096                asyncTaskExecutor.setAllowCoreThreadTimeOut(true);
097                asyncTaskExecutor.setThreadNamePrefix(threadNamePrefix);
098                asyncTaskExecutor.setRejectedExecutionHandler(theRejectedExecutionHandler);
099                asyncTaskExecutor.setTaskDecorator(taskDecorator);
100                asyncTaskExecutor.initialize();
101                return asyncTaskExecutor;
102        }
103}