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