1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.eclipse.aether.util.concurrency; 20 21 import java.util.concurrent.Executors; 22 23 import org.eclipse.aether.RepositorySystemSession; 24 25 import static java.util.Objects.requireNonNull; 26 27 /** 28 * Utilities for executors and sizing them. 29 * <em>Big fat note:</em> Do not use this class outside of resolver. This and related classes are not meant as "drop 30 * in replacement" for Jave Executors, is used in very controlled fashion only. 31 * 32 * @since 2.0.11 33 */ 34 public final class SmartExecutorUtils { 35 private static final SmartExecutor DIRECT = new SmartExecutor.Direct(); 36 37 private SmartExecutorUtils() {} 38 39 /** 40 * Returns a smart executor for given parameters. If {@code tasks} is known (non-null), it must be greater than 0. 41 * The {@code maxConcurrentTasks} also must be greater than 0. The {@code namePrefix} must be non-null. 42 * <p> 43 * If {@code tasks} is set (is known), and equals to 1 (one), or {@code maxConcurrentTasks} equals to 1, the 44 * {@link #DIRECT} executor is returned, otherwise pooled one. 45 * <p> 46 * If @code tasks} is not set (is null), pooled one is returned with pool size of {@code maxConcurrentTasks}. In 47 * this case caller is advised to <em>reuse created executor across session</em>. 48 * <p> 49 * The returned instance must be closed out, ideally in try-with-resources construct. Returned instances when 50 * {@code tasks} parameter is given should not be cached (like in a session) as they may return {@link #DIRECT} 51 * executor for one call and a pool for subsequent call, based on value of tasks. 52 * 53 * @param tasks The amount of tasks, if known, {@code null} otherwise. 54 * @param maxConcurrentTasks The maximum concurrency caller wants. 55 * @param namePrefix The thread name prefixes, must not be {@code null).} 56 */ 57 public static SmartExecutor newSmartExecutor(Integer tasks, int maxConcurrentTasks, String namePrefix) { 58 if (maxConcurrentTasks < 1) { 59 throw new IllegalArgumentException("maxConcurrentTasks must be > 0"); 60 } 61 requireNonNull(namePrefix); 62 int poolSize; 63 if (tasks != null) { 64 if (tasks < 1) { 65 throw new IllegalArgumentException("tasks must be > 0"); 66 } 67 if (tasks == 1 || maxConcurrentTasks == 1) { 68 return DIRECT; 69 } 70 poolSize = Math.min(tasks, maxConcurrentTasks); 71 } else { 72 if (maxConcurrentTasks == 1) { 73 return DIRECT; 74 } 75 poolSize = maxConcurrentTasks; 76 } 77 return new SmartExecutor.Pooled(Executors.newFixedThreadPool(poolSize, new WorkerThreadFactory(namePrefix))); 78 } 79 80 /** 81 * Returns a smart executor, bound to session if tasks to execute are not known ahead of time. The returned 82 * instance should be handled transparently, so preferably in try-with-resource even if underlying executor is 83 * probably tied to session lifecycle, if applicable. 84 * <p> 85 * Implementation note: by this change, the caller "concurrency" is made deterministic and global(!). If you consider 86 * collector example, it is called from project builder that in Maven 4 is already multithreaded, and before this 87 * change the actual threads doing IO (HTTP) was {@code callerThreadCount x maxConcurrentTask} per JVM/Maven process. 88 * Now, the {@code maxConcurrentTask} becomes global limit, and hence can be upped without unexpected "explosion" 89 * in increasing build threading or anything. 90 */ 91 public static SmartExecutor smartExecutor( 92 RepositorySystemSession session, Integer tasks, int maxConcurrentTasks, String namePrefix) { 93 if (tasks == null && maxConcurrentTasks > 1) { 94 return (SmartExecutor) 95 session.getData().computeIfAbsent(SmartExecutor.class.getSimpleName() + "-" + namePrefix, () -> { 96 SmartExecutor smartExecutor = newSmartExecutor(null, maxConcurrentTasks, namePrefix); 97 session.addOnSessionEndedHandler(smartExecutor::close); 98 return new SmartExecutor.NonClosing(smartExecutor); 99 }); 100 } else { 101 return newSmartExecutor(tasks, maxConcurrentTasks, namePrefix); 102 } 103 } 104 }