1/*
2 * Some additional glue functions for using Harfbuzz with
3 * custom allocators.
4 */
5
6#include "mupdf/fitz.h"
7#include "fitz-imp.h"
8
9#include "hb.h"
10
11#include <assert.h>
12
13/* Harfbuzz has some major design flaws (for our usage
14 * at least).
15 *
16 * By default it uses malloc and free as the underlying
17 * allocators. Thus in its default form we cannot get
18 * a record (much less control) over how much allocation
19 * is done.
20 *
21 * Harfbuzz does allow build options to control where
22 * malloc and free go - in particular we point them at
23 * fz_hb_malloc and fz_hb_free in our implementation.
24 * Unfortunately, this has problems too.
25 *
26 * Firstly, there is no mechanism for getting a context
27 * through the call. Most other libraries allow us to
28 * pass a "void *" value in, and have it passed through
29 * to arrive unchanged at the allocator functions.
30 *
31 * Without this rudimentary functionality, we are forced
32 * to serialise all access to Harfbuzz.
33 *
34 * By taking a mutex around all calls to Harfbuzz, we
35 * can use a static of our own to get a fz_context safely
36 * through to the allocators. This obviously costs us
37 * performance in the multi-threaded case.
38 *
39 * This does not protect us against the possibility of
40 * other people calling harfbuzz; for instance, if we
41 * link MuPDF into an app that either calls harfbuzz
42 * itself, or uses another library that calls harfbuzz,
43 * there is no guarantee that that library will take
44 * the same lock while calling harfbuzz. This leaves
45 * us open to the possibility of crashes. The only
46 * way around this would be to use completely separate
47 * harfbuzz instances.
48 *
49 * In order to ensure that allocations throughout mupdf
50 * are done consistently, we get harfbuzz to call our
51 * own fz_hb_malloc/realloc/calloc/free functions that
52 * call down to fz_malloc/realloc/calloc/free. These
53 * require context variables, so we get our fz_hb_lock
54 * and unlock to set these. Any attempt to call through
55 * without setting these will be detected.
56 *
57 * It is therefore vital that any fz_lock/fz_unlock
58 * handlers are shared between all the fz_contexts in
59 * use at a time.
60 *
61 * Secondly, Harfbuzz allocates some 'internal' memory
62 * on the first call, and leaves this linked from static
63 * variables. By default, this data is never freed back.
64 * This means it is impossible to clear the library back
65 * to a default state. Memory debugging will always show
66 * Harfbuzz as having leaked a set amount of memory.
67 *
68 * There is a mechanism in Harfbuzz for freeing these
69 * blocks - that of building with HAVE_ATEXIT. This
70 * causes the blocks to be freed back on exit, but a)
71 * this doesn't reset the fz_context value, so we can't
72 * free them correctly, and b) any fz_context value it
73 * did keep would already have been closed down due to
74 * the program exit.
75 *
76 * In addition, because of these everlasting blocks, we
77 * cannot safely call Harfbuzz after we close down any
78 * allocator that Harfbuzz has been using (because
79 * Harfbuzz may still be holding pointers to data within
80 * that allocators managed space).
81 *
82 * There is nothing we can do about the leaking blocks
83 * except to add some hacks to our memory debugging
84 * library to allow it to suppress the blocks that
85 * harfbuzz leaks.
86 *
87 * Consequently, we leave them to leak, and warn Memento
88 * about this.
89 */
90
91/* Potentially we can write different versions
92 * of get_context and set_context for different
93 * threading systems.
94 *
95 * This simple version relies on harfbuzz never
96 * trying to make 2 allocations at once on
97 * different threads. The only way that can happen
98 * is when one of those other threads is someone
99 * outside MuPDF calling harfbuzz while MuPDF
100 * is running. This will cause us such huge
101 * problems that for now, we'll just forbid it.
102 */
103
104static fz_context *fz_hb_secret = NULL;
105
106static void set_hb_context(fz_context *ctx)
107{
108 fz_hb_secret = ctx;
109}
110
111static fz_context *get_hb_context(void)
112{
113 return fz_hb_secret;
114}
115
116/*
117 Lock against Harfbuzz being called
118 simultaneously in several threads. This reuses
119 FZ_LOCK_FREETYPE.
120*/
121void fz_hb_lock(fz_context *ctx)
122{
123 fz_lock(ctx, FZ_LOCK_FREETYPE);
124
125 set_hb_context(ctx);
126}
127
128/*
129 Unlock after a Harfbuzz call. This reuses
130 FZ_LOCK_FREETYPE.
131*/
132void fz_hb_unlock(fz_context *ctx)
133{
134 set_hb_context(NULL);
135
136 fz_unlock(ctx, FZ_LOCK_FREETYPE);
137}
138
139void *fz_hb_malloc(size_t size)
140{
141 fz_context *ctx = get_hb_context();
142
143 assert(ctx != NULL);
144
145 return fz_malloc_no_throw(ctx, size);
146}
147
148void *fz_hb_calloc(size_t n, size_t size)
149{
150 fz_context *ctx = get_hb_context();
151
152 assert(ctx != NULL);
153
154 return fz_calloc_no_throw(ctx, n, size);
155}
156
157void *fz_hb_realloc(void *ptr, size_t size)
158{
159 fz_context *ctx = get_hb_context();
160
161 assert(ctx != NULL);
162
163 return fz_realloc_no_throw(ctx, ptr, size);
164}
165
166void fz_hb_free(void *ptr)
167{
168 fz_context *ctx = get_hb_context();
169
170 assert(ctx != NULL);
171
172 fz_free(ctx, ptr);
173}
174