Work around buggy MyersDiff by killing threads
The JGIt MyersDiff class contains a bug that triggers an infinite loop on only certain input files. Most source code is able to be processed in a reasonable time bound, but some just steal a thread and never return to the caller. Implement a custom thread pool that is used to invoke MyersDiff for the intraline difference data. If the worker thread doesn't end within the configured time bound (default of 5 seconds), Gerrit removes the worker from the pool and tries to kill the worker with the unsafe Thread.stop() method. A custom thread pool is used to try and make Thread.stop() safe by having the amount of data accessed by each worker thread be limited to only the "constant" inputs supplied by the cache lookup request, and the result that the thread would return. If any locks are released early as a result of ThreadDeath going up the worker thread stack at worst only the incoming or outgoing queues that are private to that worker will be corrupted. Since these queues are private to the worker, and to the thread that is currently borrowing this worker from the pool (and who is also now killing it), we can safely ensure that the queues won't be touched after the Thread.stop() request is made. This wouldn't be true if we reused any of the java.util.concurrent thread pool utilities. This change doesn't actually fix the MyersDiff bug, so we're leaving issue 487 open. It does however reduce the impact by trying to abort the runaway thread, and still show the file with intraline difference support disabled on just that one file. Bug: issue 487 Change-Id: I6cbfdd0acc6f7e612a29ed789efe9da591a45273 Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -31,6 +31,7 @@ public interface PatchConstants extends Constants {
|
||||
|
||||
String patchHistoryTitle();
|
||||
String disabledOnLargeFiles();
|
||||
String intralineFailure();
|
||||
|
||||
String upToChange();
|
||||
String linePrev();
|
||||
|
||||
@@ -13,6 +13,7 @@ patchHeaderOld = Old Version
|
||||
patchHeaderNew = New Version
|
||||
patchHistoryTitle = Patch History
|
||||
disabledOnLargeFiles = Disabled on very large source files.
|
||||
intralineFailure = Intraline difference not available due to server error.
|
||||
|
||||
upToChange = Up to change
|
||||
linePrev = Previous line
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.client.patches;
|
||||
|
||||
import com.google.gerrit.client.Dispatcher;
|
||||
import com.google.gerrit.client.ErrorDialog;
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.RpcStatus;
|
||||
import com.google.gerrit.client.changes.CommitMessageBlock;
|
||||
@@ -140,6 +141,7 @@ public abstract class PatchScreen extends Screen implements
|
||||
/** Keys that cause an action on this screen */
|
||||
private KeyCommandSet keysNavigation;
|
||||
private HandlerRegistration regNavigation;
|
||||
private boolean intralineFailure;
|
||||
|
||||
/**
|
||||
* How this patch should be displayed in the patch screen.
|
||||
@@ -461,6 +463,17 @@ public abstract class PatchScreen extends Screen implements
|
||||
settingsPanel.getReviewedCheckBox().setValue(true);
|
||||
setReviewedByCurrentUser(true /* reviewed */);
|
||||
}
|
||||
|
||||
intralineFailure = isFirst && script.hasIntralineFailure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowView() {
|
||||
super.onShowView();
|
||||
if (intralineFailure) {
|
||||
intralineFailure = false;
|
||||
new ErrorDialog(PatchUtil.C.intralineFailure()).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void showPatch(final boolean showPatch) {
|
||||
|
||||
Reference in New Issue
Block a user