diff options
Diffstat (limited to 'doc/overview.txt')
-rw-r--r-- | doc/overview.txt | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/doc/overview.txt b/doc/overview.txt new file mode 100644 index 00000000..25cd0d4b --- /dev/null +++ b/doc/overview.txt @@ -0,0 +1,212 @@ +Contents +======== + +* Basic MuPDF usage example +* Common function arguments +* Error Handling +* Multi-threading + +Basic MuPDF usage example +========================= + +For an example of how to use MuPDF in the most basic way, see +doc/example.c. To limit the complexity and give an easier introduction +this code has no error handling at all, but any serious piece of code +using MuPDF should use the error handling strategies described below. + +Common function arguments +========================= + +Many functions in MuPDFs interface take a context argument. + +A context contains global state used by MuPDF inside functions when +parsing or rendering pages of the document. It contains for example: + + an exception stack (see error handling below), + + a memory allocator (allowing for custom allocators) + + a resource store (for caching of images, fonts, etc.) + + a set of locks and (un-)locking functions (for multi-threading) + +Other functions in MuPDF's interface take arguments such as document, +stream and device which contain state for each type of object. Those +arguments each have a reference to a context and therefore act as +proxies for a context. + +Without the set of locks and accompanying functions the context and +its proxies may only be used in a single-threaded application. + +Error handling +============== + +MuPDF uses a set of exception handling macros to simplify error return +and cleanup. Conceptually, they work a lot like C++'s try/catch +system, but do not require any special compiler support. + +The basic formulation is as follows: + + fz_try(ctx) + { + // Try to perform a task. Never 'return', 'goto' or + // 'longjmp' out of here. 'break' may be used to + // safely exit (just) the try block scope. + } + fz_always(ctx) + { + // Any code here is always executed, regardless of + // whether an exception was thrown within the try or + // not. Never 'return', 'goto' or longjmp out from + // here. 'break' may be used to safely exit (just) the + // always block scope. + } + fz_catch(ctx) + { + // This code is called (after any always block) only + // if something within the fz_try block (including any + // functions it called) threw an exception. The code + // here is expected to handle the exception (maybe + // record/report the error, cleanup any stray state + // etc) and can then either exit the block, or pass on + // the exception to a higher level (enclosing) fz_try + // block (using fz_throw, or fz_rethrow). + } + +The fz_always block is optional, and can safely be omitted. + +The macro based nature of this system has 3 main limitations: + +1) Never return from within try (or 'goto' or longjmp out of it). + This upsets the internal housekeeping of the macros and will + cause problems later on. The code will detect such things + happening, but by then it is too late to give a helpful error + report as to where the original infraction occurred. + +2) The fz_try(ctx) { ... } fz_always(ctx) { ... } fz_catch(ctx) { ... } + is not one atomic C statement. That is to say, if you do: + + if (condition) + fz_try(ctx) { ... } + fz_catch(ctx) { ... } + + then you will not get what you want. Use the following instead: + + if (condition) { + fz_try(ctx) { ... } + fz_catch(ctx) { ... } + } + +3) The macros are implemented using setjmp and longjmp, and so + the standard C restrictions on the use of those functions + apply to fz_try/fz_catch too. In particular, any "truly local" + variable that is set between the start of fz_try and something + in fz_try throwing an exception may become undefined as part + of the process of throwing that exception. + + As a way of mitigating this problem, we provide an fz_var() + macro that tells the compiler to ensure that that variable is + not unset by the act of throwing the exception. + +A model piece of code using these macros then might be: + + house build_house(plans *p) + { + material m = NULL; + walls w = NULL; + roof r = NULL; + house h = NULL; + tiles t = make_tiles(); + + fz_var(w); + fz_var(r); + fz_var(h); + + fz_try(ctx) + { + fz_try(ctx) + { + m = make_bricks(); + } + fz_catch(ctx) + { + // No bricks available, make do with straw? + m = make_straw(); + } + w = make_walls(m, p); + r = make_roof(m, t); + // Note, NOT: return combine(w,r); + h = combine(w, r); + } + fz_always(ctx) + { + drop_walls(w); + drop_roof(r); + drop_material(m); + drop_tiles(t); + } + fz_catch(ctx) + { + fz_throw(ctx, "build_house failed"); + } + return h; + } + +Things to note about this: + +a) If make_tiles throws an exception, this will immediately be + handled by some higher level exception handler. If it + succeeds, t will be set before fz_try starts, so there is no + need to fz_var(t); + +b) We try first off to make some bricks as our building material. + If this fails, we fall back to straw. If this fails, we'll end + up in the fz_catch, and the process will fail neatly. + +c) We assume in this code that combine takes new reference to + both the walls and the roof it uses, and therefore that w and + r need to be cleaned up in all cases. + +d) We assume the standard C convention that it is safe to destroy + NULL things. + +Multi-threading +=============== + +First off, study the basic usage example in doc/example.c and make +sure you understand how it works as it will be the referenced in this +section too. + +There are two variations of how to create multi-threaded applications: + +1) lock-less operation -- in which one thread is requesting pages + to be drawn and responding to user interface actions, while + another thread is dedicated to drawing pages. In this scenario + only one thread owns and manipulates the context and document + at any one time. + +2) using locking -- where one thread is requesting pages to be + draw and responding to user interface actions, while several + threads may be drawing pages. In this scenario each thread has + its own context but they share some global state, for example + the resource store. An additional constraint + +The usage example starts by getting a context from fz_new_context with +standard memory allocation functions, default resource store size and, +crucially, no locking. + +In a multi-threaded application every thread must have a context. Or +more specifically, each context can only be used from one thread at a +time. When starting another thread, do NOT call fz_new_context again; +instead call fz_clone_context. This creates a context sharing the +memory allocator, resource store etc. + + + + +så utan lås: en gui-tråd som visa progress, en tråd som renderar +med lås: en gui-tråd som request:ar och flera trådar som renderar + +having fitz level display list objects created in +one thread, consumed as read-only in multiple threads, with locked access +around a few shared caches |