]> git.proxmox.com Git - libgit2.git/blob - src/cache.c
Some stats
[libgit2.git] / src / cache.c
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 "common.h"
9 #include "repository.h"
10 #include "commit.h"
11 #include "thread-utils.h"
12 #include "util.h"
13 #include "cache.h"
14 #include "odb.h"
15 #include "object.h"
16 #include "git2/oid.h"
17
18 GIT__USE_OIDMAP
19
20 size_t git_cache__max_object_size[8] = {
21 0, /* GIT_OBJ__EXT1 */
22 4096, /* GIT_OBJ_COMMIT */
23 4096, /* GIT_OBJ_TREE */
24 0, /* GIT_OBJ_BLOB */
25 4096, /* GIT_OBJ_TAG */
26 0, /* GIT_OBJ__EXT2 */
27 0, /* GIT_OBJ_OFS_DELTA */
28 0 /* GIT_OBJ_REF_DELTA */
29 };
30
31 void git_cache_dump_stats(git_cache *cache)
32 {
33 git_cached_obj *object;
34
35 if (kh_size(cache->map) == 0)
36 return;
37
38 printf("Cache %p: %d items cached, %d bytes\n",
39 cache, kh_size(cache->map), (int)cache->used_memory);
40
41 kh_foreach_value(cache->map, object, {
42 char oid_str[9];
43 printf(" %s%c %s (%d)\n",
44 git_object_type2string(object->type),
45 object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
46 git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
47 (int)object->size
48 );
49 });
50 }
51
52 int git_cache_init(git_cache *cache)
53 {
54 cache->used_memory = 0;
55 cache->map = git_oidmap_alloc();
56 git_mutex_init(&cache->lock);
57 return 0;
58 }
59
60 void git_cache_free(git_cache *cache)
61 {
62 git_oidmap_free(cache->map);
63 git_mutex_free(&cache->lock);
64 }
65
66 /* Call with lock, yo */
67 static void cache_evict_entries(git_cache *cache, size_t evict_count)
68 {
69 uint32_t seed = rand();
70
71 /* do not infinite loop if there's not enough entries to evict */
72 if (evict_count > kh_size(cache->map))
73 return;
74
75 while (evict_count > 0) {
76 khiter_t pos = seed++ % kh_end(cache->map);
77
78 if (kh_exist(cache->map, pos)) {
79 git_cached_obj *evict = kh_val(cache->map, pos);
80
81 evict_count--;
82 cache->used_memory -= evict->size;
83 git_cached_obj_decref(evict);
84
85 kh_del(oid, cache->map, pos);
86 }
87 }
88 }
89
90 static bool cache_should_store(git_otype object_type, size_t object_size)
91 {
92 size_t max_size = git_cache__max_object_size[object_type];
93
94 if (max_size == 0 || object_size > max_size)
95 return false;
96
97 return true;
98 }
99
100 static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
101 {
102 khiter_t pos;
103 git_cached_obj *entry = NULL;
104
105 if (git_mutex_lock(&cache->lock) < 0)
106 return NULL;
107
108 pos = kh_get(oid, cache->map, oid);
109 if (pos != kh_end(cache->map)) {
110 entry = kh_val(cache->map, pos);
111
112 if (flags && entry->flags != flags) {
113 entry = NULL;
114 } else {
115 git_cached_obj_incref(entry);
116 }
117 }
118
119 git_mutex_unlock(&cache->lock);
120
121 return entry;
122 }
123
124 static void *cache_store(git_cache *cache, git_cached_obj *entry)
125 {
126 khiter_t pos;
127
128 git_cached_obj_incref(entry);
129
130 if (!cache_should_store(entry->type, entry->size))
131 return entry;
132
133 if (git_mutex_lock(&cache->lock) < 0)
134 return entry;
135
136 pos = kh_get(oid, cache->map, &entry->oid);
137
138 /* not found */
139 if (pos == kh_end(cache->map)) {
140 int rval;
141
142 pos = kh_put(oid, cache->map, &entry->oid, &rval);
143 if (rval >= 0) {
144 kh_key(cache->map, pos) = &entry->oid;
145 kh_val(cache->map, pos) = entry;
146 git_cached_obj_incref(entry);
147 cache->used_memory += entry->size;
148 }
149 }
150 /* found */
151 else {
152 git_cached_obj *stored_entry = kh_val(cache->map, pos);
153
154 if (stored_entry->flags == entry->flags) {
155 git_cached_obj_decref(entry);
156 git_cached_obj_incref(stored_entry);
157 entry = stored_entry;
158 } else if (stored_entry->flags == GIT_CACHE_STORE_RAW &&
159 entry->flags == GIT_CACHE_STORE_PARSED) {
160 git_cached_obj_decref(stored_entry);
161 git_cached_obj_incref(entry);
162
163 kh_key(cache->map, pos) = &entry->oid;
164 kh_val(cache->map, pos) = entry;
165 } else {
166 /* NO OP */
167 }
168 }
169
170 git_mutex_unlock(&cache->lock);
171 return entry;
172 }
173
174 void *git_cache_store_raw(git_cache *cache, git_odb_object *entry)
175 {
176 entry->cached.flags = GIT_CACHE_STORE_RAW;
177 return cache_store(cache, (git_cached_obj *)entry);
178 }
179
180 void *git_cache_store_parsed(git_cache *cache, git_object *entry)
181 {
182 entry->cached.flags = GIT_CACHE_STORE_PARSED;
183 return cache_store(cache, (git_cached_obj *)entry);
184 }
185
186 git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid)
187 {
188 return cache_get(cache, oid, GIT_CACHE_STORE_RAW);
189 }
190
191 git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid)
192 {
193 return cache_get(cache, oid, GIT_CACHE_STORE_PARSED);
194 }
195
196 void *git_cache_get_any(git_cache *cache, const git_oid *oid)
197 {
198 return cache_get(cache, oid, GIT_CACHE_STORE_ANY);
199 }
200
201 void git_cached_obj_decref(void *_obj)
202 {
203 git_cached_obj *obj = _obj;
204
205 if (git_atomic_dec(&obj->refcount) == 0) {
206 switch (obj->flags) {
207 case GIT_CACHE_STORE_RAW:
208 git_odb_object__free(_obj);
209 break;
210
211 case GIT_CACHE_STORE_PARSED:
212 git_object__free(_obj);
213 break;
214
215 default:
216 git__free(_obj);
217 break;
218 }
219 }
220 }