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
22static void *
23do_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
42static void *
43do_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
62void *
63fz_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
74void *
75fz_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
82void *
83fz_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
97void *
98fz_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
111void *
112fz_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
125void *
126fz_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
136void
137fz_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
147char *
148fz_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
156static void *
157fz_malloc_default(void *opaque, size_t size)
158{
159 return malloc(size);
160}
161
162static void *
163fz_realloc_default(void *opaque, void *old, size_t size)
164{
165 return realloc(old, size);
166}
167
168static void
169fz_free_default(void *opaque, void *ptr)
170{
171 free(ptr);
172}
173
174fz_alloc_context fz_alloc_default =
175{
176 NULL,
177 fz_malloc_default,
178 fz_realloc_default,
179 fz_free_default
180};
181
182static void
183fz_lock_default(void *user, int lock)
184{
185}
186
187static void
188fz_unlock_default(void *user, int lock)
189{
190}
191
192fz_locks_context fz_locks_default =
193{
194 NULL,
195 fz_lock_default,
196 fz_unlock_default
197};
198
199#ifdef FITZ_DEBUG_LOCKING
200
201enum
202{
203 FZ_LOCK_DEBUG_CONTEXT_MAX = 100
204};
205
206fz_context *fz_lock_debug_contexts[FZ_LOCK_DEBUG_CONTEXT_MAX];
207int fz_locks_debug[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX];
208
209#ifdef FITZ_DEBUG_LOCKING_TIMES
210
211int fz_debug_locking_inited = 0;
212int fz_lock_program_start;
213int fz_lock_time[FZ_LOCK_DEBUG_CONTEXT_MAX][FZ_LOCK_MAX] = { { 0 } };
214int 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. */
218static 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
229static 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
248static 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
288void
289fz_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
304void
305fz_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
320void 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
348void 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