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.
10 #include "repository.h"
12 #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_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 */
33 int git_cache_set_max_object_size(git_object_t type
, size_t size
)
35 if (type
< 0 || (size_t)type
>= ARRAY_SIZE(git_cache__max_object_size
)) {
36 git_error_set(GIT_ERROR_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
));
69 if ((git_oidmap_new(&cache
->map
)) < 0)
72 if (git_rwlock_init(&cache
->lock
)) {
73 git_error_set(GIT_ERROR_OS
, "failed to initialize cache rwlock");
80 /* called with lock */
81 static void clear_cache(git_cache
*cache
)
83 git_cached_obj
*evict
= NULL
;
85 if (git_cache_size(cache
) == 0)
88 git_oidmap_foreach_value(cache
->map
, evict
, {
89 git_cached_obj_decref(evict
);
92 git_oidmap_clear(cache
->map
);
93 git_atomic_ssize_add(&git_cache__current_storage
, -cache
->used_memory
);
94 cache
->used_memory
= 0;
97 void git_cache_clear(git_cache
*cache
)
99 if (git_rwlock_wrlock(&cache
->lock
) < 0)
104 git_rwlock_wrunlock(&cache
->lock
);
107 void git_cache_dispose(git_cache
*cache
)
109 git_cache_clear(cache
);
110 git_oidmap_free(cache
->map
);
111 git_rwlock_free(&cache
->lock
);
112 git__memzero(cache
, sizeof(*cache
));
115 /* Called with lock */
116 static void cache_evict_entries(git_cache
*cache
)
118 size_t evict_count
= git_cache_size(cache
) / 2048, i
;
119 ssize_t evicted_memory
= 0;
124 /* do not infinite loop if there's not enough entries to evict */
125 if (evict_count
> git_cache_size(cache
)) {
131 while (evict_count
> 0) {
132 git_cached_obj
*evict
;
135 if (git_oidmap_iterate((void **) &evict
, cache
->map
, &i
, &key
) == GIT_ITEROVER
)
139 evicted_memory
+= evict
->size
;
140 git_oidmap_delete(cache
->map
, key
);
141 git_cached_obj_decref(evict
);
144 cache
->used_memory
-= evicted_memory
;
145 git_atomic_ssize_add(&git_cache__current_storage
, -evicted_memory
);
148 static bool cache_should_store(git_object_t object_type
, size_t object_size
)
150 size_t max_size
= git_cache__max_object_size
[object_type
];
151 return git_cache__enabled
&& object_size
< max_size
;
154 static void *cache_get(git_cache
*cache
, const git_oid
*oid
, unsigned int flags
)
156 git_cached_obj
*entry
;
158 if (!git_cache__enabled
|| git_rwlock_rdlock(&cache
->lock
) < 0)
161 if ((entry
= git_oidmap_get(cache
->map
, oid
)) != NULL
) {
162 if (flags
&& entry
->flags
!= flags
) {
165 git_cached_obj_incref(entry
);
169 git_rwlock_rdunlock(&cache
->lock
);
174 static void *cache_store(git_cache
*cache
, git_cached_obj
*entry
)
176 git_cached_obj
*stored_entry
;
178 git_cached_obj_incref(entry
);
180 if (!git_cache__enabled
&& cache
->used_memory
> 0) {
181 git_cache_clear(cache
);
185 if (!cache_should_store(entry
->type
, entry
->size
))
188 if (git_rwlock_wrlock(&cache
->lock
) < 0)
191 /* soften the load on the cache */
192 if (git_cache__current_storage
.val
> git_cache__max_storage
)
193 cache_evict_entries(cache
);
196 if ((stored_entry
= git_oidmap_get(cache
->map
, &entry
->oid
)) == NULL
) {
197 if (git_oidmap_set(cache
->map
, &entry
->oid
, entry
) == 0) {
198 git_cached_obj_incref(entry
);
199 cache
->used_memory
+= entry
->size
;
200 git_atomic_ssize_add(&git_cache__current_storage
, (ssize_t
)entry
->size
);
205 if (stored_entry
->flags
== entry
->flags
) {
206 git_cached_obj_decref(entry
);
207 git_cached_obj_incref(stored_entry
);
208 entry
= stored_entry
;
209 } else if (stored_entry
->flags
== GIT_CACHE_STORE_RAW
&&
210 entry
->flags
== GIT_CACHE_STORE_PARSED
) {
211 git_cached_obj_decref(stored_entry
);
212 git_cached_obj_incref(entry
);
214 git_oidmap_set(cache
->map
, &entry
->oid
, entry
);
220 git_rwlock_wrunlock(&cache
->lock
);
224 void *git_cache_store_raw(git_cache
*cache
, git_odb_object
*entry
)
226 entry
->cached
.flags
= GIT_CACHE_STORE_RAW
;
227 return cache_store(cache
, (git_cached_obj
*)entry
);
230 void *git_cache_store_parsed(git_cache
*cache
, git_object
*entry
)
232 entry
->cached
.flags
= GIT_CACHE_STORE_PARSED
;
233 return cache_store(cache
, (git_cached_obj
*)entry
);
236 git_odb_object
*git_cache_get_raw(git_cache
*cache
, const git_oid
*oid
)
238 return cache_get(cache
, oid
, GIT_CACHE_STORE_RAW
);
241 git_object
*git_cache_get_parsed(git_cache
*cache
, const git_oid
*oid
)
243 return cache_get(cache
, oid
, GIT_CACHE_STORE_PARSED
);
246 void *git_cache_get_any(git_cache
*cache
, const git_oid
*oid
)
248 return cache_get(cache
, oid
, GIT_CACHE_STORE_ANY
);
251 void git_cached_obj_decref(void *_obj
)
253 git_cached_obj
*obj
= _obj
;
255 if (git_atomic_dec(&obj
->refcount
) == 0) {
256 switch (obj
->flags
) {
257 case GIT_CACHE_STORE_RAW
:
258 git_odb_object__free(_obj
);
261 case GIT_CACHE_STORE_PARSED
:
262 git_object__free(_obj
);