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