1 | #include "mupdf/fitz.h" |
2 | #include "fitz-imp.h" |
3 | |
4 | #include <limits.h> |
5 | #include <string.h> |
6 | #include <stdlib.h> |
7 | #include <stdio.h> |
8 | |
9 | /* Enable FITZ_DEBUG_LOCKING_TIMES below if you want to check the times |
10 | * for which locks are held too. */ |
11 | #ifdef FITZ_DEBUG_LOCKING |
12 | #undef FITZ_DEBUG_LOCKING_TIMES |
13 | #endif |
14 | |
15 | /* |
16 | * The malloc family of functions will always try scavenging when they run out of memory. |
17 | * They will only fail when scavenging cannot free up memory from caches in the fz_context. |
18 | * All the functions will throw an exception when no memory can be allocated, |
19 | * except the _no_throw family which instead silently returns NULL. |
20 | */ |
21 | |
22 | static void * |
23 | do_scavenging_malloc(fz_context *ctx, size_t size) |
24 | { |
25 | void *p; |
26 | int phase = 0; |
27 | |
28 | fz_lock(ctx, FZ_LOCK_ALLOC); |
29 | do { |
30 | p = ctx->alloc.malloc(ctx->alloc.user, size); |
31 | if (p != NULL) |
32 | { |
33 | fz_unlock(ctx, FZ_LOCK_ALLOC); |
34 | return p; |
35 | } |
36 | } while (fz_store_scavenge(ctx, size, &phase)); |
37 | fz_unlock(ctx, FZ_LOCK_ALLOC); |
38 | |
39 | return NULL; |
40 | } |
41 | |
42 | static void * |
43 | do_scavenging_realloc(fz_context *ctx, void *p, size_t size) |
44 | { |
45 | void *q; |
46 | int phase = 0; |
47 | |
48 | fz_lock(ctx, FZ_LOCK_ALLOC); |
49 | do { |
50 | q = ctx->alloc.realloc(ctx->alloc.user, p, size); |
51 | if (q != NULL) |
52 | { |
53 | fz_unlock(ctx, FZ_LOCK_ALLOC); |
54 | return q; |
55 | } |
56 | } while (fz_store_scavenge(ctx, size, &phase)); |
57 | fz_unlock(ctx, FZ_LOCK_ALLOC); |
58 | |
59 | return NULL; |
60 | } |
61 | |
62 | void * |
63 | fz_malloc(fz_context *ctx, size_t size) |
64 | { |
65 | void *p; |
66 | if (size == 0) |
67 | return NULL; |
68 | p = do_scavenging_malloc(ctx, size); |
69 | if (!p) |
70 | fz_throw(ctx, FZ_ERROR_MEMORY, "malloc of %zu bytes failed" , size); |
71 | return p; |
72 | } |
73 | |
74 | void * |
75 | fz_malloc_no_throw(fz_context *ctx, size_t size) |
76 | { |
77 | if (size == 0) |
78 | return NULL; |
79 | return do_scavenging_malloc(ctx, size); |
80 | } |
81 | |
82 | void * |
83 | fz_calloc(fz_context *ctx, size_t count, size_t size) |
84 | { |
85 | void *p; |
86 | if (count == 0 || size == 0) |
87 | return NULL; |
88 | if (count > SIZE_MAX / size) |
89 | fz_throw(ctx, FZ_ERROR_MEMORY, "calloc (%zu x %zu bytes) failed (size_t overflow)" , count, size); |
90 | p = do_scavenging_malloc(ctx, count * size); |
91 | if (!p) |
92 | fz_throw(ctx, FZ_ERROR_MEMORY, "calloc (%zu x %zu bytes) failed" , count, size); |
93 | memset(p, 0, count*size); |
94 | return p; |
95 | } |
96 | |
97 | void * |
98 | fz_calloc_no_throw(fz_context *ctx, size_t count, size_t size) |
99 | { |
100 | void *p; |
101 | if (count == 0 || size == 0) |
102 | return NULL; |
103 | if (count > SIZE_MAX / size) |
104 | return NULL; |
105 | p = do_scavenging_malloc(ctx, count * size); |
106 | if (p) |
107 | memset(p, 0, count * size); |
108 | return p; |
109 | } |
110 | |
111 | void * |
112 | fz_realloc(fz_context *ctx, void *p, size_t size) |
113 | { |
114 | if (size == 0) |
115 | { |
116 | fz_free(ctx, p); |
117 | return NULL; |
118 | } |
119 | p = do_scavenging_realloc(ctx, p, size); |
120 | if (!p) |
121 | fz_throw(ctx, FZ_ERROR_MEMORY, "realloc (%zu bytes) failed" , size); |
122 | return p; |
123 | } |
124 | |
125 | void * |
126 | fz_realloc_no_throw(fz_context *ctx, void *p, size_t size) |
127 | { |
128 | if (size == 0) |
129 | { |
130 | fz_free(ctx, p); |
131 | return NULL; |
132 | } |
133 | return do_scavenging_realloc(ctx, p, size); |
134 | } |
135 | |
136 | void |
137 | fz_free(fz_context *ctx, void *p) |
138 | { |
139 | if (p) |
140 | { |
141 | fz_lock(ctx, FZ_LOCK_ALLOC); |
142 | ctx->alloc.free(ctx->alloc.user, p); |
143 | fz_unlock(ctx, FZ_LOCK_ALLOC); |
144 | } |
145 | } |
146 | |
147 | char * |
148 | fz_strdup(fz_context *ctx, const char *s) |
149 | { |
150 | size_t len = strlen(s) + 1; |
151 | char *ns = fz_malloc(ctx, len); |
152 | memcpy(ns, s, len); |
153 | return ns; |
154 | } |
155 | |
156 | static void * |
157 | fz_malloc_default(void *opaque, size_t size) |
158 | { |
159 | return malloc(size); |
160 | } |
161 | |
162 | static void * |
163 | fz_realloc_default(void *opaque, void *old, size_t size) |
164 | { |
165 | return realloc(old, size); |
166 | } |
167 | |
168 | static void |
169 | fz_free_default(void *opaque, void *ptr) |
170 | { |
171 | free(ptr); |
172 | } |
173 | |
174 | fz_alloc_context fz_alloc_default = |
175 | { |
176 | NULL, |
177 | fz_malloc_default, |
178 | fz_realloc_default, |
179 | fz_free_default |
180 | }; |
181 | |
182 | static void |
183 | fz_lock_default(void *user, int lock) |
184 | { |
185 | } |
186 | |
187 | static void |
188 | fz_unlock_default(void *user, int lock) |
189 | { |
190 | } |
191 | |
192 | fz_locks_context fz_locks_default = |
193 | { |
194 | NULL, |
195 | fz_lock_default, |
196 | fz_unlock_default |
197 | }; |
198 | |
199 | #ifdef FITZ_DEBUG_LOCKING |
200 | |
201 | enum |
202 | { |
203 | FZ_LOCK_DEBUG_CONTEXT_MAX = 100 |
204 | }; |
205 | |
206 | fz_context *fz_lock_debug_contexts[FZ_LOCK_DEBUG_CONTEXT_MAX]; |
207 | int fz_locks_debug[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX]; |
208 | |
209 | #ifdef FITZ_DEBUG_LOCKING_TIMES |
210 | |
211 | int fz_debug_locking_inited = 0; |
212 | int fz_lock_program_start; |
213 | int fz_lock_time[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX] = { { 0 } }; |
214 | int fz_lock_taken[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX] = { { 0 } }; |
215 | |
216 | /* We implement our own millisecond clock, as clock() cannot be trusted |
217 | * when threads are involved. */ |
218 | static int ms_clock(void) |
219 | { |
220 | #ifdef _WIN32 |
221 | return (int)GetTickCount(); |
222 | #else |
223 | struct timeval tp; |
224 | gettimeofday(&tp, NULL); |
225 | return (tp.tv_sec*1000) + (tp.tv_usec/1000); |
226 | #endif |
227 | } |
228 | |
229 | static void dump_lock_times(void) |
230 | { |
231 | int i, j; |
232 | int prog_time = ms_clock() - fz_lock_program_start; |
233 | |
234 | for (j = 0; j < FZ_LOCK_MAX; j++) |
235 | { |
236 | int total = 0; |
237 | for (i = 0; i < FZ_LOCK_DEBUG_CONTEXT_MAX; i++) |
238 | { |
239 | total += fz_lock_time[i][j]; |
240 | } |
241 | printf("Lock %d held for %g seconds (%g%%)\n" , j, total / 1000.0f, 100.0f*total/prog_time); |
242 | } |
243 | printf("Total program time %g seconds\n" , prog_time / 1000.0f); |
244 | } |
245 | |
246 | #endif |
247 | |
248 | static int find_context(fz_context *ctx) |
249 | { |
250 | int i; |
251 | |
252 | for (i = 0; i < FZ_LOCK_DEBUG_CONTEXT_MAX; i++) |
253 | { |
254 | if (fz_lock_debug_contexts[i] == ctx) |
255 | return i; |
256 | if (fz_lock_debug_contexts[i] == NULL) |
257 | { |
258 | int gottit = 0; |
259 | /* We've not locked on this context before, so use |
260 | * this one for this new context. We might have other |
261 | * threads trying here too though so, so claim it |
262 | * atomically. No one has locked on this context |
263 | * before, so we are safe to take the ALLOC lock. */ |
264 | ctx->locks.lock(ctx->locks.user, FZ_LOCK_ALLOC); |
265 | /* If it's still free, then claim it as ours, |
266 | * otherwise we'll keep hunting. */ |
267 | if (fz_lock_debug_contexts[i] == NULL) |
268 | { |
269 | gottit = 1; |
270 | fz_lock_debug_contexts[i] = ctx; |
271 | #ifdef FITZ_DEBUG_LOCKING_TIMES |
272 | if (fz_debug_locking_inited == 0) |
273 | { |
274 | fz_debug_locking_inited = 1; |
275 | fz_lock_program_start = ms_clock(); |
276 | atexit(dump_lock_times); |
277 | } |
278 | #endif |
279 | } |
280 | ctx->locks.unlock(ctx->locks.user, FZ_LOCK_ALLOC); |
281 | if (gottit) |
282 | return i; |
283 | } |
284 | } |
285 | return -1; |
286 | } |
287 | |
288 | void |
289 | fz_assert_lock_held(fz_context *ctx, int lock) |
290 | { |
291 | int idx; |
292 | |
293 | if (ctx->locks.lock != fz_lock_default) |
294 | return; |
295 | |
296 | idx = find_context(ctx); |
297 | if (idx < 0) |
298 | return; |
299 | |
300 | if (fz_locks_debug[idx][lock] == 0) |
301 | fprintf(stderr, "Lock %d not held when expected\n" , lock); |
302 | } |
303 | |
304 | void |
305 | fz_assert_lock_not_held(fz_context *ctx, int lock) |
306 | { |
307 | int idx; |
308 | |
309 | if (ctx->locks.lock != fz_lock_default) |
310 | return; |
311 | |
312 | idx = find_context(ctx); |
313 | if (idx < 0) |
314 | return; |
315 | |
316 | if (fz_locks_debug[idx][lock] != 0) |
317 | fprintf(stderr, "Lock %d held when not expected\n" , lock); |
318 | } |
319 | |
320 | void fz_lock_debug_lock(fz_context *ctx, int lock) |
321 | { |
322 | int i, idx; |
323 | |
324 | if (ctx->locks.lock != fz_lock_default) |
325 | return; |
326 | |
327 | idx = find_context(ctx); |
328 | if (idx < 0) |
329 | return; |
330 | |
331 | if (fz_locks_debug[idx][lock] != 0) |
332 | { |
333 | fprintf(stderr, "Attempt to take lock %d when held already!\n" , lock); |
334 | } |
335 | for (i = lock-1; i >= 0; i--) |
336 | { |
337 | if (fz_locks_debug[idx][i] != 0) |
338 | { |
339 | fprintf(stderr, "Lock ordering violation: Attempt to take lock %d when %d held already!\n" , lock, i); |
340 | } |
341 | } |
342 | fz_locks_debug[idx][lock] = 1; |
343 | #ifdef FITZ_DEBUG_LOCKING_TIMES |
344 | fz_lock_taken[idx][lock] = ms_clock(); |
345 | #endif |
346 | } |
347 | |
348 | void fz_lock_debug_unlock(fz_context *ctx, int lock) |
349 | { |
350 | int idx; |
351 | |
352 | if (ctx->locks.lock != fz_lock_default) |
353 | return; |
354 | |
355 | idx = find_context(ctx); |
356 | if (idx < 0) |
357 | return; |
358 | |
359 | if (fz_locks_debug[idx][lock] == 0) |
360 | { |
361 | fprintf(stderr, "Attempt to release lock %d when not held!\n" , lock); |
362 | } |
363 | fz_locks_debug[idx][lock] = 0; |
364 | #ifdef FITZ_DEBUG_LOCKING_TIMES |
365 | fz_lock_time[idx][lock] += ms_clock() - fz_lock_taken[idx][lock]; |
366 | #endif |
367 | } |
368 | |
369 | #endif |
370 | |