]>
Commit | Line | Data |
---|---|---|
7bfdb3d2 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
7bfdb3d2 | 3 | * |
bb742ede VM |
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. | |
7bfdb3d2 CMN |
6 | */ |
7 | ||
7bfdb3d2 | 8 | #include "mwindow.h" |
eae0bfdc | 9 | |
7bfdb3d2 | 10 | #include "vector.h" |
22a2d3d5 | 11 | #include "futils.h" |
7bfdb3d2 | 12 | #include "map.h" |
c25aa7cd | 13 | #include "runtime.h" |
b3b66c57 CMN |
14 | #include "strmap.h" |
15 | #include "pack.h" | |
16 | ||
7bfdb3d2 CMN |
17 | #define DEFAULT_WINDOW_SIZE \ |
18 | (sizeof(void*) >= 8 \ | |
87d9869f | 19 | ? 1 * 1024 * 1024 * 1024 \ |
7bfdb3d2 CMN |
20 | : 32 * 1024 * 1024) |
21 | ||
22 | #define DEFAULT_MAPPED_LIMIT \ | |
c25aa7cd | 23 | ((1024 * 1024) * (sizeof(void*) >= 8 ? UINT64_C(8192) : UINT64_C(256))) |
7bfdb3d2 | 24 | |
22a2d3d5 UG |
25 | /* default is unlimited */ |
26 | #define DEFAULT_FILE_LIMIT 0 | |
27 | ||
59853eff VM |
28 | size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE; |
29 | size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT; | |
22a2d3d5 | 30 | size_t git_mwindow__file_limit = DEFAULT_FILE_LIMIT; |
7bfdb3d2 | 31 | |
c25aa7cd PP |
32 | /* Mutex to control access to `git_mwindow__mem_ctl` and `git__pack_cache`. */ |
33 | git_mutex git__mwindow_mutex; | |
34 | ||
35 | /* Whenever you want to read or modify this, grab `git__mwindow_mutex` */ | |
22a2d3d5 | 36 | git_mwindow_ctl git_mwindow__mem_ctl; |
8cef828d | 37 | |
b3b66c57 CMN |
38 | /* Global list of mwindow files, to open packs once across repos */ |
39 | git_strmap *git__pack_cache = NULL; | |
40 | ||
c25aa7cd | 41 | static void git_mwindow_global_shutdown(void) |
7bfdb3d2 | 42 | { |
2381d9e4 | 43 | git_strmap *tmp = git__pack_cache; |
8c8ca730 | 44 | |
c25aa7cd PP |
45 | git_mutex_free(&git__mwindow_mutex); |
46 | ||
2381d9e4 ET |
47 | git__pack_cache = NULL; |
48 | git_strmap_free(tmp); | |
b3b66c57 CMN |
49 | } |
50 | ||
2381d9e4 | 51 | int git_mwindow_global_init(void) |
b3b66c57 | 52 | { |
c25aa7cd PP |
53 | int error; |
54 | ||
55 | GIT_ASSERT(!git__pack_cache); | |
b3b66c57 | 56 | |
c25aa7cd PP |
57 | if ((error = git_mutex_init(&git__mwindow_mutex)) < 0 || |
58 | (error = git_strmap_new(&git__pack_cache)) < 0) | |
59 | return error; | |
60 | ||
61 | return git_runtime_shutdown_register(git_mwindow_global_shutdown); | |
b3b66c57 CMN |
62 | } |
63 | ||
64 | int git_mwindow_get_pack(struct git_pack_file **out, const char *path) | |
65 | { | |
b3b66c57 | 66 | struct git_pack_file *pack; |
22a2d3d5 UG |
67 | char *packname; |
68 | int error; | |
b3b66c57 CMN |
69 | |
70 | if ((error = git_packfile__name(&packname, path)) < 0) | |
71 | return error; | |
72 | ||
c19b1c04 | 73 | if (git_mutex_lock(&git__mwindow_mutex) < 0) { |
ac3d33df | 74 | git_error_set(GIT_ERROR_OS, "failed to lock mwindow mutex"); |
b3b66c57 | 75 | return -1; |
c19b1c04 | 76 | } |
b3b66c57 | 77 | |
22a2d3d5 | 78 | pack = git_strmap_get(git__pack_cache, packname); |
b3b66c57 CMN |
79 | git__free(packname); |
80 | ||
22a2d3d5 | 81 | if (pack != NULL) { |
c25aa7cd | 82 | git_atomic32_inc(&pack->refcount); |
b3b66c57 CMN |
83 | git_mutex_unlock(&git__mwindow_mutex); |
84 | *out = pack; | |
85 | return 0; | |
86 | } | |
87 | ||
88 | /* If we didn't find it, we need to create it */ | |
89 | if ((error = git_packfile_alloc(&pack, path)) < 0) { | |
90 | git_mutex_unlock(&git__mwindow_mutex); | |
91 | return error; | |
92 | } | |
93 | ||
c25aa7cd | 94 | git_atomic32_inc(&pack->refcount); |
b3b66c57 | 95 | |
22a2d3d5 | 96 | error = git_strmap_set(git__pack_cache, pack->pack_name, pack); |
b3b66c57 | 97 | git_mutex_unlock(&git__mwindow_mutex); |
5e0f47c3 | 98 | if (error < 0) { |
c25aa7cd PP |
99 | git_packfile_free(pack, false); |
100 | return error; | |
5e0f47c3 | 101 | } |
8cef828d | 102 | |
b3b66c57 CMN |
103 | *out = pack; |
104 | return 0; | |
105 | } | |
106 | ||
c25aa7cd | 107 | int git_mwindow_put_pack(struct git_pack_file *pack) |
b3b66c57 | 108 | { |
c25aa7cd PP |
109 | int count, error; |
110 | struct git_pack_file *pack_to_delete = NULL; | |
b3b66c57 | 111 | |
c25aa7cd PP |
112 | if ((error = git_mutex_lock(&git__mwindow_mutex)) < 0) |
113 | return error; | |
b3b66c57 | 114 | |
c19b1c04 | 115 | /* put before get would be a corrupted state */ |
c25aa7cd | 116 | GIT_ASSERT(git__pack_cache); |
b3b66c57 | 117 | |
c19b1c04 | 118 | /* if we cannot find it, the state is corrupted */ |
c25aa7cd | 119 | GIT_ASSERT(git_strmap_exists(git__pack_cache, pack->pack_name)); |
b3b66c57 | 120 | |
c25aa7cd | 121 | count = git_atomic32_dec(&pack->refcount); |
b3b66c57 | 122 | if (count == 0) { |
22a2d3d5 | 123 | git_strmap_delete(git__pack_cache, pack->pack_name); |
c25aa7cd | 124 | pack_to_delete = pack; |
b3b66c57 | 125 | } |
b3b66c57 | 126 | git_mutex_unlock(&git__mwindow_mutex); |
c25aa7cd | 127 | git_packfile_free(pack_to_delete, false); |
b3b66c57 | 128 | |
c25aa7cd | 129 | return 0; |
b3b66c57 CMN |
130 | } |
131 | ||
132 | /* | |
133 | * Free all the windows in a sequence, typically because we're done | |
c25aa7cd | 134 | * with the file. Needs to hold the git__mwindow_mutex. |
b3b66c57 | 135 | */ |
c25aa7cd | 136 | static int git_mwindow_free_all_locked(git_mwindow_file *mwf) |
b3b66c57 | 137 | { |
22a2d3d5 | 138 | git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; |
b3b66c57 CMN |
139 | size_t i; |
140 | ||
7bfdb3d2 CMN |
141 | /* |
142 | * Remove these windows from the global list | |
143 | */ | |
a15c550d VM |
144 | for (i = 0; i < ctl->windowfiles.length; ++i){ |
145 | if (git_vector_get(&ctl->windowfiles, i) == mwf) { | |
146 | git_vector_remove(&ctl->windowfiles, i); | |
7bfdb3d2 CMN |
147 | break; |
148 | } | |
149 | } | |
150 | ||
a15c550d VM |
151 | if (ctl->windowfiles.length == 0) { |
152 | git_vector_free(&ctl->windowfiles); | |
153 | ctl->windowfiles.contents = NULL; | |
7bfdb3d2 CMN |
154 | } |
155 | ||
156 | while (mwf->windows) { | |
157 | git_mwindow *w = mwf->windows; | |
c25aa7cd | 158 | GIT_ASSERT(w->inuse_cnt == 0); |
7bfdb3d2 | 159 | |
a15c550d VM |
160 | ctl->mapped -= w->window_map.len; |
161 | ctl->open_windows--; | |
7bfdb3d2 CMN |
162 | |
163 | git_futils_mmap_free(&w->window_map); | |
164 | ||
165 | mwf->windows = w->next; | |
3286c408 | 166 | git__free(w); |
7bfdb3d2 | 167 | } |
c25aa7cd PP |
168 | |
169 | return 0; | |
170 | } | |
171 | ||
172 | int git_mwindow_free_all(git_mwindow_file *mwf) | |
173 | { | |
174 | int error; | |
175 | ||
176 | if (git_mutex_lock(&git__mwindow_mutex)) { | |
177 | git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex"); | |
178 | return -1; | |
179 | } | |
180 | ||
181 | error = git_mwindow_free_all_locked(mwf); | |
182 | ||
183 | git_mutex_unlock(&git__mwindow_mutex); | |
184 | ||
185 | return error; | |
7bfdb3d2 CMN |
186 | } |
187 | ||
188 | /* | |
189 | * Check if a window 'win' contains the address 'offset' | |
190 | */ | |
22a2d3d5 | 191 | int git_mwindow_contains(git_mwindow *win, off64_t offset) |
7bfdb3d2 | 192 | { |
22a2d3d5 | 193 | off64_t win_off = win->offset; |
7bfdb3d2 | 194 | return win_off <= offset |
22a2d3d5 | 195 | && offset <= (off64_t)(win_off + win->window_map.len); |
7bfdb3d2 CMN |
196 | } |
197 | ||
22a2d3d5 UG |
198 | #define GIT_MWINDOW__LRU -1 |
199 | #define GIT_MWINDOW__MRU 1 | |
200 | ||
7bfdb3d2 | 201 | /* |
22a2d3d5 UG |
202 | * Find the least- or most-recently-used window in a file that is not currently |
203 | * being used. The 'only_unused' flag controls whether the caller requires the | |
204 | * file to only have unused windows. If '*out_window' is non-null, it is used as | |
205 | * a starting point for the comparison. | |
206 | * | |
207 | * Returns whether such a window was found in the file. | |
7bfdb3d2 | 208 | */ |
22a2d3d5 UG |
209 | static bool git_mwindow_scan_recently_used( |
210 | git_mwindow_file *mwf, | |
211 | git_mwindow **out_window, | |
212 | git_mwindow **out_last, | |
213 | bool only_unused, | |
214 | int comparison_sign) | |
7bfdb3d2 | 215 | { |
22a2d3d5 UG |
216 | git_mwindow *w, *w_last; |
217 | git_mwindow *lru_window = NULL, *lru_last = NULL; | |
218 | bool found = false; | |
219 | ||
c25aa7cd PP |
220 | GIT_ASSERT_ARG(mwf); |
221 | GIT_ASSERT_ARG(out_window); | |
22a2d3d5 UG |
222 | |
223 | lru_window = *out_window; | |
224 | if (out_last) | |
225 | lru_last = *out_last; | |
226 | ||
227 | for (w_last = NULL, w = mwf->windows; w; w_last = w, w = w->next) { | |
228 | if (w->inuse_cnt) { | |
229 | if (only_unused) | |
230 | return false; | |
231 | /* This window is currently being used. Skip it. */ | |
232 | continue; | |
233 | } | |
234 | ||
235 | /* | |
236 | * If the current one is more (or less) recent than the last one, | |
237 | * store it in the output parameter. If lru_window is NULL, | |
238 | * it's the first loop, so store it as well. | |
239 | */ | |
240 | if (!lru_window || | |
241 | (comparison_sign == GIT_MWINDOW__LRU && lru_window->last_used > w->last_used) || | |
242 | (comparison_sign == GIT_MWINDOW__MRU && lru_window->last_used < w->last_used)) { | |
243 | lru_window = w; | |
244 | lru_last = w_last; | |
245 | found = true; | |
7bfdb3d2 | 246 | } |
7bfdb3d2 | 247 | } |
22a2d3d5 UG |
248 | |
249 | if (!found) | |
250 | return false; | |
251 | ||
252 | *out_window = lru_window; | |
253 | if (out_last) | |
254 | *out_last = lru_last; | |
255 | return true; | |
7bfdb3d2 CMN |
256 | } |
257 | ||
258 | /* | |
22a2d3d5 | 259 | * Close the least recently used window (that is currently not being used) out |
c25aa7cd | 260 | * of all the files. Called under lock from new_window_locked. |
7bfdb3d2 | 261 | */ |
c25aa7cd | 262 | static int git_mwindow_close_lru_window_locked(void) |
7bfdb3d2 | 263 | { |
22a2d3d5 UG |
264 | git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; |
265 | git_mwindow_file *cur; | |
10c06114 | 266 | size_t i; |
22a2d3d5 | 267 | git_mwindow *lru_window = NULL, *lru_last = NULL, **list = NULL; |
7bfdb3d2 | 268 | |
22a2d3d5 UG |
269 | git_vector_foreach(&ctl->windowfiles, i, cur) { |
270 | if (git_mwindow_scan_recently_used( | |
271 | cur, &lru_window, &lru_last, false, GIT_MWINDOW__LRU)) { | |
5fa1bed0 | 272 | list = &cur->windows; |
22a2d3d5 | 273 | } |
7bfdb3d2 CMN |
274 | } |
275 | ||
22a2d3d5 | 276 | if (!lru_window) { |
ac3d33df | 277 | git_error_set(GIT_ERROR_OS, "failed to close memory window; couldn't find LRU"); |
0d0fa7c3 RB |
278 | return -1; |
279 | } | |
7bfdb3d2 | 280 | |
22a2d3d5 UG |
281 | ctl->mapped -= lru_window->window_map.len; |
282 | git_futils_mmap_free(&lru_window->window_map); | |
7bfdb3d2 | 283 | |
22a2d3d5 UG |
284 | if (lru_last) |
285 | lru_last->next = lru_window->next; | |
0d0fa7c3 | 286 | else |
22a2d3d5 | 287 | *list = lru_window->next; |
7bfdb3d2 | 288 | |
22a2d3d5 | 289 | git__free(lru_window); |
0d0fa7c3 | 290 | ctl->open_windows--; |
7bfdb3d2 | 291 | |
0d0fa7c3 | 292 | return 0; |
7bfdb3d2 CMN |
293 | } |
294 | ||
22a2d3d5 | 295 | /* |
c25aa7cd | 296 | * Finds the file that does not have any open windows AND whose |
22a2d3d5 UG |
297 | * most-recently-used window is the least-recently used one across all |
298 | * currently open files. | |
299 | * | |
c25aa7cd | 300 | * Called under lock from new_window_locked. |
22a2d3d5 | 301 | */ |
c25aa7cd | 302 | static int git_mwindow_find_lru_file_locked(git_mwindow_file **out) |
22a2d3d5 UG |
303 | { |
304 | git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; | |
305 | git_mwindow_file *lru_file = NULL, *current_file = NULL; | |
306 | git_mwindow *lru_window = NULL; | |
307 | size_t i; | |
308 | ||
309 | git_vector_foreach(&ctl->windowfiles, i, current_file) { | |
310 | git_mwindow *mru_window = NULL; | |
311 | if (!git_mwindow_scan_recently_used( | |
312 | current_file, &mru_window, NULL, true, GIT_MWINDOW__MRU)) { | |
313 | continue; | |
314 | } | |
c25aa7cd PP |
315 | if (!lru_window || lru_window->last_used > mru_window->last_used) { |
316 | lru_window = mru_window; | |
22a2d3d5 | 317 | lru_file = current_file; |
c25aa7cd | 318 | } |
22a2d3d5 UG |
319 | } |
320 | ||
321 | if (!lru_file) { | |
322 | git_error_set(GIT_ERROR_OS, "failed to close memory window file; couldn't find LRU"); | |
323 | return -1; | |
324 | } | |
325 | ||
c25aa7cd | 326 | *out = lru_file; |
22a2d3d5 UG |
327 | return 0; |
328 | } | |
329 | ||
8cef828d | 330 | /* This gets called under lock from git_mwindow_open */ |
c25aa7cd | 331 | static git_mwindow *new_window_locked( |
a15c550d | 332 | git_file fd, |
22a2d3d5 UG |
333 | off64_t size, |
334 | off64_t offset) | |
7bfdb3d2 | 335 | { |
22a2d3d5 | 336 | git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; |
59853eff | 337 | size_t walign = git_mwindow__window_size / 2; |
22a2d3d5 | 338 | off64_t len; |
7bfdb3d2 CMN |
339 | git_mwindow *w; |
340 | ||
c25aa7cd | 341 | w = git__calloc(1, sizeof(*w)); |
53607868 | 342 | |
7bfdb3d2 | 343 | if (w == NULL) |
0d0fa7c3 | 344 | return NULL; |
7bfdb3d2 | 345 | |
7bfdb3d2 CMN |
346 | w->offset = (offset / walign) * walign; |
347 | ||
348 | len = size - w->offset; | |
22a2d3d5 UG |
349 | if (len > (off64_t)git_mwindow__window_size) |
350 | len = (off64_t)git_mwindow__window_size; | |
7bfdb3d2 | 351 | |
a15c550d | 352 | ctl->mapped += (size_t)len; |
7bfdb3d2 | 353 | |
59853eff | 354 | while (git_mwindow__mapped_limit < ctl->mapped && |
c25aa7cd | 355 | git_mwindow_close_lru_window_locked() == 0) /* nop */; |
7bfdb3d2 | 356 | |
5fa1bed0 | 357 | /* |
59853eff | 358 | * We treat `mapped_limit` as a soft limit. If we can't find a |
5fa1bed0 CMN |
359 | * window to close and are above the limit, we still mmap the new |
360 | * window. | |
361 | */ | |
7bfdb3d2 | 362 | |
e1de726c | 363 | if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) { |
d50fd571 CMN |
364 | /* |
365 | * The first error might be down to memory fragmentation even if | |
366 | * we're below our soft limits, so free up what we can and try again. | |
367 | */ | |
368 | ||
c25aa7cd | 369 | while (git_mwindow_close_lru_window_locked() == 0) |
d50fd571 CMN |
370 | /* nop */; |
371 | ||
372 | if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) { | |
373 | git__free(w); | |
374 | return NULL; | |
375 | } | |
e1de726c | 376 | } |
7bfdb3d2 | 377 | |
a15c550d VM |
378 | ctl->mmap_calls++; |
379 | ctl->open_windows++; | |
7bfdb3d2 | 380 | |
a15c550d VM |
381 | if (ctl->mapped > ctl->peak_mapped) |
382 | ctl->peak_mapped = ctl->mapped; | |
7bfdb3d2 | 383 | |
a15c550d VM |
384 | if (ctl->open_windows > ctl->peak_open_windows) |
385 | ctl->peak_open_windows = ctl->open_windows; | |
7bfdb3d2 CMN |
386 | |
387 | return w; | |
7bfdb3d2 CMN |
388 | } |
389 | ||
390 | /* | |
391 | * Open a new window, closing the least recenty used until we have | |
392 | * enough space. Don't forget to add it to your list | |
393 | */ | |
a15c550d VM |
394 | unsigned char *git_mwindow_open( |
395 | git_mwindow_file *mwf, | |
396 | git_mwindow **cursor, | |
22a2d3d5 | 397 | off64_t offset, |
deafee7b | 398 | size_t extra, |
a15c550d | 399 | unsigned int *left) |
7bfdb3d2 | 400 | { |
22a2d3d5 | 401 | git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; |
7bfdb3d2 CMN |
402 | git_mwindow *w = *cursor; |
403 | ||
a35b3864 | 404 | if (git_mutex_lock(&git__mwindow_mutex)) { |
ac3d33df | 405 | git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex"); |
a35b3864 JSS |
406 | return NULL; |
407 | } | |
408 | ||
40879fac | 409 | if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) { |
7bfdb3d2 CMN |
410 | if (w) { |
411 | w->inuse_cnt--; | |
412 | } | |
413 | ||
414 | for (w = mwf->windows; w; w = w->next) { | |
31e80290 | 415 | if (git_mwindow_contains(w, offset) && |
946a6dc4 | 416 | git_mwindow_contains(w, offset + extra)) |
7bfdb3d2 CMN |
417 | break; |
418 | } | |
419 | ||
420 | /* | |
421 | * If there isn't a suitable window, we need to create a new | |
422 | * one. | |
423 | */ | |
424 | if (!w) { | |
c25aa7cd | 425 | w = new_window_locked(mwf->fd, mwf->size, offset); |
8cef828d CMN |
426 | if (w == NULL) { |
427 | git_mutex_unlock(&git__mwindow_mutex); | |
7bfdb3d2 | 428 | return NULL; |
8cef828d | 429 | } |
7bfdb3d2 CMN |
430 | w->next = mwf->windows; |
431 | mwf->windows = w; | |
432 | } | |
433 | } | |
434 | ||
435 | /* If we changed w, store it in the cursor */ | |
436 | if (w != *cursor) { | |
a15c550d | 437 | w->last_used = ctl->used_ctr++; |
7bfdb3d2 CMN |
438 | w->inuse_cnt++; |
439 | *cursor = w; | |
440 | } | |
441 | ||
442 | offset -= w->offset; | |
7bfdb3d2 CMN |
443 | |
444 | if (left) | |
f6867e63 | 445 | *left = (unsigned int)(w->window_map.len - offset); |
7bfdb3d2 | 446 | |
8cef828d | 447 | git_mutex_unlock(&git__mwindow_mutex); |
7bfdb3d2 | 448 | return (unsigned char *) w->window_map.data + offset; |
7bfdb3d2 CMN |
449 | } |
450 | ||
451 | int git_mwindow_file_register(git_mwindow_file *mwf) | |
452 | { | |
c25aa7cd | 453 | git_vector closed_files = GIT_VECTOR_INIT; |
22a2d3d5 | 454 | git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; |
c25aa7cd PP |
455 | int error; |
456 | size_t i; | |
457 | git_mwindow_file *closed_file = NULL; | |
7bfdb3d2 | 458 | |
a35b3864 | 459 | if (git_mutex_lock(&git__mwindow_mutex)) { |
ac3d33df | 460 | git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex"); |
a35b3864 JSS |
461 | return -1; |
462 | } | |
463 | ||
a15c550d | 464 | if (ctl->windowfiles.length == 0 && |
c25aa7cd | 465 | (error = git_vector_init(&ctl->windowfiles, 8, NULL)) < 0) { |
8cef828d | 466 | git_mutex_unlock(&git__mwindow_mutex); |
c25aa7cd | 467 | goto cleanup; |
8cef828d CMN |
468 | } |
469 | ||
22a2d3d5 | 470 | if (git_mwindow__file_limit) { |
c25aa7cd | 471 | git_mwindow_file *lru_file; |
22a2d3d5 | 472 | while (git_mwindow__file_limit <= ctl->windowfiles.length && |
c25aa7cd PP |
473 | git_mwindow_find_lru_file_locked(&lru_file) == 0) { |
474 | if ((error = git_vector_insert(&closed_files, lru_file)) < 0) { | |
475 | /* | |
e579e0f7 | 476 | * Exceeding the file limit seems preferable to being open to |
c25aa7cd PP |
477 | * data races that can end up corrupting the heap. |
478 | */ | |
479 | break; | |
480 | } | |
481 | git_mwindow_free_all_locked(lru_file); | |
482 | } | |
22a2d3d5 UG |
483 | } |
484 | ||
c25aa7cd | 485 | error = git_vector_insert(&ctl->windowfiles, mwf); |
8cef828d | 486 | git_mutex_unlock(&git__mwindow_mutex); |
c25aa7cd PP |
487 | if (error < 0) |
488 | goto cleanup; | |
489 | ||
490 | /* | |
491 | * Once we have released the global windowfiles lock, we can close each | |
492 | * individual file. Before doing so, acquire that file's lock to avoid | |
493 | * closing a file that is currently being used. | |
494 | */ | |
495 | git_vector_foreach(&closed_files, i, closed_file) { | |
496 | error = git_mutex_lock(&closed_file->lock); | |
497 | if (error < 0) | |
498 | continue; | |
499 | p_close(closed_file->fd); | |
500 | closed_file->fd = -1; | |
501 | git_mutex_unlock(&closed_file->lock); | |
502 | } | |
7bfdb3d2 | 503 | |
c25aa7cd PP |
504 | cleanup: |
505 | git_vector_free(&closed_files); | |
506 | return error; | |
7bfdb3d2 CMN |
507 | } |
508 | ||
f9b55bcb | 509 | void git_mwindow_file_deregister(git_mwindow_file *mwf) |
1d8943c6 | 510 | { |
22a2d3d5 | 511 | git_mwindow_ctl *ctl = &git_mwindow__mem_ctl; |
1d8943c6 | 512 | git_mwindow_file *cur; |
10c06114 | 513 | size_t i; |
1d8943c6 | 514 | |
f9b55bcb SG |
515 | if (git_mutex_lock(&git__mwindow_mutex)) |
516 | return; | |
a35b3864 | 517 | |
1d8943c6 CMN |
518 | git_vector_foreach(&ctl->windowfiles, i, cur) { |
519 | if (cur == mwf) { | |
520 | git_vector_remove(&ctl->windowfiles, i); | |
8cef828d | 521 | git_mutex_unlock(&git__mwindow_mutex); |
f9b55bcb | 522 | return; |
1d8943c6 CMN |
523 | } |
524 | } | |
8cef828d | 525 | git_mutex_unlock(&git__mwindow_mutex); |
1d8943c6 CMN |
526 | } |
527 | ||
7bfdb3d2 CMN |
528 | void git_mwindow_close(git_mwindow **window) |
529 | { | |
530 | git_mwindow *w = *window; | |
531 | if (w) { | |
a35b3864 | 532 | if (git_mutex_lock(&git__mwindow_mutex)) { |
ac3d33df | 533 | git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex"); |
a35b3864 JSS |
534 | return; |
535 | } | |
536 | ||
7bfdb3d2 | 537 | w->inuse_cnt--; |
8cef828d | 538 | git_mutex_unlock(&git__mwindow_mutex); |
7bfdb3d2 CMN |
539 | *window = NULL; |
540 | } | |
541 | } |