2 * Copyright (C) 2008, Google Inc.
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org
.spearce
.jgit
.lib
;
41 import java
.io
.IOException
;
42 import java
.lang
.ref
.ReferenceQueue
;
43 import java
.util
.concurrent
.atomic
.AtomicInteger
;
46 * Caches slices of a {@link PackFile} in memory for faster read access.
48 * The WindowCache serves as a Java based "buffer cache", loading segments of a
49 * PackFile into the JVM heap prior to use. As JGit often wants to do reads of
50 * only tiny slices of a file, the WindowCache tries to smooth out these tiny
51 * reads into larger block-sized IO operations.
53 public class WindowCache
extends OffsetCache
<ByteWindow
, WindowCache
.WindowRef
> {
54 private static final int bits(int newSize
) {
56 throw new IllegalArgumentException("Invalid window size");
57 if (Integer
.bitCount(newSize
) != 1)
58 throw new IllegalArgumentException("Window size must be power of 2");
59 return Integer
.numberOfTrailingZeros(newSize
);
62 private static volatile WindowCache cache
;
65 reconfigure(new WindowCacheConfig());
69 * Modify the configuration of the window cache.
71 * The new configuration is applied immediately. If the new limits are
72 * smaller than what what is currently cached, older entries will be purged
73 * as soon as possible to allow the cache to meet the new limit.
75 * @param packedGitLimit
76 * maximum number of bytes to hold within this instance.
77 * @param packedGitWindowSize
78 * number of bytes per window within the cache.
79 * @param packedGitMMAP
80 * true to enable use of mmap when creating windows.
81 * @param deltaBaseCacheLimit
82 * number of bytes to hold in the delta base cache.
83 * @deprecated Use {@link WindowCacheConfig} instead.
85 public static void reconfigure(final int packedGitLimit
,
86 final int packedGitWindowSize
, final boolean packedGitMMAP
,
87 final int deltaBaseCacheLimit
) {
88 final WindowCacheConfig c
= new WindowCacheConfig();
89 c
.setPackedGitLimit(packedGitLimit
);
90 c
.setPackedGitWindowSize(packedGitWindowSize
);
91 c
.setPackedGitMMAP(packedGitMMAP
);
92 c
.setDeltaBaseCacheLimit(deltaBaseCacheLimit
);
97 * Modify the configuration of the window cache.
99 * The new configuration is applied immediately. If the new limits are
100 * smaller than what what is currently cached, older entries will be purged
101 * as soon as possible to allow the cache to meet the new limit.
104 * the new window cache configuration.
105 * @throws IllegalArgumentException
106 * the cache configuration contains one or more invalid
107 * settings, usually too low of a limit.
109 public static void reconfigure(final WindowCacheConfig cfg
) {
110 final WindowCache nc
= new WindowCache(cfg
);
111 final WindowCache oc
= cache
;
115 UnpackedObjectCache
.reconfigure(cfg
);
118 static WindowCache
getInstance() {
122 static final ByteWindow
get(final PackFile pack
, final long offset
)
124 final WindowCache c
= cache
;
125 final ByteWindow r
= c
.getOrLoad(pack
, c
.toStart(offset
));
127 // The cache was reconfigured while we were using the old one
128 // to load this window. The window is still valid, but our
129 // cache may think its still live. Ensure the window is removed
130 // from the old cache so resources can be released.
137 static final void purge(final PackFile pack
) {
138 cache
.removeAll(pack
);
141 private final int maxFiles
;
143 private final int maxBytes
;
145 private final boolean mmap
;
147 private final int windowSizeShift
;
149 private final int windowSize
;
151 private final AtomicInteger openFiles
;
153 private final AtomicInteger openBytes
;
155 private WindowCache(final WindowCacheConfig cfg
) {
156 super(tableSize(cfg
), lockCount(cfg
));
157 maxFiles
= cfg
.getPackedGitOpenFiles();
158 maxBytes
= cfg
.getPackedGitLimit();
159 mmap
= cfg
.isPackedGitMMAP();
160 windowSizeShift
= bits(cfg
.getPackedGitWindowSize());
161 windowSize
= 1 << windowSizeShift
;
163 openFiles
= new AtomicInteger();
164 openBytes
= new AtomicInteger();
167 throw new IllegalArgumentException("Open files must be >= 1");
168 if (maxBytes
< windowSize
)
169 throw new IllegalArgumentException("Window size must be < limit");
173 return openFiles
.get();
177 return openBytes
.get();
181 protected int hash(final int packHash
, final long off
) {
182 return packHash
+ (int) (off
>>> windowSizeShift
);
186 protected ByteWindow
load(final PackFile pack
, final long offset
)
188 if (pack
.beginWindowCache())
189 openFiles
.incrementAndGet();
192 return pack
.mmap(offset
, windowSize
);
193 return pack
.read(offset
, windowSize
);
194 } catch (IOException e
) {
197 } catch (RuntimeException e
) {
207 protected WindowRef
createRef(final PackFile p
, final long o
,
208 final ByteWindow v
) {
209 final WindowRef ref
= new WindowRef(p
, o
, v
, queue
);
210 openBytes
.addAndGet(ref
.size
);
215 protected void clear(final WindowRef ref
) {
216 openBytes
.addAndGet(-ref
.size
);
220 private void close(final PackFile pack
) {
221 if (pack
.endWindowCache())
222 openFiles
.decrementAndGet();
226 protected boolean isFull() {
227 return maxFiles
< openFiles
.get() || maxBytes
< openBytes
.get();
230 private long toStart(final long offset
) {
231 return (offset
>>> windowSizeShift
) << windowSizeShift
;
234 private static int tableSize(final WindowCacheConfig cfg
) {
235 final int wsz
= cfg
.getPackedGitWindowSize();
236 final int limit
= cfg
.getPackedGitLimit();
238 throw new IllegalArgumentException("Invalid window size");
240 throw new IllegalArgumentException("Window size must be < limit");
241 return 5 * (limit
/ wsz
) / 2;
244 private static int lockCount(final WindowCacheConfig cfg
) {
245 return Math
.max(cfg
.getPackedGitOpenFiles(), 32);
248 static class WindowRef
extends OffsetCache
.Ref
<ByteWindow
> {
251 WindowRef(final PackFile pack
, final long position
, final ByteWindow v
,
252 final ReferenceQueue
<ByteWindow
> queue
) {
253 super(pack
, position
, v
, queue
);