Skip to content

Commit

Permalink
[Performance] Optimize get performance by stealing provider job while…
Browse files Browse the repository at this point in the history
… waiting for result
  • Loading branch information
rh-id committed Dec 4, 2021
1 parent 3b8a691 commit 6fb5e22
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public <I> void register(Class<I> clazz, I implementation) {

@Override
public <I> void registerLazy(Class<I> clazz, ProviderValue<I> providerValue) {
register(new LazySingletonProviderRegister<>(clazz, providerValue));
register(new LazySingletonProviderRegister<>(clazz, providerValue, mThreadPoolExecutor));
}

@Override
Expand All @@ -179,7 +179,7 @@ public <I> void registerAsync(Class<I> clazz, ProviderValue<I> providerValue) {

@Override
public <I> void registerFactory(Class<I> clazz, ProviderValue<I> providerValue) {
register(new FactoryProviderRegister<>(clazz, providerValue, mContext));
register(new FactoryProviderRegister<>(clazz, providerValue, mContext, mThreadPoolExecutor));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,56 @@
import android.content.Context;
import android.util.Log;

import java.util.concurrent.ThreadPoolExecutor;

/**
* Helper class to register factory
*/
class FactoryProviderRegister<I> extends ProviderRegister<I> implements ProviderDisposable {
private static final String TAG = "FactoryProvider";

private Context mContext;
private SyncWorkStealingWorker mSyncWorkStealingWorker;
private I mPreviousValue;

public FactoryProviderRegister(Class<I> type, ProviderValue<I> providerValue, Context context) {
public FactoryProviderRegister(Class<I> type, ProviderValue<I> providerValue, Context context, ThreadPoolExecutor threadPoolExecutor) {
super(type, providerValue);
mContext = context;
mSyncWorkStealingWorker = new SyncWorkStealingWorker(threadPoolExecutor);
}

@Override
public synchronized I get() {
if (mPreviousValue != null) {
if (mPreviousValue instanceof ProviderDisposable) {
try {
((ProviderDisposable) mPreviousValue).dispose(mContext);
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
public I get() {
return mSyncWorkStealingWorker.submit(() -> {
if (mPreviousValue != null) {
if (mPreviousValue instanceof ProviderDisposable) {
try {
((ProviderDisposable) mPreviousValue).dispose(mContext);
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
}
}
}
}
mPreviousValue = getProviderValue().get();
return mPreviousValue;
mPreviousValue = getProviderValue().get();
return mPreviousValue;
});
}

@Override
public synchronized void dispose(Context context) {
if (mPreviousValue != null) {
if (mPreviousValue instanceof ProviderDisposable) {
try {
((ProviderDisposable) mPreviousValue).dispose(mContext);
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
public void dispose(Context context) {
mSyncWorkStealingWorker.execute(() -> {
if (mPreviousValue != null) {
if (mPreviousValue instanceof ProviderDisposable) {
try {
((ProviderDisposable) mPreviousValue).dispose(mContext);
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
}
}
}
}
mPreviousValue = null;
mContext = null;
mPreviousValue = null;
mContext = null;
mSyncWorkStealingWorker = null;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,58 @@ class LazyFutureProviderRegister<I> extends ProviderRegister<I> implements Provi
private static final String TAG = "FutureProvider";
private Future<I> mFutureValue;
private ThreadPoolExecutor mThreadPoolExecutor;
private SyncWorkStealingWorker mSyncWorkStealingWorker;

public LazyFutureProviderRegister(Class<I> type, ProviderValue<I> providerValue, ThreadPoolExecutor executorService) {
super(type, providerValue);
mThreadPoolExecutor = executorService;
mSyncWorkStealingWorker = new SyncWorkStealingWorker(mThreadPoolExecutor);
}

@Override
public synchronized I get() {
startLoad();
try {
while (!mFutureValue.isDone()) {
// try to run next task to avoid deadlock due to limited thread size
Runnable nextTask = mThreadPoolExecutor.getQueue().poll();
if (nextTask != null) {
nextTask.run();
public I get() {
return mSyncWorkStealingWorker.submit(() -> {
startLoad();
try {
while (!mFutureValue.isDone()) {
// try to run next task to avoid deadlock due to limited thread size
Runnable nextTask = mThreadPoolExecutor.getQueue().poll();
if (nextTask != null) {
nextTask.run();
}
}
return mFutureValue.get();
} catch (Exception e) {
Log.e(TAG, getType().getName() + " throws exception with message: " + e.getMessage());
throw new RuntimeException(e);
}
return mFutureValue.get();
} catch (Exception e) {
Log.e(TAG, getType().getName() + " throws exception with message: " + e.getMessage());
throw new RuntimeException(e);
}
});
}

public synchronized void startLoad() {
if (mFutureValue == null) {
mFutureValue = mThreadPoolExecutor.submit(() -> getProviderValue().get());
}
public void startLoad() {
mSyncWorkStealingWorker.execute(() -> {
if (mFutureValue == null) {
mFutureValue = mThreadPoolExecutor.submit(() -> getProviderValue().get());
}
});
}

@Override
public synchronized void dispose(Context context) {
if (mFutureValue != null) {
try {
I i = mFutureValue.get();
if (i instanceof ProviderDisposable) {
((ProviderDisposable) i).dispose(context);
public void dispose(Context context) {
mSyncWorkStealingWorker.execute(() -> {
if (mFutureValue != null) {
try {
I i = mFutureValue.get();
if (i instanceof ProviderDisposable) {
((ProviderDisposable) i).dispose(context);
}
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
}
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
}
}
mFutureValue = null;
mThreadPoolExecutor = null;
mFutureValue = null;
mThreadPoolExecutor = null;
mSyncWorkStealingWorker = null;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,45 @@
import android.content.Context;
import android.util.Log;

import java.util.concurrent.ThreadPoolExecutor;

/**
* Helper class to register lazy-loaded singleton to the provider
*/
class LazySingletonProviderRegister<I> extends ProviderRegister<I> implements ProviderDisposable {
private static final String TAG = "SingletonProvider";

private SyncWorkStealingWorker mSyncWorkStealingWorker;
private I mValue;

public LazySingletonProviderRegister(Class<I> type, ProviderValue<I> providerValue) {
public LazySingletonProviderRegister(Class<I> type, ProviderValue<I> providerValue, ThreadPoolExecutor threadPoolExecutor) {
super(type, providerValue);
mSyncWorkStealingWorker = new SyncWorkStealingWorker(threadPoolExecutor);
}

@Override
public synchronized I get() {
if (mValue == null) {
mValue = getProviderValue().get();
}
return mValue;
public I get() {
return mSyncWorkStealingWorker.submit(() -> {
if (mValue == null) {
mValue = getProviderValue().get();
}
return mValue;
});
}

@Override
public synchronized void dispose(Context context) {
if (mValue != null) {
if (mValue instanceof ProviderDisposable) {
try {
((ProviderDisposable) mValue).dispose(context);
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
public void dispose(Context context) {
mSyncWorkStealingWorker.execute(() -> {
if (mValue != null) {
if (mValue instanceof ProviderDisposable) {
try {
((ProviderDisposable) mValue).dispose(context);
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
}
}
}
}
mValue = null;
mValue = null;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,52 @@
import android.util.Log;

import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;

/**
* Helper class to register pool
*/
class PoolProviderRegister<I> extends ProviderRegister<I> implements ProviderDisposable {
private static final String TAG = "PoolProvider";

private ExecutorService mExecutorService;
private ThreadPoolExecutor mThreadPool;
private LinkedList<I> mPreviousValues;
private SyncWorkStealingWorker mSyncWorkStealingWorker;

public PoolProviderRegister(Class<I> type, ProviderValue<I> providerValue, ExecutorService executorService) {
public PoolProviderRegister(Class<I> type, ProviderValue<I> providerValue, ThreadPoolExecutor threadPoolExecutor) {
super(type, providerValue);
mExecutorService = executorService;
mThreadPool = threadPoolExecutor;
mPreviousValues = new LinkedList<>();
mSyncWorkStealingWorker = new SyncWorkStealingWorker(threadPoolExecutor);
}

@Override
public synchronized I get() {
I previousVal = getProviderValue().get();
mPreviousValues.add(previousVal);
return previousVal;
public I get() {
return mSyncWorkStealingWorker.submit(() -> {
I previousVal = getProviderValue().get();
mPreviousValues.add(previousVal);
return previousVal;
});
}

@Override
public synchronized void dispose(Context context) {
while (!mPreviousValues.isEmpty()) {
I prevValue = mPreviousValues.pop();
if (prevValue instanceof ProviderDisposable) {
mExecutorService.execute(() -> {
try {
((ProviderDisposable) prevValue).dispose(context);
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
}
});
public void dispose(Context context) {
mSyncWorkStealingWorker.execute(() -> {
while (!mPreviousValues.isEmpty()) {
I prevValue = mPreviousValues.pop();
if (prevValue instanceof ProviderDisposable) {
mThreadPool.execute(() -> {
try {
((ProviderDisposable) prevValue).dispose(context);
} catch (Exception e) {
Log.e(TAG, getType().getName() + " failed to dispose: " + e.getMessage());
}
});
}
}
}
mPreviousValues = null;
mExecutorService = null;
mPreviousValues = null;
mThreadPool = null;
mSyncWorkStealingWorker = null;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package m.co.rh.id.aprovider;

import android.util.Log;

import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.ReentrantLock;

/**
* Special class to synchronize execution in a lock.
* If lock is not yet acquired then it will try to steal and run task rather than waiting
*/
class SyncWorkStealingWorker {

private static final String TAG = SyncWorkStealingWorker.class.getName();
private ThreadPoolExecutor mThreadPoolExecutor;
private ReentrantLock mLock;

public SyncWorkStealingWorker(ThreadPoolExecutor threadPoolExecutor) {
mThreadPoolExecutor = threadPoolExecutor;
mLock = new ReentrantLock(true);
}

public <R> R submit(Callable<R> callable) {
tryLock();
R result;
try {
result = callable.call();
} catch (Exception e) {
Log.e(TAG, "Error executing task", e);
throw new RuntimeException(e);
} finally {
mLock.unlock();
}
return result;
}

public void execute(Runnable runnable) {
tryLock();
try {
runnable.run();
} catch (Exception e) {
Log.e(TAG, "Error executing task", e);
throw e;
} finally {
mLock.unlock();
}
}

private void tryLock() {
while (!mLock.tryLock()) {
try {
Runnable poolRunnable = mThreadPoolExecutor.getQueue().poll();
if (poolRunnable != null) {
poolRunnable.run();
}
} catch (Exception e) {
Log.e(TAG, "Error executing stolen task", e);
throw e;
}
}
}
}

0 comments on commit 6fb5e22

Please sign in to comment.