Migrated concurrency module to Kotlin

This commit is contained in:
Saifuddin 2025-01-07 17:42:45 +05:30
parent b724216de6
commit 652e5f544b
4 changed files with 119 additions and 122 deletions

View file

@ -1,23 +1,21 @@
package fr.free.nrw.commons.concurrency; package fr.free.nrw.commons.concurrency
import androidx.annotation.NonNull; import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.BuildConfig;
public class BackgroundPoolExceptionHandler implements ExceptionHandler { class BackgroundPoolExceptionHandler : ExceptionHandler {
/** /**
* If an exception occurs on a background thread, this handler will crash for debug builds * If an exception occurs on a background thread, this handler will crash for debug builds
* but fail silently for release builds. * but fail silently for release builds.
* @param t * @param t
*/ */
@Override override fun onException(t: Throwable) {
public void onException(@NonNull final Throwable t) { // Crash for debug build
//Crash for debug build
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Thread thread = new Thread(() -> { val thread = Thread {
throw new RuntimeException(t); throw RuntimeException(t)
}); }
thread.start(); thread.start()
} }
} }
} }

View file

@ -1,39 +1,40 @@
package fr.free.nrw.commons.concurrency; package fr.free.nrw.commons.concurrency
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException
import java.util.concurrent.Future; import java.util.concurrent.Future
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory
class ExceptionAwareThreadPoolExecutor extends ScheduledThreadPoolExecutor {
private final ExceptionHandler exceptionHandler; class ExceptionAwareThreadPoolExecutor(
corePoolSize: Int,
threadFactory: ThreadFactory,
private val exceptionHandler: ExceptionHandler?
) : ScheduledThreadPoolExecutor(corePoolSize, threadFactory) {
public ExceptionAwareThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, override fun afterExecute(r: Runnable, t: Throwable?) {
ExceptionHandler exceptionHandler) { super.afterExecute(r, t)
super(corePoolSize, threadFactory); var throwable = t
this.exceptionHandler = exceptionHandler;
}
@Override if (throwable == null && r is Future<*>) {
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try { try {
Future<?> future = (Future<?>) r; if (r.isDone) {
if (future.isDone()) future.get(); r.get()
} catch (CancellationException | InterruptedException e) { }
//ignore } catch (e: CancellationException) {
} catch (ExecutionException e) { // ignore
t = e.getCause() != null ? e.getCause() : e; } catch (e: InterruptedException) {
} catch (Exception e) { // ignore
t = e; } catch (e: ExecutionException) {
throwable = e.cause ?: e
} catch (e: Exception) {
throwable = e
} }
} }
if (t != null) { throwable?.let {
exceptionHandler.onException(t); exceptionHandler?.onException(it)
} }
} }
} }

View file

@ -1,7 +1,7 @@
package fr.free.nrw.commons.concurrency; package fr.free.nrw.commons.concurrency
import androidx.annotation.NonNull; interface ExceptionHandler {
public interface ExceptionHandler { fun onException(t: Throwable)
void onException(@NonNull Throwable t);
} }

View file

