File Coverage

deps/libgit2/src/cache.c
Criterion Covered Total %
statement 70 105 66.6
branch 30 52 57.6
condition n/a
subroutine n/a
pod n/a
total 100 157 63.6


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 99           int git_cache_init(git_cache *cache)
45             {
46 99           memset(cache, 0, sizeof(*cache));
47              
48 99 50         if ((git_oidmap_new(&cache->map)) < 0)
49 0           return -1;
50              
51             if (git_rwlock_init(&cache->lock)) {
52             git_error_set(GIT_ERROR_OS, "failed to initialize cache rwlock");
53             return -1;
54             }
55              
56 99           return 0;
57             }
58              
59             /* called with lock */
60 158           static void clear_cache(git_cache *cache)
61             {
62 158           git_cached_obj *evict = NULL;
63              
64 158 100         if (git_cache_size(cache) == 0)
65 128           return;
66              
67 288 100         git_oidmap_foreach_value(cache->map, evict, {
68             git_cached_obj_decref(evict);
69             });
70              
71 30           git_oidmap_clear(cache->map);
72 30           git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory);
73 30           cache->used_memory = 0;
74             }
75              
76 316           void git_cache_clear(git_cache *cache)
77             {
78             if (git_rwlock_wrlock(&cache->lock) < 0)
79             return;
80              
81 158           clear_cache(cache);
82              
83             git_rwlock_wrunlock(&cache->lock);
84             }
85              
86 97           void git_cache_dispose(git_cache *cache)
87             {
88 97           git_cache_clear(cache);
89 97           git_oidmap_free(cache->map);
90             git_rwlock_free(&cache->lock);
91 97           git__memzero(cache, sizeof(*cache));
92 97           }
93              
94             /* Called with lock */
95 0           static void cache_evict_entries(git_cache *cache)
96             {
97 0           size_t evict_count = git_cache_size(cache) / 2048, i;
98 0           ssize_t evicted_memory = 0;
99              
100 0 0         if (evict_count < 8)
101 0           evict_count = 8;
102              
103             /* do not infinite loop if there's not enough entries to evict */
104 0 0         if (evict_count > git_cache_size(cache)) {
105 0           clear_cache(cache);
106 0           return;
107             }
108              
109 0           i = 0;
110 0 0         while (evict_count > 0) {
111             git_cached_obj *evict;
112             const git_oid *key;
113              
114 0 0         if (git_oidmap_iterate((void **) &evict, cache->map, &i, &key) == GIT_ITEROVER)
115 0           break;
116              
117 0           evict_count--;
118 0           evicted_memory += evict->size;
119 0           git_oidmap_delete(cache->map, key);
120 0           git_cached_obj_decref(evict);
121             }
122              
123 0           cache->used_memory -= evicted_memory;
124 0           git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory);
125             }
126              
127 1155           static bool cache_should_store(git_object_t object_type, size_t object_size)
128             {
129 1155           size_t max_size = git_cache__max_object_size[object_type];
130 1155 50         return git_cache__enabled && object_size < max_size;
    100          
131             }
132              
133 2852           static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
134             {
135             git_cached_obj *entry;
136              
137 2852 50         if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0)
138 0           return NULL;
139              
140 2852 100         if ((entry = git_oidmap_get(cache->map, oid)) != NULL) {
141 1683 100         if (flags && entry->flags != flags) {
    100          
142 371           entry = NULL;
143             } else {
144 1312           git_cached_obj_incref(entry);
145             }
146             }
147              
148             git_rwlock_rdunlock(&cache->lock);
149              
150 2852           return entry;
151             }
152              
153 1155           static void *cache_store(git_cache *cache, git_cached_obj *entry)
154             {
155             git_cached_obj *stored_entry;
156              
157 1155           git_cached_obj_incref(entry);
158              
159 1155 50         if (!git_cache__enabled && cache->used_memory > 0) {
    0          
160 0           git_cache_clear(cache);
161 0           return entry;
162             }
163              
164 1155 100         if (!cache_should_store(entry->type, entry->size))
165 477           return entry;
166              
167             if (git_rwlock_wrlock(&cache->lock) < 0)
168             return entry;
169              
170             /* soften the load on the cache */
171 678 50         if (git_cache__current_storage.val > git_cache__max_storage)
172 0           cache_evict_entries(cache);
173              
174             /* not found */
175 678 100         if ((stored_entry = git_oidmap_get(cache->map, &entry->oid)) == NULL) {
176 258 50         if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) {
177 258           git_cached_obj_incref(entry);
178 258           cache->used_memory += entry->size;
179 258           git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size);
180             }
181             }
182             /* found */
183             else {
184 420 100         if (stored_entry->flags == entry->flags) {
185 7           git_cached_obj_decref(entry);
186 7           git_cached_obj_incref(stored_entry);
187 7           entry = stored_entry;
188 413 100         } else if (stored_entry->flags == GIT_CACHE_STORE_RAW &&
    50          
189 182           entry->flags == GIT_CACHE_STORE_PARSED) {
190 182 50         if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) {
191 182           git_cached_obj_decref(stored_entry);
192 182           git_cached_obj_incref(entry);
193             } else {
194 0           git_cached_obj_decref(entry);
195 0           git_cached_obj_incref(stored_entry);
196 182           entry = stored_entry;
197             }
198             } else {
199             /* NO OP */
200             }
201             }
202              
203             git_rwlock_wrunlock(&cache->lock);
204 678           return entry;
205             }
206              
207 776           void *git_cache_store_raw(git_cache *cache, git_odb_object *entry)
208             {
209 776           entry->cached.flags = GIT_CACHE_STORE_RAW;
210 776           return cache_store(cache, (git_cached_obj *)entry);
211             }
212              
213 379           void *git_cache_store_parsed(git_cache *cache, git_object *entry)
214             {
215 379           entry->cached.flags = GIT_CACHE_STORE_PARSED;
216 379           return cache_store(cache, (git_cached_obj *)entry);
217             }
218              
219 1174           git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid)
220             {
221 1174           return cache_get(cache, oid, GIT_CACHE_STORE_RAW);
222             }
223              
224 0           git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid)
225             {
226 0           return cache_get(cache, oid, GIT_CACHE_STORE_PARSED);
227             }
228              
229 1678           void *git_cache_get_any(git_cache *cache, const git_oid *oid)
230             {
231 1678           return cache_get(cache, oid, GIT_CACHE_STORE_ANY);
232             }
233              
234 4225           void git_cached_obj_decref(void *_obj)
235             {
236 4225           git_cached_obj *obj = _obj;
237              
238 4225 100         if (git_atomic_dec(&obj->refcount) == 0) {
239 1146           switch (obj->flags) {
240             case GIT_CACHE_STORE_RAW:
241 774           git_odb_object__free(_obj);
242 774           break;
243              
244             case GIT_CACHE_STORE_PARSED:
245 372           git_object__free(_obj);
246 372           break;
247              
248             default:
249 0           git__free(_obj);
250 0           break;
251             }
252             }
253 4225           }