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