@ -1,13 +1,12 @@
package fr.free.nrw.commons.concurrency; package fr.free.nrw.commons.concurrency
import androidx.annotation.NonNull; import java.util.concurrent.Callable
import java.util.concurrent.Executor
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.ThreadFactory
import java.util.concurrent.TimeUnit
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/** /**
* This class is a thread pool which provides some additional features: * This class is a thread pool which provides some additional features:
@ -15,110 +14,109 @@ import java.util.concurrent.TimeUnit;
* supply your own priority * supply your own priority
* - it gives you a way to handle exceptions thrown in the thread pool * - it gives you a way to handle exceptions thrown in the thread pool
*/ */
class ThreadPoolService private constructor(builder: Builder) : Executor {
private val backgroundPool: ScheduledThreadPoolExecutor = ExceptionAwareThreadPoolExecutor(
builder.poolSize,
object : ThreadFactory {
private var count = 0
override fun newThread(r: Runnable): Thread {
count++
val t = Thread(r, "${builder.name}-$count")
// If the priority is specified out of range, we set the thread priority to
// Thread.MIN_PRIORITY
// It's done to prevent IllegalArgumentException and to prevent setting of
// improper high priority for a less priority task
t.priority =
if (
builder.priority > Thread.MAX_PRIORITY
||
builder.priority < Thread.MIN_PRIORITY
) {
Thread.MIN_PRIORITY
} else {
builder.priority
}
return t
}
},
builder.exceptionHandler
)
public class ThreadPoolService implements Executor { fun <V> schedule(callable: Callable<V>, time: Long, timeUnit: TimeUnit): ScheduledFuture<V> {
private final ScheduledThreadPoolExecutor backgroundPool; return backgroundPool.schedule(callable, time, timeUnit)
private ThreadPoolService(final Builder b) {
backgroundPool = new ExceptionAwareThreadPoolExecutor(b.poolSize,
new ThreadFactory() {
int count = 0;
@Override
public Thread newThread(@NonNull Runnable r) {
count++;
Thread t = new Thread(r, String.format("%s-%s", b.name, count));
//If the priority is specified out of range, we set the thread priority to Thread.MIN_PRIORITY
//It's done prevent IllegalArgumentException and to prevent setting of improper high priority for a less priority task
t.setPriority(b.priority > Thread.MAX_PRIORITY || b.priority < Thread.MIN_PRIORITY ?
Thread.MIN_PRIORITY : b.priority);
return t;
}
}, b.exceptionHandler);
} }
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long time, TimeUnit timeUnit) { fun schedule(runnable: Runnable): ScheduledFuture<*> {
return backgroundPool.schedule(callable, time, timeUnit); return schedule(runnable, 0, TimeUnit.SECONDS)
} }
public ScheduledFuture<?> schedule(Runnable runnable) { fun schedule(runnable: Runnable, time: Long, timeUnit: TimeUnit): ScheduledFuture<*> {
return schedule(runnable, 0, TimeUnit.SECONDS); return backgroundPool.schedule(runnable, time, timeUnit)
} }
public ScheduledFuture<?> schedule(Runnable runnable, long time, TimeUnit timeUnit) { fun scheduleAtFixedRate(
return backgroundPool.schedule(runnable, time, timeUnit); task: Runnable,
initialDelay: Long,
period: Long,
timeUnit: TimeUnit
): ScheduledFuture<*> {
return backgroundPool.scheduleWithFixedDelay(task, initialDelay, period, timeUnit)
} }
public ScheduledFuture<?> scheduleAtFixedRate(final Runnable task, long initialDelay, fun executor(): ScheduledThreadPoolExecutor {
long period, final TimeUnit timeUnit) { return backgroundPool
return backgroundPool.scheduleAtFixedRate(task, initialDelay, period, timeUnit);
} }
public ScheduledThreadPoolExecutor executor() { fun shutdown() {
return backgroundPool; backgroundPool.shutdown()
} }
public void shutdown(){ override fun execute(command: Runnable) {
backgroundPool.shutdown(); backgroundPool.execute(command)
}
@Override
public void execute(Runnable command) {
backgroundPool.execute(command);
} }
/** /**
* Builder class for {@link ThreadPoolService} * Builder class for [ThreadPoolService]
*/ */
public static class Builder { class Builder(val name: String) {
//Required var poolSize: Int = 1
private final String name; var priority: Int = Thread.MIN_PRIORITY
var exceptionHandler: ExceptionHandler? = null
//Optional
private int poolSize = 1;
private int priority = Thread.MIN_PRIORITY;
private ExceptionHandler exceptionHandler = null;
/**
* @param name the name of the threads in the service. if there are N threads,
* the thread names will be like name-1, name-2, name-3,...,name-N
*/
public Builder(@NonNull String name) {
this.name = name;
}
/** /**
* @param poolSize the number of threads to keep in the pool * @param poolSize the number of threads to keep in the pool
* @throws IllegalArgumentException if size of pool <=0 * @throws IllegalArgumentException if size of pool <= 0
*/ */
public Builder setPoolSize(int poolSize) throws IllegalArgumentException { fun setPoolSize(poolSize: Int): Builder {
if (poolSize <= 0) { if (poolSize <= 0) {
throw new IllegalArgumentException("Pool size must be grater than 0"); throw IllegalArgumentException("Pool size must be greater than 0")
} }
this.poolSize = poolSize; this.poolSize = poolSize
return this; return this
} }
/** /**
* @param priority Priority of the threads in the service. You can supply a constant from * @param priority Priority of the threads in the service. You can supply a constant from
* {@link java.lang.Thread} or * [java.lang.Thread] or
* specify your own priority in the range 1(MIN_PRIORITY) to 10(MAX_PRIORITY) * specify your own priority in the range 1(MIN_PRIORITY)
* By default, the priority is set to {@link java.lang.Thread#MIN_PRIORITY} * to 10(MAX_PRIORITY)
* By default, the priority is set to [java.lang.Thread.MIN_PRIORITY]
*/ */
public Builder setPriority(int priority) { fun setPriority(priority: Int): Builder {
this.priority = priority; this.priority = priority
return this; return this
} }
/** /**
* @param handler The handler to use to handle exceptions in the service * @param handler The handler to use to handle exceptions in the service
*/ */
public Builder setExceptionHandler(ExceptionHandler handler) { fun setExceptionHandler(handler: ExceptionHandler): Builder {
this.exceptionHandler = handler; exceptionHandler = handler
return this; return this
} }
public ThreadPoolService build() { fun build(): ThreadPoolService {
return new ThreadPoolService(this); return ThreadPoolService(this)
} }
} }
} }