Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/RevlogStream.java @ 607:66f1cc23b906
Refresh revlogs if a change to a file has been detected; do not force reload of the whole repository
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Tue, 07 May 2013 16:52:46 +0200 |
parents | 5daa42067e7c |
children | e1b29756f901 |
comparison
equal
deleted
inserted
replaced
606:5daa42067e7c | 607:66f1cc23b906 |
---|---|
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; | 25 import java.lang.ref.Reference; |
26 import java.lang.ref.ReferenceQueue; | 26 import java.lang.ref.ReferenceQueue; |
27 import java.lang.ref.SoftReference; | 27 import java.lang.ref.SoftReference; |
28 import java.util.ArrayList; | |
29 import java.util.List; | |
28 import java.util.zip.Inflater; | 30 import java.util.zip.Inflater; |
29 | 31 |
30 import org.tmatesoft.hg.core.Nodeid; | 32 import org.tmatesoft.hg.core.Nodeid; |
31 import org.tmatesoft.hg.repo.HgInternals; | 33 import org.tmatesoft.hg.repo.HgInternals; |
32 import org.tmatesoft.hg.repo.HgInvalidControlFileException; | 34 import org.tmatesoft.hg.repo.HgInvalidControlFileException; |
68 // revision case as well). Now this helps when second #iterate() call is for a revision greater | 70 // 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 | 71 // than one from the first call, and both revisions got same base rev. It's often the case when |
70 // parents/children are analyzed. | 72 // parents/children are analyzed. |
71 private SoftReference<CachedRevision> lastRevisionRead; | 73 private SoftReference<CachedRevision> lastRevisionRead; |
72 private final ReferenceQueue<CachedRevision> lastRevisionQueue = new ReferenceQueue<CachedRevision>(); | 74 private final ReferenceQueue<CachedRevision> lastRevisionQueue = new ReferenceQueue<CachedRevision>(); |
75 // | |
76 private final RevlogChangeMonitor changeTracker; | |
77 private List<Observer> observers; | |
78 private boolean shallDropDerivedCaches = false; | |
73 | 79 |
74 // if we need anything else from HgRepo, might replace DAP parameter with HgRepo and query it for DAP. | 80 // if we need anything else from HgRepo, might replace DAP parameter with HgRepo and query it for DAP. |
75 public RevlogStream(DataAccessProvider dap, File indexFile) { | 81 public RevlogStream(DataAccessProvider dap, File indexFile) { |
76 this.dataAccess = dap; | 82 this.dataAccess = dap; |
77 this.indexFile = indexFile; | 83 this.indexFile = indexFile; |
84 // TODO in fact, shall ask Internals for an instance (there we'll decide whether to use | |
85 // one monitor per multiple files or an instance per file; and let SessionContext pass | |
86 // alternative implementation) | |
87 changeTracker = new RevlogChangeMonitor(indexFile); | |
78 } | 88 } |
79 | 89 |
80 /** | 90 /** |
81 * @param shortRead pass <code>true</code> to indicate intention to read few revisions only (as opposed to reading most of/complete revlog) | 91 * @param shortRead pass <code>true</code> to indicate intention to read few revisions only (as opposed to reading most of/complete revlog) |
82 * @return never <code>null</code>, empty {@link DataAccess} if no stream is available | 92 * @return never <code>null</code>, empty {@link DataAccess} if no stream is available |
212 * | 222 * |
213 * @throws HgInvalidControlFileException if attempt to read index file failed | 223 * @throws HgInvalidControlFileException if attempt to read index file failed |
214 * @throws HgInvalidRevisionException if revisionIndex argument doesn't represent a valid record in the revlog | 224 * @throws HgInvalidRevisionException if revisionIndex argument doesn't represent a valid record in the revlog |
215 */ | 225 */ |
216 public int baseRevision(int revisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { | 226 public int baseRevision(int revisionIndex) throws HgInvalidControlFileException, HgInvalidRevisionException { |
217 initOutline(); | |
218 revisionIndex = checkRevisionIndex(revisionIndex); | 227 revisionIndex = checkRevisionIndex(revisionIndex); |
219 return getBaseRevision(revisionIndex); | 228 return getBaseRevision(revisionIndex); |
220 } | 229 } |
221 | 230 |
222 // Perhaps, RevlogStream should be limited to use of plain int revisions for access, | 231 // Perhaps, RevlogStream should be limited to use of plain int revisions for access, |
352 } finally { | 361 } finally { |
353 CachedRevision cr = r.finish(); | 362 CachedRevision cr = r.finish(); |
354 setLastRevisionRead(cr); | 363 setLastRevisionRead(cr); |
355 } | 364 } |
356 } | 365 } |
366 | |
367 public void attach(Observer listener) { | |
368 assert listener != null; | |
369 if (observers == null) { | |
370 observers = new ArrayList<Observer>(3); | |
371 } | |
372 observers.add(listener); | |
373 } | |
374 | |
375 public void detach(Observer listener) { | |
376 assert listener != null; | |
377 if (observers != null) { | |
378 observers.remove(listener); | |
379 } | |
380 } | |
381 | |
382 /* | |
383 * Note, this method IS NOT a replacement for Observer. It has to be invoked when the validity of any | |
384 * cache built using revision information is in doubt, but it provides reasonable value only till the | |
385 * first initOutline() to be invoked, i.e. in [change..revlog read operation] time frame. If your code | |
386 * accesses cached information without any prior explicit read operation, you shall consult this method | |
387 * if next read operation would in fact bring changed content. | |
388 * Observer is needed in addition to this method because any revlog read operation (e.g. Revlog#getLastRevision) | |
389 * would clear shallDropDerivedCaches(), and if code relies only on this method to clear its derived caches, | |
390 * it would miss the update. | |
391 */ | |
392 public boolean shallDropDerivedCaches() { | |
393 if (shallDropDerivedCaches) { | |
394 return shallDropDerivedCaches; | |
395 } | |
396 return shallDropDerivedCaches = changeTracker.hasChanged(indexFile); | |
397 } | |
357 | 398 |
358 void revisionAdded(int revisionIndex, Nodeid revision, int baseRevisionIndex, long revisionOffset) throws HgInvalidControlFileException { | 399 void revisionAdded(int revisionIndex, Nodeid revision, int baseRevisionIndex, long revisionOffset) throws HgInvalidControlFileException { |
400 shallDropDerivedCaches = true; | |
359 if (!outlineCached()) { | 401 if (!outlineCached()) { |
360 return; | 402 return; |
361 } | 403 } |
362 if (baseRevisions.length != revisionIndex) { | 404 if (baseRevisions.length != revisionIndex) { |
363 throw new HgInvalidControlFileException(String.format("New entry's index shall be %d, not %d", baseRevisions.length, revisionIndex), null, indexFile); | 405 throw new HgInvalidControlFileException(String.format("New entry's index shall be %d, not %d", baseRevisions.length, revisionIndex), null, indexFile); |
419 throw new HgInvalidStateException("Data too big, offset didn't fit to sizeof(int)"); | 461 throw new HgInvalidStateException("Data too big, offset didn't fit to sizeof(int)"); |
420 } | 462 } |
421 return o + REVLOGV1_RECORD_SIZE * recordIndex; | 463 return o + REVLOGV1_RECORD_SIZE * recordIndex; |
422 } | 464 } |
423 | 465 |
466 // every access to index revlog goes after this method only. | |
424 private void initOutline() throws HgInvalidControlFileException { | 467 private void initOutline() throws HgInvalidControlFileException { |
468 // true to send out 'drop-your-caches' event after outline has been built | |
469 final boolean notifyReload; | |
425 if (outlineCached()) { | 470 if (outlineCached()) { |
426 return; | 471 if (!changeTracker.hasChanged(indexFile)) { |
427 } | 472 return; |
473 } | |
474 notifyReload = true; | |
475 } else { | |
476 // no cached outline - inital read, do not send any reload/invalidate notifications | |
477 notifyReload = false; | |
478 } | |
479 changeTracker.touch(indexFile); | |
428 DataAccess da = getIndexStream(false); | 480 DataAccess da = getIndexStream(false); |
429 try { | 481 try { |
430 if (da.isEmpty()) { | 482 if (da.isEmpty()) { |
431 // do not fail with exception if stream is empty, it's likely intentional | 483 // do not fail with exception if stream is empty, it's likely intentional |
432 baseRevisions = new int[0]; | 484 baseRevisions = new int[0]; |
481 } | 533 } |
482 } catch (IOException ex) { | 534 } catch (IOException ex) { |
483 throw new HgInvalidControlFileException("Failed to analyze revlog index", ex, indexFile); | 535 throw new HgInvalidControlFileException("Failed to analyze revlog index", ex, indexFile); |
484 } finally { | 536 } finally { |
485 da.done(); | 537 da.done(); |
538 if (notifyReload && observers != null) { | |
539 for (Observer l : observers) { | |
540 l.reloaded(this); | |
541 } | |
542 shallDropDerivedCaches = false; | |
543 } | |
486 } | 544 } |
487 } | 545 } |
488 | 546 |
489 private CachedRevision getLastRevisionRead() { | 547 private CachedRevision getLastRevisionRead() { |
490 return lastRevisionRead == null ? null : lastRevisionRead.get(); | 548 return lastRevisionRead == null ? null : lastRevisionRead.get(); |
775 // TODO specify nodeid and data length, and reuse policy (i.e. if revlog stream doesn't reuse nodeid[] for each call) | 833 // TODO specify nodeid and data length, and reuse policy (i.e. if revlog stream doesn't reuse nodeid[] for each call) |
776 // implementers shall not invoke DataAccess.done(), it's accomplished by #iterate at appropraite moment | 834 // implementers shall not invoke DataAccess.done(), it's accomplished by #iterate at appropraite moment |
777 void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data); | 835 void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data); |
778 } | 836 } |
779 | 837 |
838 public interface Observer { | |
839 // notify observer of invalidate/reload event in the stream | |
840 public void reloaded(RevlogStream src); | |
841 } | |
780 } | 842 } |