Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
1
Contents
2
========
3
4
* Basic MuPDF usage example
5
* Common function arguments
6
* Error Handling
7
* Multi-threading
8
* Cloning the context
9
* Bound contexts
10
11
Basic MuPDF usage example
12
=========================
13
14
For an example of how to use MuPDF in the most basic way, see
15
doc/example.c. To limit the complexity and give an easier introduction
16
this code has no error handling at all, but any serious piece of code
17
using MuPDF should use the error handling strategies described below.
18
19
Common function arguments
20
=========================
21
22
Most functions in MuPDFs interface take a context argument.
23
24
A context contains global state used by MuPDF inside functions when
25
parsing or rendering pages of the document. It contains for example:
26
27
an exception stack (see error handling below),
28
29
a memory allocator (allowing for custom allocators)
30
31
a resource store (for caching of images, fonts, etc.)
32
33
a set of locks and (un-)locking functions (for multi-threading)
34
35
Without the set of locks and accompanying functions the context and
36
its proxies may only be used in a single-threaded application.
37
38
Error handling
39
==============
40
41
MuPDF uses a set of exception handling macros to simplify error return
42
and cleanup. Conceptually, they work a lot like C++'s try/catch
43
system, but do not require any special compiler support.
44
45
The basic formulation is as follows:
46
47
fz_try(ctx)
48
{
49
// Try to perform a task. Never 'return', 'goto' or
50
// 'longjmp' out of here. 'break' may be used to
51
// safely exit (just) the try block scope.
52
}
53
fz_always(ctx)
54
{
55
// Any code here is always executed, regardless of
56
// whether an exception was thrown within the try or
57
// not. Never 'return', 'goto' or longjmp out from
58
// here. 'break' may be used to safely exit (just) the
59
// always block scope.
60
}
61
fz_catch(ctx)
62
{
63
// This code is called (after any always block) only
64
// if something within the fz_try block (including any
65
// functions it called) threw an exception. The code
66
// here is expected to handle the exception (maybe
67
// record/report the error, cleanup any stray state
68
// etc) and can then either exit the block, or pass on
69
// the exception to a higher level (enclosing) fz_try
70
// block (using fz_throw, or fz_rethrow).
71
}
72
73
The fz_always block is optional, and can safely be omitted.
74
75
The macro based nature of this system has 3 main limitations:
76
77
1) Never return from within try (or 'goto' or longjmp out of it).
78
This upsets the internal housekeeping of the macros and will
79
cause problems later on. The code will detect such things
80
happening, but by then it is too late to give a helpful error
81
report as to where the original infraction occurred.
82
83
2) The fz_try(ctx) { ... } fz_always(ctx) { ... } fz_catch(ctx) { ... }
84
is not one atomic C statement. That is to say, if you do:
85
86
if (condition)
87
fz_try(ctx) { ... }
88
fz_catch(ctx) { ... }
89
90
then you will not get what you want. Use the following instead:
91
92
if (condition) {
93
fz_try(ctx) { ... }
94
fz_catch(ctx) { ... }
95
}
96
97
3) The macros are implemented using setjmp and longjmp, and so
98
the standard C restrictions on the use of those functions
99
apply to fz_try/fz_catch too. In particular, any "truly local"
100
variable that is set between the start of fz_try and something
101
in fz_try throwing an exception may become undefined as part
102
of the process of throwing that exception.
103
104
As a way of mitigating this problem, we provide an fz_var()
105
macro that tells the compiler to ensure that that variable is
106
not unset by the act of throwing the exception.
107
108
A model piece of code using these macros then might be:
109
110
house build_house(plans *p)
111
{
112
material m = NULL;
113
walls w = NULL;
114
roof r = NULL;
115
house h = NULL;
116
tiles t = make_tiles();
117
118
fz_var(w);
119
fz_var(r);
120
fz_var(h);
121
122
fz_try(ctx)
123
{
124
fz_try(ctx)
125
{
126
m = make_bricks();
127
}
128
fz_catch(ctx)
129
{
130
// No bricks available, make do with straw?
131
m = make_straw();
132
}
133
w = make_walls(m, p);
134
r = make_roof(m, t);
135
// Note, NOT: return combine(w,r);
136
h = combine(w, r);
137
}
138
fz_always(ctx)
139
{
140
drop_walls(w);
141
drop_roof(r);
142
drop_material(m);
143
drop_tiles(t);
144
}
145
fz_catch(ctx)
146
{
147
fz_throw(ctx, "build_house failed");
148
}
149
return h;
150
}
151
152
Things to note about this:
153
154
a) If make_tiles throws an exception, this will immediately be
155
handled by some higher level exception handler. If it
156
succeeds, t will be set before fz_try starts, so there is no
157
need to fz_var(t);
158
159
b) We try first off to make some bricks as our building material.
160
If this fails, we fall back to straw. If this fails, we'll end
161
up in the fz_catch, and the process will fail neatly.
162
163
c) We assume in this code that combine takes new reference to
164
both the walls and the roof it uses, and therefore that w and
165
r need to be cleaned up in all cases.
166
167
d) We assume the standard C convention that it is safe to destroy
168
NULL things.
169
170
Multi-threading
171
===============
172
173
First off, study the basic usage example in doc/example.c and make
174
sure you understand how it works as the data structures manipulated
175
there will be refered to in this section too.
176
177
MuPDF can usefully be built into a multi-threaded application without
178
the library needing to know anything threading at all. If the library
179
opens a document in one thread, and then sits there as a 'server'
180
requesting pages and rendering them for other threads that need them,
181
then the library is only ever being called from this one thread.
182
183
Other threads can still be used to handle UI requests etc, but as far
184
as MuPDF is concerned it is only being used in a single threaded way.
185
In this instance, there are no threading issues with MuPDF at all,
186
and it can safely be used without any locking, as described in the
187
previous sections.
188
189
This section will attempt to explain how to use MuPDF in the more
190
complex case; where we genuinely want to call the MuPDF library
191
concurrently from multiple threads within a single application.
192
193
MuPDF can be invoked with a user supplied set of locking functions.
194
It uses these to take mutexes around operations that would conflict
195
if performed concurrently in multiple threads. By leaving the
196
exact implementation of locks to the caller MuPDF remains threading
197
library agnostic.
198
199
The following simple rules should be followed to ensure that
200
multi-threaded operations run smoothly:
201
202
1) "No simultaneous calls to MuPDF in different threads are
203
allowed to use the same context."
204
205
Most of the time it is simplest to just use a different
206
context for every thread; just create a new context at the
207
same time as you create the thread. For more details see
208
"Cloning the context" below.
209
210
2) "No simultaneous calls to MuPDF in different threads are
211
allowed to use the same document."
212
213
Only one thread can be accessing a document at a time, but
214
once display lists are created from that document, multiple
215
threads at a time can operate on them.
216
217
The document can be used from several different threads as
218
long as there are safeguards in place to prevent the usages
219
being simultaneous.
220
221
3) "No simultaneous calls to MuPDF in different threads are
222
allowed to use the same device."
223
224
Calling a device simultaneously from different threads will
225
cause it to get confused and may crash. Calling a device from
226
several different threads is perfectly acceptable as long as
227
there are safeguards in place to prevent the calls being
228
simultaneous.
229
230
So, how does a multi-threaded example differ from a non-multithreaded
231
one?
232
233
Firstly, when we create the first context, we call fz_new_context
234
as before, but the second argument should be a pointer to a set
235
of locking functions.
236
237
The calling code should provide FZ_LOCK_MAX mutexes, which will be
238
locked/unlocked by MuPDF calling the lock/unlock function pointers
239
in the supplied structure with the user pointer from the structure
240
and the lock number, i (0 <= i < FZ_LOCK_MAX). These mutexes can
241
safely be recursive or non-recursive as MuPDF only calls in a non-
242
recursive style.
243
244
To make subsequent contexts, the user should NOT call fz_new_context
245
again (as this will fail to share important resources such as the
246
store and glyphcache), but should rather call fz_clone_context.
247
Each of these cloned contexts can be freed by fz_free_context as
248
usual. They will share the important data structures (like store,
249
glyph cache etc) with the original context, but will have their
250
own exception stacks.
251
252
To open a document, call fz_open_document as usual, passing a context
253
and a filename. It is important to realise that only one thread at a
254
time can be accessing the documents itself.
255
256
This means that only one thread at a time can perform operations such
257
as fetching a page, or rendering that page to a display list. Once a
258
display list has been obtained however, it can be rendered from any
259
other thread (or even from several threads simultaneously, giving
260
banded rendering).
261
262
This means that an implementer has 2 basic choices when constructing
263
an application to use MuPDF in multi-threaded mode. Either he can
264
construct it so that a single nominated thread opens the document
265
and then acts as a 'server' creating display lists for other threads
266
to render, or he can add his own mutex around calls to mupdf that
267
use the document. The former is likely to be far more efficient in
268
the long run.
269
270
For an example of how to do multi-threading see doc/multi-threaded.c
271
which has a main thread and one rendering thread per page.
272
273
Cloning the context
274
===================
275
276
As described above, every context contains an exception stack which is
277
manipulated during the course of nested fz_try/fz_catches. For obvious
278
reasons the same exception stack cannot be used from more than one
279
thread at a time.
280
281
If, however, we simply created a new context (using fz_new_context) for
282
every thread, we would end up with separate stores/glyph caches etc,
283
which is not (generally) what is desired. MuPDF therefore provides a
284
mechanism for "cloning" a context. This creates a new context that
285
shares everything with the given context, except for the exception
286
stack.
287
288
A commonly used general scheme is therefore to create a 'base' context
289
at program start up, and to clone this repeatedly to get new contexts
290
that can be used on new threads.
291
292