2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
9 #include "repository.h"
11 #include "thread-utils.h"
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};
22 static size_t git_cache__max_object_size
[8] = {
23 0, /* GIT_OBJ__EXT1 */
24 4096, /* GIT_OBJ_COMMIT */
25 4096, /* GIT_OBJ_TREE */
27 4096, /* GIT_OBJ_TAG */
28 0, /* GIT_OBJ__EXT2 */
29 0, /* GIT_OBJ_OFS_DELTA */
30 0 /* GIT_OBJ_REF_DELTA */
33 int git_cache_set_max_object_size(git_otype type
, size_t size
)
35 if (type
< 0 || (size_t)type
>= ARRAY_SIZE(git_cache__max_object_size
)) {
36 giterr_set(GITERR_INVALID
, "type out of range");
40 git_cache__max_object_size
[type
] = size
;
44 void git_cache_dump_stats(git_cache
*cache
)
46 git_cached_obj
*object
;
48 if (git_cache_size(cache
) == 0)
51 printf("Cache %p: %"PRIuZ
" items cached, %"PRIdZ
" bytes\n",
52 cache
, git_cache_size(cache
), cache
->used_memory
);
54 git_oidmap_foreach_value(cache
->map
, object
, {
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
),
65 int git_cache_init(git_cache
*cache
)
67 memset(cache
, 0, sizeof(*cache
));
68 cache
->map
= git_oidmap_alloc();
69 GITERR_CHECK_ALLOC(cache
->map
);
70 if (git_rwlock_init(&cache
->lock
)) {
71 giterr_set(GITERR_OS
, "failed to initialize cache rwlock");
77 /* called with lock */
78 static void clear_cache(git_cache
*cache
)
80 git_cached_obj
*evict
= NULL
;
82 if (git_cache_size(cache
) == 0)
85 git_oidmap_foreach_value(cache
->map
, evict
, {
86 git_cached_obj_decref(evict
);
89 git_oidmap_clear(cache
->map
);
90 git_atomic_ssize_add(&git_cache__current_storage
, -cache
->used_memory
);
91 cache
->used_memory
= 0;
94 void git_cache_clear(git_cache
*cache
)
96 if (git_rwlock_wrlock(&cache
->lock
) < 0)
101 git_rwlock_wrunlock(&cache
->lock
);
104 void git_cache_free(git_cache
*cache
)
106 git_cache_clear(cache
);
107 git_oidmap_free(cache
->map
);
108 git_rwlock_free(&cache
->lock
);
109 git__memzero(cache
, sizeof(*cache
));
112 /* Called with lock */
113 static void cache_evict_entries(git_cache
*cache
)
115 uint32_t seed
= rand();
116 size_t evict_count
= 8;
117 ssize_t evicted_memory
= 0;
119 /* do not infinite loop if there's not enough entries to evict */
120 if (evict_count
> git_cache_size(cache
)) {
125 while (evict_count
> 0) {
126 khiter_t pos
= seed
++ % git_oidmap_end(cache
->map
);
128 if (git_oidmap_has_data(cache
->map
, pos
)) {
129 git_cached_obj
*evict
= git_oidmap_value_at(cache
->map
, pos
);
132 evicted_memory
+= evict
->size
;
133 git_cached_obj_decref(evict
);
135 git_oidmap_delete_at(cache
->map
, pos
);
139 cache
->used_memory
-= evicted_memory
;
140 git_atomic_ssize_add(&git_cache__current_storage
, -evicted_memory
);
143 static bool cache_should_store(git_otype object_type
, size_t object_size
)
145 size_t max_size
= git_cache__max_object_size
[object_type
];
146 return git_cache__enabled
&& object_size
< max_size
;
149 static void *cache_get(git_cache
*cache
, const git_oid
*oid
, unsigned int flags
)
152 git_cached_obj
*entry
= NULL
;
154 if (!git_cache__enabled
|| git_rwlock_rdlock(&cache
->lock
) < 0)
157 pos
= git_oidmap_lookup_index(cache
->map
, oid
);
158 if (git_oidmap_valid_index(cache
->map
, pos
)) {
159 entry
= git_oidmap_value_at(cache
->map
, pos
);
161 if (flags
&& entry
->flags
!= flags
) {
164 git_cached_obj_incref(entry
);
168 git_rwlock_rdunlock(&cache
->lock
);
173 static void *cache_store(git_cache
*cache
, git_cached_obj
*entry
)
177 git_cached_obj_incref(entry
);
179 if (!git_cache__enabled
&& cache
->used_memory
> 0) {
180 git_cache_clear(cache
);
184 if (!cache_should_store(entry
->type
, entry
->size
))
187 if (git_rwlock_wrlock(&cache
->lock
) < 0)
190 /* soften the load on the cache */
191 if (git_cache__current_storage
.val
> git_cache__max_storage
)
192 cache_evict_entries(cache
);
194 pos
= git_oidmap_lookup_index(cache
->map
, &entry
->oid
);
197 if (!git_oidmap_valid_index(cache
->map
, pos
)) {
200 git_oidmap_insert(cache
->map
, &entry
->oid
, entry
, &rval
);
202 git_cached_obj_incref(entry
);
203 cache
->used_memory
+= entry
->size
;
204 git_atomic_ssize_add(&git_cache__current_storage
, (ssize_t
)entry
->size
);
209 git_cached_obj
*stored_entry
= git_oidmap_value_at(cache
->map
, pos
);
211 if (stored_entry
->flags
== entry
->flags
) {
212 git_cached_obj_decref(entry
);
213 git_cached_obj_incref(stored_entry
);
214 entry
= stored_entry
;
215 } else if (stored_entry
->flags
== GIT_CACHE_STORE_RAW
&&
216 entry
->flags
== GIT_CACHE_STORE_PARSED
) {
217 git_cached_obj_decref(stored_entry
);
218 git_cached_obj_incref(entry
);
220 git_oidmap_set_key_at(cache
->map
, pos
, &entry
->oid
);
221 git_oidmap_set_value_at(cache
->map
, pos
, entry
);
227 git_rwlock_wrunlock(&cache
->lock
);
231 void *git_cache_store_raw(git_cache
*cache
, git_odb_object
*entry
)
233 entry
->cached
.flags
= GIT_CACHE_STORE_RAW
;
234 return cache_store(cache
, (git_cached_obj
*)entry
);
237 void *git_cache_store_parsed(git_cache
*cache
, git_object
*entry
)
239 entry
->cached
.flags
= GIT_CACHE_STORE_PARSED
;
240 return cache_store(cache
, (git_cached_obj
*)entry
);
243 git_odb_object
*git_cache_get_raw(git_cache
*cache
, const git_oid
*oid
)
245 return cache_get(cache
, oid
, GIT_CACHE_STORE_RAW
);
248 git_object
*git_cache_get_parsed(git_cache
*cache
, const git_oid
*oid
)
250 return cache_get(cache
, oid
, GIT_CACHE_STORE_PARSED
);
253 void *git_cache_get_any(git_cache
*cache
, const git_oid
*oid
)
255 return cache_get(cache
, oid
, GIT_CACHE_STORE_ANY
);
258 void git_cached_obj_decref(void *_obj
)
260 git_cached_obj
*obj
= _obj
;
262 if (git_atomic_dec(&obj
->refcount
) == 0) {
263 switch (obj
->flags
) {
264 case GIT_CACHE_STORE_RAW
:
265 git_odb_object__free(_obj
);
268 case GIT_CACHE_STORE_PARSED
:
269 git_object__free(_obj
);