/** * OpenAtlasForAndroid Project * The MIT License (MIT) Copyright (OpenAtlasForAndroid) 2015 Bunny Blue,achellies * <p> * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following conditions: * <p> * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * <p> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * @author BunnyBlue **/ package com.openatlas.android.task; import android.os.AsyncTask; import android.os.Build.VERSION; import java.io.File; import java.io.FileFilter; import java.lang.Thread.State; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Iterator; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; import java.util.regex.Pattern; public class SaturativeExecutor extends ThreadPoolExecutor { private static final boolean DEBUG = false; static final Pattern PATTERN_CPU_ENTRIES; static final String TAG = "SatuExec"; private static SaturationAwareBlockingQueue<Runnable> mQueue; private static final HashSet<Thread> mThreads; private static final ThreadFactory sThreadFactory; protected static class CountedTask implements Runnable { static final AtomicInteger mNumRunning; Runnable mRunnable; public CountedTask(Runnable runnable) { this.mRunnable = runnable; } @Override public void run() { mNumRunning.incrementAndGet(); try { this.mRunnable.run(); } finally { mNumRunning.decrementAndGet(); } } static { mNumRunning = new AtomicInteger(); } } protected static class SaturationAwareBlockingQueue<T> extends LinkedBlockingQueue<T> { private static final long serialVersionUID = 1; private SaturativeExecutor mExecutor; public SaturationAwareBlockingQueue(int i) { super(i); } void setExecutor(SaturativeExecutor saturativeExecutor) { this.mExecutor = saturativeExecutor; } @Override public boolean add(T t) { if (!this.mExecutor.isReallyUnsaturated()) { return super.add(t); } throw new IllegalStateException("Unsaturated"); } @Override public boolean offer(T t) { return this.mExecutor.isReallyUnsaturated() ? DEBUG : super .offer(t); } @Override public void put(T t) { throw new UnsupportedOperationException(); } @Override public boolean offer(T t, long j, TimeUnit timeUnit) { throw new UnsupportedOperationException(); } } static { PATTERN_CPU_ENTRIES = Pattern.compile("cpu[0-9]+"); sThreadFactory = new ThreadFactory() { private final AtomicInteger a = new AtomicInteger(1); @Override public Thread newThread(Runnable runnable) { Thread thread = new Thread(runnable, "SaturativeThread #" + this.a.getAndIncrement()); SaturativeExecutor.collectThread(thread); return thread; } }; mThreads = new HashSet<Thread>(); } @Override public void execute(Runnable runnable) { super.execute(new CountedTask(runnable)); } public static final boolean installAsDefaultAsyncTaskExecutor( ThreadPoolExecutor threadPoolExecutor) { if (VERSION.SDK_INT >= 11) { try { Field declaredField = AsyncTask.class .getDeclaredField("THREAD_POOL_EXECUTOR"); declaredField.setAccessible(true); declaredField.set(null, threadPoolExecutor); } catch (Exception e) { } } try { Method method = AsyncTask.class.getMethod("setDefaultExecutor", Executor.class); method.setAccessible(true); method.invoke(null, threadPoolExecutor); return true; } catch (Exception e2) { Field declaredField; try { declaredField = AsyncTask.class .getDeclaredField("sDefaultExecutor"); declaredField.setAccessible(true); declaredField.set(null, threadPoolExecutor); return true; } catch (Exception e3) { try { declaredField = AsyncTask.class .getDeclaredField("sExecutor"); declaredField.setAccessible(true); declaredField.set(null, threadPoolExecutor); return true; } catch (Exception e4) { return false; } } } } public SaturativeExecutor() { this(determineBestMinPoolSize()); } public SaturativeExecutor(int corePoolSize) { super(corePoolSize, 128, 1, TimeUnit.SECONDS, new SaturationAwareBlockingQueue( 1024), sThreadFactory, new CallerRunsPolicy()); // BlockingQueue saturationAwareBlockingQueue = new // SaturationAwareBlockingQueue(1024); // mQueue = saturationAwareBlockingQueue; mQueue = (SaturationAwareBlockingQueue<Runnable>) getQueue(); ((SaturationAwareBlockingQueue<?>) getQueue()).setExecutor(this); } protected boolean isReallyUnsaturated() { if (isSaturated()) { return DEBUG; } LockSupport.parkNanos(10); return !isSaturated() ? true : DEBUG; } protected boolean isSaturated() { if (getPoolSize() <= 3) { return DEBUG; } int corePoolSize = getCorePoolSize(); int i = CountedTask.mNumRunning.get(); int size = mThreads.size(); if (i < corePoolSize || i < size) { return true; } boolean z; synchronized (mThreads) { Iterator<Thread> it = mThreads.iterator(); size = 0; while (it.hasNext()) { State state = it.next().getState(); if (state == State.RUNNABLE || state == State.NEW) { i = size + 1; } else { if (state == State.TERMINATED) { it.remove(); } i = size; } size = i; } } z = size >= corePoolSize; return z; } public static void collectThread(Thread thread) { synchronized (mThreads) { mThreads.add(thread); } } private static int determineBestMinPoolSize() { int countCpuCores = countCpuCores(); return countCpuCores > 0 ? countCpuCores : Runtime.getRuntime() .availableProcessors() * 2; } private static int countCpuCores() { try { return new File("/sys/devices/system/cpu/").listFiles(new FileFilter() { @Override public boolean accept(File file) { return SaturativeExecutor.PATTERN_CPU_ENTRIES.matcher(file.getName()) .matches(); } }).length; } catch (Exception e) { return 0; } } }