Repository navigation
Return last evaluated progress on lock timeout in RestrictionTrackers.getProgress #40205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,7 +49,8 @@ private static class RestrictionTrackerObserver<RestrictionT, PositionT> | |
| extends RestrictionTracker<RestrictionT, PositionT> { | ||
| protected final RestrictionTracker<RestrictionT, PositionT> delegate; | ||
| protected ReentrantLock lock = new ReentrantLock(); | ||
| protected volatile boolean hasInitialProgress = false; | ||
| protected volatile Progress lastProgress = Progress.NONE; | ||
| protected volatile boolean needsProgressUpdate = false; | ||
| private final ClaimObserver<PositionT> claimObserver; | ||
|
|
||
| protected RestrictionTrackerObserver( | ||
|
|
@@ -59,6 +60,16 @@ protected RestrictionTrackerObserver( | |
| this.claimObserver = claimObserver; | ||
| } | ||
|
|
||
| protected void updateProgressAndUnlock() { | ||
| try { | ||
| if (needsProgressUpdate) { | ||
| updateProgressBlocking(); | ||
| } | ||
| } finally { | ||
| lock.unlock(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public boolean tryClaim(PositionT position) { | ||
| lock.lock(); | ||
|
|
@@ -71,7 +82,7 @@ public boolean tryClaim(PositionT position) { | |
| return false; | ||
| } | ||
| } finally { | ||
| lock.unlock(); | ||
| updateProgressAndUnlock(); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -81,7 +92,7 @@ public RestrictionT currentRestriction() { | |
| try { | ||
| return delegate.currentRestriction(); | ||
| } finally { | ||
| lock.unlock(); | ||
| updateProgressAndUnlock(); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -90,9 +101,10 @@ public SplitResult<RestrictionT> trySplit(double fractionOfRemainder) { | |
| lock.lock(); | ||
| try { | ||
| SplitResult<RestrictionT> result = delegate.trySplit(fractionOfRemainder); | ||
| needsProgressUpdate = true; | ||
| return result; | ||
| } finally { | ||
| lock.unlock(); | ||
| updateProgressAndUnlock(); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -102,7 +114,7 @@ public void checkDone() throws IllegalStateException { | |
| try { | ||
| delegate.checkDone(); | ||
| } finally { | ||
| lock.unlock(); | ||
| updateProgressAndUnlock(); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -112,10 +124,13 @@ public IsBounded isBounded() { | |
| } | ||
|
|
||
| /** Evaluate progress if requested. */ | ||
| protected Progress getProgressBlocking() { | ||
| protected void updateProgressBlocking() { | ||
| lock.lock(); | ||
| try { | ||
| return ((HasProgress) delegate).getProgress(); | ||
| needsProgressUpdate = false; | ||
| if (delegate instanceof HasProgress) { | ||
| lastProgress = ((HasProgress) delegate).getProgress(); | ||
| } | ||
| } finally { | ||
| lock.unlock(); | ||
| } | ||
|
|
@@ -129,7 +144,7 @@ protected Progress getProgressBlocking() { | |
| @ThreadSafe | ||
| static class RestrictionTrackerObserverWithProgress<RestrictionT, PositionT> | ||
| extends RestrictionTrackerObserver<RestrictionT, PositionT> implements HasProgress { | ||
| private static final int FIRST_PROGRESS_TIMEOUT_SEC = 60; | ||
| private static final int PROGRESS_TIMEOUT_SEC = 60; | ||
|
|
||
| protected RestrictionTrackerObserverWithProgress( | ||
| RestrictionTracker<RestrictionT, PositionT> delegate, | ||
|
|
@@ -139,32 +154,29 @@ protected RestrictionTrackerObserverWithProgress( | |
|
|
||
| @Override | ||
| public Progress getProgress() { | ||
| return getProgress(FIRST_PROGRESS_TIMEOUT_SEC); | ||
| return getProgress(PROGRESS_TIMEOUT_SEC); | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
| Progress getProgress(int timeOutSec) { | ||
| if (!hasInitialProgress) { | ||
| Progress progress = Progress.NONE; | ||
| try { | ||
| // lock can be held long by long-running tryClaim/trySplit. We tolerate this scenario | ||
| // by returning zero progress when initial progress never evaluated before due to lock | ||
| // timeout. | ||
| if (lock.tryLock(timeOutSec, TimeUnit.SECONDS)) { | ||
| try { | ||
| progress = getProgressBlocking(); | ||
| hasInitialProgress = true; | ||
| } finally { | ||
| lock.unlock(); | ||
| } | ||
| try { | ||
| // lock can be held long by long-running tryClaim/trySplit. We tolerate this scenario | ||
| // by returning the last evaluated progress (or zero progress if never evaluated before) | ||
| // when lock timeout occurs. | ||
| if (lock.tryLock(timeOutSec, TimeUnit.SECONDS)) { | ||
| try { | ||
| updateProgressBlocking(); | ||
| } finally { | ||
| lock.unlock(); | ||
| } | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } else { | ||
| needsProgressUpdate = true; | ||
| } | ||
| return progress; | ||
| } else { | ||
| return getProgressBlocking(); | ||
| } catch (InterruptedException e) { | ||
| needsProgressUpdate = true; | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
| return lastProgress; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this correct if a split has occurred?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can probably just unconditionally update progress after a split to fix this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We now have mixed calls of
unlock()andlock.unlock(). Consider name the newunlock()methodupdateProgressAndUnlock()?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't do this because
updateProgressAndUnlock()implies that we're always updating progress, which we're not. But I see the downside, and I think I prefer your approach.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done