]>
Commit | Line | Data |
---|---|---|
bb3de0c4 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
bb3de0c4 | 3 | * |
bb742ede VM |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
bb3de0c4 VM |
6 | */ |
7 | ||
8 | #include "common.h" | |
9 | #include "repository.h" | |
10 | #include "commit.h" | |
11 | #include "thread-utils.h" | |
25f258e7 | 12 | #include "util.h" |
bb3de0c4 | 13 | #include "cache.h" |
5df18424 VM |
14 | #include "odb.h" |
15 | #include "object.h" | |
c07d9c95 | 16 | #include "git2/oid.h" |
bb3de0c4 | 17 | |
5df18424 | 18 | GIT__USE_OIDMAP |
bb3de0c4 | 19 | |
ee12272d | 20 | bool git_cache__enabled = true; |
eb63fda2 ET |
21 | ssize_t git_cache__max_storage = (256 * 1024 * 1024); |
22 | git_atomic_ssize git_cache__current_storage = {0}; | |
ee12272d | 23 | |
b12b72ea RB |
24 | static size_t git_cache__max_object_size[8] = { |
25 | 0, /* GIT_OBJ__EXT1 */ | |
064236ca VM |
26 | 4096, /* GIT_OBJ_COMMIT */ |
27 | 4096, /* GIT_OBJ_TREE */ | |
b12b72ea | 28 | 0, /* GIT_OBJ_BLOB */ |
064236ca | 29 | 4096, /* GIT_OBJ_TAG */ |
b12b72ea RB |
30 | 0, /* GIT_OBJ__EXT2 */ |
31 | 0, /* GIT_OBJ_OFS_DELTA */ | |
32 | 0 /* GIT_OBJ_REF_DELTA */ | |
6b90e244 VM |
33 | }; |
34 | ||
b12b72ea RB |
35 | int git_cache_set_max_object_size(git_otype type, size_t size) |
36 | { | |
37 | if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) { | |
38 | giterr_set(GITERR_INVALID, "type out of range"); | |
39 | return -1; | |
40 | } | |
41 | ||
42 | git_cache__max_object_size[type] = size; | |
43 | return 0; | |
44 | } | |
45 | ||
d9d423e4 VM |
46 | void git_cache_dump_stats(git_cache *cache) |
47 | { | |
48 | git_cached_obj *object; | |
49 | ||
50 | if (kh_size(cache->map) == 0) | |
51 | return; | |
52 | ||
53 | printf("Cache %p: %d items cached, %d bytes\n", | |
54 | cache, kh_size(cache->map), (int)cache->used_memory); | |
55 | ||
56 | kh_foreach_value(cache->map, object, { | |
57 | char oid_str[9]; | |
58 | printf(" %s%c %s (%d)\n", | |
59 | git_object_type2string(object->type), | |
60 | object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ', | |
61 | git_oid_tostr(oid_str, sizeof(oid_str), &object->oid), | |
62 | (int)object->size | |
63 | ); | |
64 | }); | |
65 | } | |
66 | ||
5df18424 VM |
67 | int git_cache_init(git_cache *cache) |
68 | { | |
064236ca | 69 | cache->used_memory = 0; |
5df18424 | 70 | cache->map = git_oidmap_alloc(); |
e4b4da14 | 71 | git_mutex_init(&cache->lock); |
0d0fa7c3 | 72 | return 0; |
bb3de0c4 VM |
73 | } |
74 | ||
d8771592 VM |
75 | /* called with lock */ |
76 | static void clear_cache(git_cache *cache) | |
e183e375 VM |
77 | { |
78 | git_cached_obj *evict = NULL; | |
79 | ||
879458e7 VM |
80 | if (kh_size(cache->map) == 0) |
81 | return; | |
82 | ||
e183e375 VM |
83 | kh_foreach_value(cache->map, evict, { |
84 | git_cached_obj_decref(evict); | |
85 | }); | |
86 | ||
87 | kh_clear(oid, cache->map); | |
eb63fda2 | 88 | git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory); |
e183e375 | 89 | cache->used_memory = 0; |
d8771592 VM |
90 | } |
91 | ||
92 | void git_cache_clear(git_cache *cache) | |
93 | { | |
94 | if (git_mutex_lock(&cache->lock) < 0) | |
95 | return; | |
96 | ||
97 | clear_cache(cache); | |
e183e375 VM |
98 | |
99 | git_mutex_unlock(&cache->lock); | |
100 | } | |
101 | ||
78606263 RB |
102 | void git_cache_free(git_cache *cache) |
103 | { | |
104 | git_cache_clear(cache); | |
105 | ||
106 | git_oidmap_free(cache->map); | |
107 | git_mutex_free(&cache->lock); | |
108 | } | |
109 | ||
d8771592 VM |
110 | /* Called with lock */ |
111 | static void cache_evict_entries(git_cache *cache) | |
c4e91d45 VM |
112 | { |
113 | uint32_t seed = rand(); | |
eb63fda2 ET |
114 | size_t evict_count = 8; |
115 | ssize_t evicted_memory = 0; | |
c4e91d45 VM |
116 | |
117 | /* do not infinite loop if there's not enough entries to evict */ | |
e183e375 | 118 | if (evict_count > kh_size(cache->map)) { |
d8771592 | 119 | clear_cache(cache); |
c4e91d45 | 120 | return; |
e183e375 | 121 | } |
c4e91d45 | 122 | |
064236ca | 123 | while (evict_count > 0) { |
c4e91d45 VM |
124 | khiter_t pos = seed++ % kh_end(cache->map); |
125 | ||
126 | if (kh_exist(cache->map, pos)) { | |
064236ca VM |
127 | git_cached_obj *evict = kh_val(cache->map, pos); |
128 | ||
129 | evict_count--; | |
a14163a7 | 130 | evicted_memory += evict->size; |
064236ca VM |
131 | git_cached_obj_decref(evict); |
132 | ||
c4e91d45 | 133 | kh_del(oid, cache->map, pos); |
c4e91d45 VM |
134 | } |
135 | } | |
a14163a7 VM |
136 | |
137 | cache->used_memory -= evicted_memory; | |
eb63fda2 | 138 | git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory); |
c4e91d45 VM |
139 | } |
140 | ||
6b90e244 | 141 | static bool cache_should_store(git_otype object_type, size_t object_size) |
bb3de0c4 | 142 | { |
064236ca | 143 | size_t max_size = git_cache__max_object_size[object_type]; |
ee12272d | 144 | return git_cache__enabled && object_size < max_size; |
5df18424 | 145 | } |
bb3de0c4 | 146 | |
5df18424 VM |
147 | static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags) |
148 | { | |
149 | khiter_t pos; | |
150 | git_cached_obj *entry = NULL; | |
bb3de0c4 | 151 | |
ee12272d | 152 | if (!git_cache__enabled || git_mutex_lock(&cache->lock) < 0) |
a35b3864 | 153 | return NULL; |
a35b3864 | 154 | |
5df18424 VM |
155 | pos = kh_get(oid, cache->map, oid); |
156 | if (pos != kh_end(cache->map)) { | |
157 | entry = kh_val(cache->map, pos); | |
e4b4da14 | 158 | |
5df18424 VM |
159 | if (flags && entry->flags != flags) { |
160 | entry = NULL; | |
161 | } else { | |
162 | git_cached_obj_incref(entry); | |
bb3de0c4 | 163 | } |
bb3de0c4 | 164 | } |
5df18424 | 165 | |
e4b4da14 | 166 | git_mutex_unlock(&cache->lock); |
bb3de0c4 | 167 | |
5df18424 | 168 | return entry; |
bb3de0c4 VM |
169 | } |
170 | ||
5df18424 | 171 | static void *cache_store(git_cache *cache, git_cached_obj *entry) |
bb3de0c4 | 172 | { |
5df18424 | 173 | khiter_t pos; |
bb3de0c4 | 174 | |
064236ca VM |
175 | git_cached_obj_incref(entry); |
176 | ||
177 | if (!cache_should_store(entry->type, entry->size)) | |
178 | return entry; | |
179 | ||
5df18424 VM |
180 | if (git_mutex_lock(&cache->lock) < 0) |
181 | return entry; | |
e4b4da14 | 182 | |
a14163a7 VM |
183 | /* soften the load on the cache */ |
184 | if (git_cache__current_storage.val > git_cache__max_storage) | |
d8771592 VM |
185 | cache_evict_entries(cache); |
186 | ||
5df18424 | 187 | pos = kh_get(oid, cache->map, &entry->oid); |
1d009603 | 188 | |
5df18424 VM |
189 | /* not found */ |
190 | if (pos == kh_end(cache->map)) { | |
191 | int rval; | |
a35b3864 | 192 | |
5df18424 VM |
193 | pos = kh_put(oid, cache->map, &entry->oid, &rval); |
194 | if (rval >= 0) { | |
195 | kh_key(cache->map, pos) = &entry->oid; | |
196 | kh_val(cache->map, pos) = entry; | |
197 | git_cached_obj_incref(entry); | |
064236ca | 198 | cache->used_memory += entry->size; |
eb63fda2 | 199 | git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size); |
5df18424 VM |
200 | } |
201 | } | |
202 | /* found */ | |
203 | else { | |
204 | git_cached_obj *stored_entry = kh_val(cache->map, pos); | |
205 | ||
206 | if (stored_entry->flags == entry->flags) { | |
207 | git_cached_obj_decref(entry); | |
208 | git_cached_obj_incref(stored_entry); | |
209 | entry = stored_entry; | |
210 | } else if (stored_entry->flags == GIT_CACHE_STORE_RAW && | |
211 | entry->flags == GIT_CACHE_STORE_PARSED) { | |
212 | git_cached_obj_decref(stored_entry); | |
213 | git_cached_obj_incref(entry); | |
214 | ||
215 | kh_key(cache->map, pos) = &entry->oid; | |
216 | kh_val(cache->map, pos) = entry; | |
e4b4da14 | 217 | } else { |
5df18424 | 218 | /* NO OP */ |
e4b4da14 | 219 | } |
bb3de0c4 VM |
220 | } |
221 | ||
5df18424 | 222 | git_mutex_unlock(&cache->lock); |
bb3de0c4 VM |
223 | return entry; |
224 | } | |
5df18424 VM |
225 | |
226 | void *git_cache_store_raw(git_cache *cache, git_odb_object *entry) | |
227 | { | |
228 | entry->cached.flags = GIT_CACHE_STORE_RAW; | |
229 | return cache_store(cache, (git_cached_obj *)entry); | |
230 | } | |
231 | ||
232 | void *git_cache_store_parsed(git_cache *cache, git_object *entry) | |
233 | { | |
234 | entry->cached.flags = GIT_CACHE_STORE_PARSED; | |
235 | return cache_store(cache, (git_cached_obj *)entry); | |
236 | } | |
237 | ||
238 | git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid) | |
239 | { | |
240 | return cache_get(cache, oid, GIT_CACHE_STORE_RAW); | |
241 | } | |
242 | ||
243 | git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid) | |
244 | { | |
245 | return cache_get(cache, oid, GIT_CACHE_STORE_PARSED); | |
246 | } | |
247 | ||
248 | void *git_cache_get_any(git_cache *cache, const git_oid *oid) | |
249 | { | |
250 | return cache_get(cache, oid, GIT_CACHE_STORE_ANY); | |
251 | } | |
252 | ||
253 | void git_cached_obj_decref(void *_obj) | |
254 | { | |
255 | git_cached_obj *obj = _obj; | |
256 | ||
257 | if (git_atomic_dec(&obj->refcount) == 0) { | |
258 | switch (obj->flags) { | |
259 | case GIT_CACHE_STORE_RAW: | |
260 | git_odb_object__free(_obj); | |
261 | break; | |
262 | ||
263 | case GIT_CACHE_STORE_PARSED: | |
264 | git_object__free(_obj); | |
265 | break; | |
266 | ||
267 | default: | |
268 | git__free(_obj); | |
269 | break; | |
270 | } | |
271 | } | |
272 | } |