File Coverage

deps/libgit2/src/cache.c
Criterion Covered Total %
statement 70 110 63.6
branch 30 58 51.7
condition n/a
subroutine n/a
pod n/a
total 100 168 59.5


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
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.
6             */
7              
8             #include "cache.h"
9              
10             #include "repository.h"
11             #include "commit.h"
12             #include "thread-utils.h"
13             #include "util.h"
14             #include "odb.h"
15             #include "object.h"
16             #include "git2/oid.h"
17              
18             bool git_cache__enabled = true;
19             ssize_t git_cache__max_storage = (256 * 1024 * 1024);
20             git_atomic_ssize git_cache__current_storage = {0};
21              
22             static size_t git_cache__max_object_size[8] = {
23             0, /* GIT_OBJECT__EXT1 */
24             4096, /* GIT_OBJECT_COMMIT */
25             4096, /* GIT_OBJECT_TREE */
26             0, /* GIT_OBJECT_BLOB */
27             4096, /* GIT_OBJECT_TAG */
28             0, /* GIT_OBJECT__EXT2 */
29             0, /* GIT_OBJECT_OFS_DELTA */
30             0 /* GIT_OBJECT_REF_DELTA */
31             };
32              
33 0           int git_cache_set_max_object_size(git_object_t type, size_t size)
34             {
35 0 0         if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) {
    0          
36 0           git_error_set(GIT_ERROR_INVALID, "type out of range");
37 0           return -1;
38             }
39              
40 0           git_cache__max_object_size[type] = size;
41 0           return 0;
42             }
43              
44 0           void git_cache_dump_stats(git_cache *cache)
45             {
46             git_cached_obj *object;
47              
48 0 0         if (git_cache_size(cache) == 0)
49 0           return;
50              
51 0           printf("Cache %p: %"PRIuZ" items cached, %"PRIdZ" bytes\n",
52             cache, git_cache_size(cache), cache->used_memory);
53              
54 0 0         git_oidmap_foreach_value(cache->map, object, {
    0          
55             char oid_str[9];
56             printf(" %s%c %s (%"PRIuZ")\n",
57             git_object_type2string(object->type),
58             object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
59             git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
60             object->size
61             );
62             });
63             }
64              
65 80           int git_cache_init(git_cache *cache)
66             {
67 80           memset(cache, 0, sizeof(*cache));
68              
69 80 50         if ((git_oidmap_new(&cache->map)) < 0)
70 0           return -1;
71              
72             if (git_rwlock_init(&cache->lock)) {
73             git_error_set(GIT_ERROR_OS, "failed to initialize cache rwlock");
74             return -1;
75             }
76              
77 80           return 0;
78             }
79              
80             /* called with lock */
81 124           static void clear_cache(git_cache *cache)
82             {
83 124           git_cached_obj *evict = NULL;
84              
85 124 100         if (git_cache_size(cache) == 0)
86 94           return;
87              
88 288 100         git_oidmap_foreach_value(cache->map, evict, {
89             git_cached_obj_decref(evict);
90             });
91              
92 30           git_oidmap_clear(cache->map);
93 30           git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory);
94 30           cache->used_memory = 0;
95             }
96              
97 248           void git_cache_clear(git_cache *cache)
98             {
99             if (git_rwlock_wrlock(&cache->lock) < 0)
100             return;
101              
102 124           clear_cache(cache);
103              
104             git_rwlock_wrunlock(&cache->lock);
105             }
106              
107 80           void git_cache_dispose(git_cache *cache)
108             {
109 80           git_cache_clear(cache);
110 80           git_oidmap_free(cache->map);
111             git_rwlock_free(&cache->lock);
112 80           git__memzero(cache, sizeof(*cache));
113 80           }
114              
115             /* Called with lock */
116 0           static void cache_evict_entries(git_cache *cache)
117             {
118 0           size_t evict_count = git_cache_size(cache) / 2048, i;
119 0           ssize_t evicted_memory = 0;
120              
121 0 0         if (evict_count < 8)
122 0           evict_count = 8;
123              
124             /* do not infinite loop if there's not enough entries to evict */
125 0 0         if (evict_count > git_cache_size(cache)) {
126 0           clear_cache(cache);
127 0           return;
128             }
129              
130 0           i = 0;
131 0 0         while (evict_count > 0) {
132             git_cached_obj *evict;
133             const git_oid *key;
134              
135 0 0         if (git_oidmap_iterate((void **) &evict, cache->map, &i, &key) == GIT_ITEROVER)
136 0           break;
137              
138 0           evict_count--;
139 0           evicted_memory += evict->size;
140 0           git_oidmap_delete(cache->map, key);
141 0           git_cached_obj_decref(evict);
142             }
143              
144 0           cache->used_memory -= evicted_memory;
145 0           git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory);
146             }
147              
148 1131           static bool cache_should_store(git_object_t object_type, size_t object_size)
149             {
150 1131           size_t max_size = git_cache__max_object_size[object_type];
151 1131 50         return git_cache__enabled && object_size < max_size;
    100          
152             }
153              
154 2827           static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
155             {
156             git_cached_obj *entry;
157              
158 2827 50         if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0)
159 0           return NULL;
160              
161 2827 100         if ((entry = git_oidmap_get(cache->map, oid)) != NULL) {
162 1683 100         if (flags && entry->flags != flags) {
    100          
163 371           entry = NULL;
164             } else {
165 1312           git_cached_obj_incref(entry);
166             }
167             }
168              
169             git_rwlock_rdunlock(&cache->lock);
170              
171 2827           return entry;
172             }
173              
174 1131           static void *cache_store(git_cache *cache, git_cached_obj *entry)
175             {
176             git_cached_obj *stored_entry;
177              
178 1131           git_cached_obj_incref(entry);
179              
180 1131 50         if (!git_cache__enabled && cache->used_memory > 0) {
    0          
181 0           git_cache_clear(cache);
182 0           return entry;
183             }
184              
185 1131 100         if (!cache_should_store(entry->type, entry->size))
186 453           return entry;
187              
188             if (git_rwlock_wrlock(&cache->lock) < 0)
189             return entry;
190              
191             /* soften the load on the cache */
192 678 50         if (git_cache__current_storage.val > git_cache__max_storage)
193 0           cache_evict_entries(cache);
194              
195             /* not found */
196 678 100         if ((stored_entry = git_oidmap_get(cache->map, &entry->oid)) == NULL) {
197 258 50         if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) {
198 258           git_cached_obj_incref(entry);
199 258           cache->used_memory += entry->size;
200 258           git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size);
201             }
202             }
203             /* found */
204             else {
205 420 100         if (stored_entry->flags == entry->flags) {
206 7           git_cached_obj_decref(entry);
207 7           git_cached_obj_incref(stored_entry);
208 7           entry = stored_entry;
209 413 100         } else if (stored_entry->flags == GIT_CACHE_STORE_RAW &&
    50          
210 182           entry->flags == GIT_CACHE_STORE_PARSED) {
211 182 50         if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) {
212 182           git_cached_obj_decref(stored_entry);
213 182           git_cached_obj_incref(entry);
214             } else {
215 0           git_cached_obj_decref(entry);
216 0           git_cached_obj_incref(stored_entry);
217 182           entry = stored_entry;
218             }
219             } else {
220             /* NO OP */
221             }
222             }
223              
224             git_rwlock_wrunlock(&cache->lock);
225 678           return entry;
226             }
227              
228 764           void *git_cache_store_raw(git_cache *cache, git_odb_object *entry)
229             {
230 764           entry->cached.flags = GIT_CACHE_STORE_RAW;
231 764           return cache_store(cache, (git_cached_obj *)entry);
232             }
233              
234 367           void *git_cache_store_parsed(git_cache *cache, git_object *entry)
235             {
236 367           entry->cached.flags = GIT_CACHE_STORE_PARSED;
237 367           return cache_store(cache, (git_cached_obj *)entry);
238             }
239              
240 1161           git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid)
241             {
242 1161           return cache_get(cache, oid, GIT_CACHE_STORE_RAW);
243             }
244              
245 0           git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid)
246             {
247 0           return cache_get(cache, oid, GIT_CACHE_STORE_PARSED);
248             }
249              
250 1666           void *git_cache_get_any(git_cache *cache, const git_oid *oid)
251             {
252 1666           return cache_get(cache, oid, GIT_CACHE_STORE_ANY);
253             }
254              
255 4189           void git_cached_obj_decref(void *_obj)
256             {
257 4189           git_cached_obj *obj = _obj;
258              
259 4189 100         if (git_atomic_dec(&obj->refcount) == 0) {
260 1122           switch (obj->flags) {
261             case GIT_CACHE_STORE_RAW:
262 762           git_odb_object__free(_obj);
263 762           break;
264              
265             case GIT_CACHE_STORE_PARSED:
266 360           git_object__free(_obj);
267 360           break;
268              
269             default:
270 0           git__free(_obj);
271 0           break;
272             }
273             }
274 4189           }