Mercurial > jhg
comparison src/org/tmatesoft/hg/internal/RevlogStream.java @ 593:9619301a7bb9
Share last revision read between #iterate() invocations, to save revision rebuild efforts when few subsequent revisions are read
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 02 May 2013 16:51:02 +0200 |
parents | b47ef0d2777b |
children | cc7b0c4dc993 |
comparison
equal
deleted
inserted
replaced
592:b12cc3d64a35 | 593:9619301a7bb9 |
---|---|
20 import static org.tmatesoft.hg.repo.HgRepository.TIP; | 20 import static org.tmatesoft.hg.repo.HgRepository.TIP; |
21 import static org.tmatesoft.hg.internal.Internals.REVLOGV1_RECORD_SIZE; | 21 import static org.tmatesoft.hg.internal.Internals.REVLOGV1_RECORD_SIZE; |
22 | 22 |
23 import java.io.File; | 23 import java.io.File; |
24 import java.io.IOException; | 24 import java.io.IOException; |
25 import java.lang.ref.Reference; | |
26 import java.lang.ref.ReferenceQueue; | |
27 import java.lang.ref.SoftReference; | |
25 import java.util.zip.Inflater; | 28 import java.util.zip.Inflater; |
26 | 29 |
27 import org.tmatesoft.hg.core.Nodeid; | 30 import org.tmatesoft.hg.core.Nodeid; |
28 import org.tmatesoft.hg.repo.HgInternals; | 31 import org.tmatesoft.hg.repo.HgInternals; |
29 import org.tmatesoft.hg.repo.HgInvalidControlFileException; | 32 import org.tmatesoft.hg.repo.HgInvalidControlFileException; |
57 private int[] baseRevisions; | 60 private int[] baseRevisions; |
58 private boolean inline = false; | 61 private boolean inline = false; |
59 private final File indexFile; | 62 private final File indexFile; |
60 private File dataFile; | 63 private File dataFile; |
61 private final DataAccessProvider dataAccess; | 64 private final DataAccessProvider dataAccess; |
65 // keeps last complete revision we've read. Note, this cached revision doesn't help | |
66 // for subsequent #iterate() calls with the same revision (Inspector needs more data than | |
67 // we currently cache here, perhaps, we shall cache everything it wants to cover same | |
68 // revision case as well). Now this helps when second #iterate() call is for a revision greater | |
69 // than one from the first call, and both revisions got same base rev. It's often the case when | |
70 // parents/children are analyzed. | |
71 private SoftReference<CachedRevision> lastRevisionRead; | |
72 private final ReferenceQueue<CachedRevision> lastRevisionQueue = new ReferenceQueue<CachedRevision>(); | |
62 | 73 |
63 // if we need anything else from HgRepo, might replace DAP parameter with HgRepo and query it for DAP. | 74 // if we need anything else from HgRepo, might replace DAP parameter with HgRepo and query it for DAP. |
64 public RevlogStream(DataAccessProvider dap, File indexFile) { | 75 public RevlogStream(DataAccessProvider dap, File indexFile) { |
65 this.dataAccess = dap; | 76 this.dataAccess = dap; |
66 this.indexFile = indexFile; | 77 this.indexFile = indexFile; |
258 } finally { | 269 } finally { |
259 daIndex.done(); | 270 daIndex.done(); |
260 } | 271 } |
261 } | 272 } |
262 | 273 |
263 | |
264 | |
265 // should be possible to use TIP, ALL, or -1, -2, -n notation of Hg | 274 // should be possible to use TIP, ALL, or -1, -2, -n notation of Hg |
266 // ? boolean needsNodeid | 275 // ? boolean needsNodeid |
267 public void iterate(int start, int end, boolean needData, Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { | 276 public void iterate(int start, int end, boolean needData, Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { |
268 initOutline(); | 277 initOutline(); |
269 final int indexSize = revisionCount(); | 278 final int indexSize = revisionCount(); |
279 HgInternals.checkRevlogRange(start, end, indexSize-1); | 288 HgInternals.checkRevlogRange(start, end, indexSize-1); |
280 // XXX may cache [start .. end] from index with a single read (pre-read) | 289 // XXX may cache [start .. end] from index with a single read (pre-read) |
281 | 290 |
282 ReaderN1 r = new ReaderN1(needData, inspector, dataAccess.shallMergePatches()); | 291 ReaderN1 r = new ReaderN1(needData, inspector, dataAccess.shallMergePatches()); |
283 try { | 292 try { |
284 r.start(end - start + 1); | 293 r.start(end - start + 1, getLastRevisionRead()); |
285 r.range(start, end); | 294 r.range(start, end); |
286 } catch (IOException ex) { | 295 } catch (IOException ex) { |
287 throw new HgInvalidControlFileException(String.format("Failed reading [%d..%d]", start, end), ex, indexFile); | 296 throw new HgInvalidControlFileException(String.format("Failed reading [%d..%d]", start, end), ex, indexFile); |
288 } catch (HgInvalidControlFileException ex) { | 297 } catch (HgInvalidControlFileException ex) { |
289 throw ex; | 298 throw ex; |
290 } finally { | 299 } finally { |
291 r.finish(); | 300 CachedRevision cr = r.finish(); |
301 setLastRevisionRead(cr); | |
292 } | 302 } |
293 } | 303 } |
294 | 304 |
295 /** | 305 /** |
296 * Effective alternative to {@link #iterate(int, int, boolean, Inspector) batch read}, when only few selected | 306 * Effective alternative to {@link #iterate(int, int, boolean, Inspector) batch read}, when only few selected |
311 throw new HgInvalidRevisionException(String.format("Can't iterate [%d, %d] in range [0..%d]", sortedRevisions[0], sortedRevisions[sortedRevisions.length - 1], indexSize), null, sortedRevisions[sortedRevisions.length - 1]); | 321 throw new HgInvalidRevisionException(String.format("Can't iterate [%d, %d] in range [0..%d]", sortedRevisions[0], sortedRevisions[sortedRevisions.length - 1], indexSize), null, sortedRevisions[sortedRevisions.length - 1]); |
312 } | 322 } |
313 | 323 |
314 ReaderN1 r = new ReaderN1(needData, inspector, dataAccess.shallMergePatches()); | 324 ReaderN1 r = new ReaderN1(needData, inspector, dataAccess.shallMergePatches()); |
315 try { | 325 try { |
316 r.start(sortedRevisions.length); | 326 r.start(sortedRevisions.length, lastRevisionRead == null ? null : lastRevisionRead.get()); |
317 for (int i = 0; i < sortedRevisions.length; ) { | 327 for (int i = 0; i < sortedRevisions.length; ) { |
318 int x = i; | 328 int x = i; |
319 i++; | 329 i++; |
320 while (i < sortedRevisions.length) { | 330 while (i < sortedRevisions.length) { |
321 if (sortedRevisions[i] == sortedRevisions[i-1] + 1) { | 331 if (sortedRevisions[i] == sortedRevisions[i-1] + 1) { |
334 throw new HgInvalidControlFileException(String.format("Failed reading %d revisions in [%d; %d]",c, sortedRevisions[0], sortedRevisions[c-1]), ex, indexFile); | 344 throw new HgInvalidControlFileException(String.format("Failed reading %d revisions in [%d; %d]",c, sortedRevisions[0], sortedRevisions[c-1]), ex, indexFile); |
335 } catch (HgInvalidControlFileException ex) { | 345 } catch (HgInvalidControlFileException ex) { |
336 // TODO post-1.0 fill HgRuntimeException with appropriate file (either index or data, depending on error source) | 346 // TODO post-1.0 fill HgRuntimeException with appropriate file (either index or data, depending on error source) |
337 throw ex; | 347 throw ex; |
338 } finally { | 348 } finally { |
339 r.finish(); | 349 CachedRevision cr = r.finish(); |
350 setLastRevisionRead(cr); | |
340 } | 351 } |
341 } | 352 } |
342 | 353 |
343 void revisionAdded(int revisionIndex, Nodeid revision, int baseRevisionIndex, long revisionOffset) throws HgInvalidControlFileException { | 354 void revisionAdded(int revisionIndex, Nodeid revision, int baseRevisionIndex, long revisionOffset) throws HgInvalidControlFileException { |
344 if (!outlineCached()) { | 355 if (!outlineCached()) { |
469 } finally { | 480 } finally { |
470 da.done(); | 481 da.done(); |
471 } | 482 } |
472 } | 483 } |
473 | 484 |
485 private CachedRevision getLastRevisionRead() { | |
486 return lastRevisionRead == null ? null : lastRevisionRead.get(); | |
487 } | |
488 | |
489 private void setLastRevisionRead(CachedRevision cr) { | |
490 // done() for lastRevisionRead.userData has been called by ReaderN1 once | |
491 // it noticed unsuitable DataAccess. | |
492 // Now, done() for any CachedRevision cleared by GC: | |
493 for (Reference<? extends CachedRevision> r; (r = lastRevisionQueue.poll()) != null;) { | |
494 CachedRevision toClean = r.get(); | |
495 if (toClean != null && toClean.userData != null) { | |
496 toClean.userData.done(); | |
497 } | |
498 } | |
499 if (cr != null) { | |
500 lastRevisionRead = new SoftReference<CachedRevision>(cr, lastRevisionQueue); | |
501 } else { | |
502 lastRevisionRead = null; | |
503 } | |
504 } | |
505 | |
506 final static class CachedRevision { | |
507 final int revision; | |
508 final DataAccess userData; | |
509 | |
510 public CachedRevision(int lastRevisionRead, DataAccess lastUserData) { | |
511 revision = lastRevisionRead; | |
512 userData = lastUserData; | |
513 } | |
514 } | |
515 | |
474 /** | 516 /** |
475 * operation with single file open/close and multiple diverse reads. | 517 * operation with single file open/close and multiple diverse reads. |
476 * XXX initOutline might need similar extraction to keep N1 format knowledge | 518 * XXX initOutline might need similar extraction to keep N1 format knowledge |
477 */ | 519 */ |
478 final class ReaderN1 { | 520 final class ReaderN1 { |
498 private int actualLen; | 540 private int actualLen; |
499 private int baseRevision; | 541 private int baseRevision; |
500 private int linkRevision; | 542 private int linkRevision; |
501 private int parent1Revision; | 543 private int parent1Revision; |
502 private int parent2Revision; | 544 private int parent2Revision; |
503 // next are to track two major bottlenecks - patch application and actual time spent in inspector | |
504 // private long applyTime, inspectorTime; // TIMING | |
505 | 545 |
506 public ReaderN1(boolean dataRequested, Inspector insp, boolean usePatchMerge) { | 546 public ReaderN1(boolean dataRequested, Inspector insp, boolean usePatchMerge) { |
507 assert insp != null; | 547 assert insp != null; |
508 needData = dataRequested; | 548 needData = dataRequested; |
509 inspector = insp; | 549 inspector = insp; |
510 mergePatches = usePatchMerge; | 550 mergePatches = usePatchMerge; |
511 } | 551 } |
512 | 552 |
513 public void start(int totalWork) { | 553 public void start(int totalWork, CachedRevision cachedRevision) { |
514 daIndex = getIndexStream(); | 554 daIndex = getIndexStream(); |
515 if (needData && !inline) { | 555 if (needData && !inline) { |
516 daData = getDataStream(); | 556 daData = getDataStream(); |
517 } | 557 } |
518 lifecycleListener = Adaptable.Factory.getAdapter(inspector, Lifecycle.class, null); | 558 lifecycleListener = Adaptable.Factory.getAdapter(inspector, Lifecycle.class, null); |
519 if (lifecycleListener != null) { | 559 if (lifecycleListener != null) { |
520 cb = new Lifecycle.BasicCallback(); | 560 cb = new Lifecycle.BasicCallback(); |
521 lifecycleListener.start(totalWork, cb, cb); | 561 lifecycleListener.start(totalWork, cb, cb); |
522 } | 562 } |
523 // applyTime = inspectorTime = 0; // TIMING | 563 if (needData && cachedRevision != null) { |
564 lastUserData = cachedRevision.userData; | |
565 lastRevisionRead = cachedRevision.revision; | |
566 assert lastUserData != null; | |
567 } | |
524 } | 568 } |
525 | 569 |
526 // invoked only once per instance | 570 // invoked only once per instance |
527 public void finish() { | 571 public CachedRevision finish() { |
572 CachedRevision rv = null; | |
528 if (lastUserData != null) { | 573 if (lastUserData != null) { |
529 lastUserData.done(); | 574 rv = new CachedRevision(lastRevisionRead, lastUserData); |
530 lastUserData = null; | 575 lastUserData = null; |
531 } | 576 } |
532 if (lifecycleListener != null) { | 577 if (lifecycleListener != null) { |
533 lifecycleListener.finish(cb); | 578 lifecycleListener.finish(cb); |
534 lifecycleListener = null; | 579 lifecycleListener = null; |
538 daIndex.done(); | 583 daIndex.done(); |
539 if (daData != null) { | 584 if (daData != null) { |
540 daData.done(); | 585 daData.done(); |
541 daData = null; | 586 daData = null; |
542 } | 587 } |
543 // System.out.printf("applyTime:%d ms, inspectorTime: %d ms\n", applyTime, inspectorTime); // TIMING | 588 return rv; |
544 } | 589 } |
545 | 590 |
546 private void readHeaderRecord(int i) throws IOException { | 591 private void readHeaderRecord(int i) throws IOException { |
547 if (inline && needData) { | 592 if (inline && needData) { |
548 // inspector reading data (though FilterDataAccess) may have affected index position | 593 // inspector reading data (though FilterDataAccess) may have affected index position |