]> git.proxmox.com Git - libgit2.git/blame - src/cache.c
Add typedefs on some public enums
[libgit2.git] / src / cache.c
CommitLineData
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 18GIT__USE_OIDMAP
bb3de0c4 19
ee12272d 20bool git_cache__enabled = true;
eb63fda2
ET
21ssize_t git_cache__max_storage = (256 * 1024 * 1024);
22git_atomic_ssize git_cache__current_storage = {0};
ee12272d 23
b12b72ea
RB
24static 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
35int 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
46void 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
67int 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 */
76static 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
92void 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
102void 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 */
111static 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 141static 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
147static 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 171static 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
226void *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
232void *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
238git_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
243git_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
248void *git_cache_get_any(git_cache *cache, const git_oid *oid)
249{
250 return cache_get(cache, oid, GIT_CACHE_STORE_ANY);
251}
252
253void 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}