Ensure RevWalk is released when done
Update a number of calling sites of RevWalk to ensure the walker's internal ObjectReader is released after the walk is no longer used. Because the ObjectReader is likely to hold onto a native resource like an Inflater, we don't want to leak them outside of their useful scope. Where possible we also try to share ObjectReaders across several walk pools, or between a walker and a PackWriter. This permits the ObjectReader to actually do some caching if it felt inclined to do so. Not everything was updated, we'll probably need to come back and update even more call sites, but these are some of the biggest offenders. Test cases in particular aren't updated. My plan is to move most storage-agnostic tests onto some purely in-memory storage solution that doesn't do compression. Change-Id: I04087ec79faeea208b19848939898ad7172b6672 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
parent
4913ad57fc
commit
515deaf7e5
|
@ -74,13 +74,15 @@ public void doGet(final HttpServletRequest req,
|
|||
|
||||
final Repository db = getRepository(req);
|
||||
final RevWalk walk = new RevWalk(db);
|
||||
try {
|
||||
final RevFlag ADVERTISED = walk.newFlag("ADVERTISED");
|
||||
|
||||
final OutputStreamWriter out = new OutputStreamWriter(
|
||||
new SmartOutputStream(req, rsp), Constants.CHARSET);
|
||||
final RefAdvertiser adv = new RefAdvertiser() {
|
||||
@Override
|
||||
protected void writeOne(final CharSequence line) throws IOException {
|
||||
protected void writeOne(final CharSequence line)
|
||||
throws IOException {
|
||||
// Whoever decided that info/refs should use a different
|
||||
// delimiter than the native git:// protocol shouldn't
|
||||
// be allowed to design this sort of stuff. :-(
|
||||
|
@ -99,5 +101,8 @@ protected void end() {
|
|||
refs.remove(Constants.HEAD);
|
||||
adv.send(refs);
|
||||
out.close();
|
||||
} finally {
|
||||
walk.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,12 @@ static class InfoRefs extends SmartServiceInfoRefs {
|
|||
protected void advertise(HttpServletRequest req, Repository db,
|
||||
PacketLineOutRefAdvertiser pck) throws IOException,
|
||||
ServiceNotEnabledException, ServiceNotAuthorizedException {
|
||||
receivePackFactory.create(req, db).sendAdvertisedRefs(pck);
|
||||
ReceivePack rp = receivePackFactory.create(req, db);
|
||||
try {
|
||||
rp.sendAdvertisedRefs(pck);
|
||||
} finally {
|
||||
rp.getRevWalk().release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,12 @@ static class InfoRefs extends SmartServiceInfoRefs {
|
|||
protected void advertise(HttpServletRequest req, Repository db,
|
||||
PacketLineOutRefAdvertiser pck) throws IOException,
|
||||
ServiceNotEnabledException, ServiceNotAuthorizedException {
|
||||
uploadPackFactory.create(req, db).sendAdvertisedRefs(pck);
|
||||
UploadPack up = uploadPackFactory.create(req, db);
|
||||
try {
|
||||
up.sendAdvertisedRefs(pck);
|
||||
} finally {
|
||||
up.getRevWalk().release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -297,6 +297,7 @@ private Map<String, Ref> computeNewRefs() throws IOException {
|
|||
name, id));
|
||||
}
|
||||
} finally {
|
||||
rw.release();
|
||||
br.close();
|
||||
}
|
||||
return refs;
|
||||
|
|
|
@ -160,8 +160,9 @@ public RevCommit call() throws NoHeadException, NoMessageException,
|
|||
.format(commit));
|
||||
odi.flush();
|
||||
|
||||
RevCommit revCommit = new RevWalk(repo)
|
||||
.parseCommit(commitId);
|
||||
RevWalk revWalk = new RevWalk(repo);
|
||||
try {
|
||||
RevCommit revCommit = revWalk.parseCommit(commitId);
|
||||
RefUpdate ru = repo.updateRef(Constants.HEAD);
|
||||
ru.setNewObjectId(commitId);
|
||||
ru.setRefLogMessage("commit : "
|
||||
|
@ -185,13 +186,16 @@ public RevCommit call() throws NoHeadException, NoMessageException,
|
|||
}
|
||||
case REJECTED:
|
||||
case LOCK_FAILURE:
|
||||
throw new ConcurrentRefUpdateException(
|
||||
JGitText.get().couldNotLockHEAD, ru.getRef(),
|
||||
rc);
|
||||
throw new ConcurrentRefUpdateException(JGitText
|
||||
.get().couldNotLockHEAD, ru.getRef(), rc);
|
||||
default:
|
||||
throw new JGitInternalException(MessageFormat.format(
|
||||
JGitText.get().updatingRefFailed,
|
||||
Constants.HEAD, commitId.toString(), rc));
|
||||
throw new JGitInternalException(MessageFormat
|
||||
.format(JGitText.get().updatingRefFailed,
|
||||
Constants.HEAD,
|
||||
commitId.toString(), rc));
|
||||
}
|
||||
} finally {
|
||||
revWalk.release();
|
||||
}
|
||||
} finally {
|
||||
odi.release();
|
||||
|
|
|
@ -119,6 +119,7 @@ public MergeResult call() throws NoHeadException,
|
|||
|
||||
// Check for FAST_FORWARD, ALREADY_UP_TO_DATE
|
||||
RevWalk revWalk = new RevWalk(repo);
|
||||
try {
|
||||
RevCommit headCommit = revWalk.lookupCommit(head.getObjectId());
|
||||
|
||||
Ref ref = commits.get(0);
|
||||
|
@ -151,6 +152,9 @@ public MergeResult call() throws NoHeadException,
|
|||
mergeStrategy,
|
||||
JGitText.get().onlyAlreadyUpToDateAndFastForwardMergesAreAvailable);
|
||||
}
|
||||
} finally {
|
||||
revWalk.release();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new JGitInternalException(
|
||||
MessageFormat.format(
|
||||
|
|
|
@ -440,7 +440,12 @@ public Result forceUpdate() throws IOException {
|
|||
* an unexpected IO error occurred while writing changes.
|
||||
*/
|
||||
public Result update() throws IOException {
|
||||
return update(new RevWalk(getRepository()));
|
||||
RevWalk rw = new RevWalk(getRepository());
|
||||
try {
|
||||
return update(rw);
|
||||
} finally {
|
||||
rw.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -485,7 +490,12 @@ Result execute(Result status) throws IOException {
|
|||
* @throws IOException
|
||||
*/
|
||||
public Result delete() throws IOException {
|
||||
return delete(new RevWalk(getRepository()));
|
||||
RevWalk rw = new RevWalk(getRepository());
|
||||
try {
|
||||
return delete(rw);
|
||||
} finally {
|
||||
rw.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -484,9 +484,17 @@ public RefRename renameRef(final String fromRef, final String toRef) throws IOEx
|
|||
* on serious errors
|
||||
*/
|
||||
public ObjectId resolve(final String revstr) throws IOException {
|
||||
RevWalk rw = new RevWalk(this);
|
||||
try {
|
||||
return resolve(rw, revstr);
|
||||
} finally {
|
||||
rw.release();
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectId resolve(final RevWalk rw, final String revstr) throws IOException {
|
||||
char[] rev = revstr.toCharArray();
|
||||
RevObject ref = null;
|
||||
RevWalk rw = new RevWalk(this);
|
||||
for (int i = 0; i < rev.length; ++i) {
|
||||
switch (rev[i]) {
|
||||
case '^':
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jgit.JGitText;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.errors.ObjectWritingException;
|
||||
import org.eclipse.jgit.events.RefsChangedEvent;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
|
@ -418,16 +419,7 @@ public Ref peel(final Ref ref) throws IOException {
|
|||
if (leaf.isPeeled() || leaf.getObjectId() == null)
|
||||
return ref;
|
||||
|
||||
RevWalk rw = new RevWalk(getRepository());
|
||||
RevObject obj = rw.parseAny(leaf.getObjectId());
|
||||
ObjectIdRef newLeaf;
|
||||
if (obj instanceof RevTag) {
|
||||
newLeaf = new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf
|
||||
.getName(), leaf.getObjectId(), rw.peel(obj).copy());
|
||||
} else {
|
||||
newLeaf = new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf
|
||||
.getName(), leaf.getObjectId());
|
||||
}
|
||||
ObjectIdRef newLeaf = doPeel(leaf);
|
||||
|
||||
// Try to remember this peeling in the cache, so we don't have to do
|
||||
// it again in the future, but only if the reference is unchanged.
|
||||
|
@ -444,6 +436,23 @@ public Ref peel(final Ref ref) throws IOException {
|
|||
return recreate(ref, newLeaf);
|
||||
}
|
||||
|
||||
private ObjectIdRef doPeel(final Ref leaf) throws MissingObjectException,
|
||||
IOException {
|
||||
RevWalk rw = new RevWalk(getRepository());
|
||||
try {
|
||||
RevObject obj = rw.parseAny(leaf.getObjectId());
|
||||
if (obj instanceof RevTag) {
|
||||
return new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf
|
||||
.getName(), leaf.getObjectId(), rw.peel(obj).copy());
|
||||
} else {
|
||||
return new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf
|
||||
.getName(), leaf.getObjectId());
|
||||
}
|
||||
} finally {
|
||||
rw.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static Ref recreate(final Ref old, final ObjectIdRef leaf) {
|
||||
if (old.isSymbolic()) {
|
||||
Ref dst = recreate(old.getTarget(), leaf);
|
||||
|
|
|
@ -92,10 +92,10 @@ protected Result doRename() throws IOException {
|
|||
if (source.getRef().isSymbolic())
|
||||
return Result.IO_FAILURE; // not supported
|
||||
|
||||
final RevWalk rw = new RevWalk(refdb.getRepository());
|
||||
objId = source.getOldObjectId();
|
||||
updateHEAD = needToUpdateHEAD();
|
||||
tmp = refdb.newTemporaryUpdate();
|
||||
final RevWalk rw = new RevWalk(refdb.getRepository());
|
||||
try {
|
||||
// First backup the source so its never unreachable.
|
||||
tmp.setNewObjectId(objId);
|
||||
|
@ -177,6 +177,7 @@ protected Result doRename() throws IOException {
|
|||
} catch (IOException err) {
|
||||
refdb.fileFor(tmp.getName()).delete();
|
||||
}
|
||||
rw.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.CoreConfig;
|
||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||
|
@ -181,8 +182,6 @@ public class PackWriter {
|
|||
// edge objects for thin packs
|
||||
private final ObjectIdSubclassMap<ObjectId> edgeObjects = new ObjectIdSubclassMap<ObjectId>();
|
||||
|
||||
private final Repository db;
|
||||
|
||||
private final Deflater deflater;
|
||||
|
||||
private final ObjectReader reader;
|
||||
|
@ -218,19 +217,51 @@ public class PackWriter {
|
|||
* repository where objects are stored.
|
||||
*/
|
||||
public PackWriter(final Repository repo) {
|
||||
this.db = repo;
|
||||
this(repo, repo.newObjectReader());
|
||||
}
|
||||
|
||||
reader = db.newObjectReader();
|
||||
/**
|
||||
* Create a writer to load objects from the specified reader.
|
||||
* <p>
|
||||
* Objects for packing are specified in {@link #preparePack(Iterator)} or
|
||||
* {@link #preparePack(ProgressMonitor, Collection, Collection)}.
|
||||
*
|
||||
* @param reader
|
||||
* reader to read from the repository with.
|
||||
*/
|
||||
public PackWriter(final ObjectReader reader) {
|
||||
this(null, reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create writer for specified repository.
|
||||
* <p>
|
||||
* Objects for packing are specified in {@link #preparePack(Iterator)} or
|
||||
* {@link #preparePack(ProgressMonitor, Collection, Collection)}.
|
||||
*
|
||||
* @param repo
|
||||
* repository where objects are stored.
|
||||
* @param reader
|
||||
* reader to read from the repository with.
|
||||
*/
|
||||
public PackWriter(final Repository repo, final ObjectReader reader) {
|
||||
this.reader = reader;
|
||||
if (reader instanceof ObjectReuseAsIs)
|
||||
reuseSupport = ((ObjectReuseAsIs) reader);
|
||||
else
|
||||
reuseSupport = null;
|
||||
|
||||
final CoreConfig coreConfig = db.getConfig().get(CoreConfig.KEY);
|
||||
this.deflater = new Deflater(coreConfig.getCompression());
|
||||
final CoreConfig coreConfig = configOf(repo).get(CoreConfig.KEY);
|
||||
deflater = new Deflater(coreConfig.getCompression());
|
||||
outputVersion = coreConfig.getPackIndexVersion();
|
||||
}
|
||||
|
||||
private static Config configOf(final Repository repo) {
|
||||
if (repo == null)
|
||||
return new Config();
|
||||
return repo.getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether object is configured to reuse deltas existing in
|
||||
* repository.
|
||||
|
|
|
@ -270,6 +270,12 @@ protected void doFetch(final ProgressMonitor monitor,
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
walk.release();
|
||||
super.close();
|
||||
}
|
||||
|
||||
private int maxTimeWanted(final Collection<Ref> wants) {
|
||||
int maxTime = 0;
|
||||
for (final Ref r : wants) {
|
||||
|
|
|
@ -213,6 +213,7 @@ private void verifyPrerequisites() throws TransportException {
|
|||
return;
|
||||
|
||||
final RevWalk rw = new RevWalk(transport.local);
|
||||
try {
|
||||
final RevFlag PREREQ = rw.newFlag("PREREQ");
|
||||
final RevFlag SEEN = rw.newFlag("SEEN");
|
||||
|
||||
|
@ -229,12 +230,14 @@ private void verifyPrerequisites() throws TransportException {
|
|||
} catch (MissingObjectException notFound) {
|
||||
missing.put(p, e.getValue());
|
||||
} catch (IOException err) {
|
||||
throw new TransportException(transport.uri
|
||||
, MessageFormat.format(JGitText.get().cannotReadCommit, p.name()), err);
|
||||
throw new TransportException(transport.uri, MessageFormat
|
||||
.format(JGitText.get().cannotReadCommit, p.name()),
|
||||
err);
|
||||
}
|
||||
}
|
||||
if (!missing.isEmpty())
|
||||
throw new MissingBundlePrerequisiteException(transport.uri, missing);
|
||||
throw new MissingBundlePrerequisiteException(transport.uri,
|
||||
missing);
|
||||
|
||||
for (final Ref r : transport.local.getAllRefs().values()) {
|
||||
try {
|
||||
|
@ -255,7 +258,8 @@ private void verifyPrerequisites() throws TransportException {
|
|||
}
|
||||
}
|
||||
} catch (IOException err) {
|
||||
throw new TransportException(transport.uri, JGitText.get().cannotReadObject, err);
|
||||
throw new TransportException(transport.uri,
|
||||
JGitText.get().cannotReadObject, err);
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
|
@ -263,7 +267,11 @@ private void verifyPrerequisites() throws TransportException {
|
|||
if (!o.has(SEEN))
|
||||
missing.put(o, prereqs.get(o));
|
||||
}
|
||||
throw new MissingBundlePrerequisiteException(transport.uri, missing);
|
||||
throw new MissingBundlePrerequisiteException(transport.uri,
|
||||
missing);
|
||||
}
|
||||
} finally {
|
||||
rw.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -176,6 +176,7 @@ else if (tagopt == TagOpt.FETCH_TAGS)
|
|||
}
|
||||
|
||||
final RevWalk walk = new RevWalk(transport.local);
|
||||
try {
|
||||
if (transport.isRemoveDeletedRefs())
|
||||
deleteStaleTrackingRefs(result, walk);
|
||||
for (TrackingRefUpdate u : localUpdates) {
|
||||
|
@ -183,10 +184,14 @@ else if (tagopt == TagOpt.FETCH_TAGS)
|
|||
u.update(walk);
|
||||
result.add(u);
|
||||
} catch (IOException err) {
|
||||
throw new TransportException(MessageFormat.format(
|
||||
JGitText.get().failureUpdatingTrackingRef, u.getLocalName(), err.getMessage()), err);
|
||||
throw new TransportException(MessageFormat.format(JGitText
|
||||
.get().failureUpdatingTrackingRef,
|
||||
u.getLocalName(), err.getMessage()), err);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
walk.release();
|
||||
}
|
||||
|
||||
if (!fetchHeadUpdates.isEmpty()) {
|
||||
try {
|
||||
|
@ -296,11 +301,15 @@ private void updateFETCH_HEAD(final FetchResult result) throws IOException {
|
|||
private boolean askForIsComplete() throws TransportException {
|
||||
try {
|
||||
final ObjectWalk ow = new ObjectWalk(transport.local);
|
||||
try {
|
||||
for (final ObjectId want : askFor.keySet())
|
||||
ow.markStart(ow.parseAny(want));
|
||||
for (final Ref ref : transport.local.getAllRefs().values())
|
||||
ow.markUninteresting(ow.parseAny(ref.getObjectId()));
|
||||
ow.checkConnectivity();
|
||||
} finally {
|
||||
ow.release();
|
||||
}
|
||||
return true;
|
||||
} catch (MissingObjectException e) {
|
||||
return false;
|
||||
|
|
|
@ -122,12 +122,15 @@ class PushProcess {
|
|||
*/
|
||||
PushResult execute(final ProgressMonitor monitor)
|
||||
throws NotSupportedException, TransportException {
|
||||
monitor.beginTask(PROGRESS_OPENING_CONNECTION, ProgressMonitor.UNKNOWN);
|
||||
try {
|
||||
monitor.beginTask(PROGRESS_OPENING_CONNECTION,
|
||||
ProgressMonitor.UNKNOWN);
|
||||
|
||||
final PushResult res = new PushResult();
|
||||
connection = transport.openPush();
|
||||
try {
|
||||
res.setAdvertisedRefs(transport.getURI(), connection.getRefsMap());
|
||||
res.setAdvertisedRefs(transport.getURI(), connection
|
||||
.getRefsMap());
|
||||
res.setRemoteUpdates(toPush);
|
||||
monitor.endTask();
|
||||
|
||||
|
@ -148,6 +151,9 @@ else if (!preprocessed.isEmpty())
|
|||
res.add(tru);
|
||||
}
|
||||
return res;
|
||||
} finally {
|
||||
walker.release();
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
|
||||
|
|
|
@ -574,6 +574,7 @@ public void receive(final InputStream input, final OutputStream output,
|
|||
|
||||
service();
|
||||
} finally {
|
||||
walk.release();
|
||||
try {
|
||||
if (pckOut != null)
|
||||
pckOut.flush();
|
||||
|
|
|
@ -296,6 +296,7 @@ public void upload(final InputStream input, final OutputStream output,
|
|||
pckOut = new PacketLineOut(rawOut);
|
||||
service();
|
||||
} finally {
|
||||
walk.release();
|
||||
if (timer != null) {
|
||||
try {
|
||||
timer.terminate();
|
||||
|
@ -567,7 +568,7 @@ private void sendPack() throws IOException {
|
|||
SideBandOutputStream.CH_PROGRESS, bufsz, rawOut));
|
||||
}
|
||||
|
||||
final PackWriter pw = new PackWriter(db);
|
||||
final PackWriter pw = new PackWriter(db, walk.getObjectReader());
|
||||
try {
|
||||
pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
|
||||
pw.setThin(thin);
|
||||
|
|
Loading…
Reference in New Issue