summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2016-12-15 19:05:53 +0000
committerRobin Watts <robin.watts@artifex.com>2016-12-19 12:56:50 +0000
commit247fda9a136ff14a33aef4c3b7404a04cf3d3752 (patch)
tree584181d4b06f5763e931011c3ed687c23be424b8
parent1c3463c27470783a5b6807a91628a71f33b91090 (diff)
downloadmupdf-247fda9a136ff14a33aef4c3b7404a04cf3d3752.tar.xz
Add MuOfficeLib functions to safely run native MuPDF ops.
It seems likely that we'll want people to able to use the MuPDF C API as well as the MuOfficeLib helper lib. We therefore need a way to get fz_context and fz_document values out of MuOfficeLib. Potential problems exist with people calling MuPDF C API functions using an fz_context that is in use elsewhere. Similarly, if an fz_document is in use in a background thread (for instance in a page render), we need to ensure that it can't be used at the same time elsewhere. We therefore provide MuOffice{Lib,Doc,Page}_run functions that allow this to happen safely. This largely insulates callers from the complexities of having to clone contexts etc, it safely ensures that exceptions cannot be propogated beyond the topmost fz_try/ fz_catch, and it ensures that appropriate locking is used.
-rw-r--r--include/mupdf/helpers/mu-office-lib.h74
-rw-r--r--source/helpers/mu-office-lib/mu-office-lib.c189
2 files changed, 258 insertions, 5 deletions
diff --git a/include/mupdf/helpers/mu-office-lib.h b/include/mupdf/helpers/mu-office-lib.h
index 9c4cd845..1c856962 100644
--- a/include/mupdf/helpers/mu-office-lib.h
+++ b/include/mupdf/helpers/mu-office-lib.h
@@ -38,6 +38,8 @@
*/
#include <stddef.h> /* For size_t */
+#include "mupdf/fitz.h" /* For fz_context/fz_document/fz_page */
+
/** Error type returned from most MuOffice functions
*
@@ -323,6 +325,24 @@ MuError MuOfficeLib_loadDocument(MuOfficeLib *mu,
/**
+ * Perform MuPDF native operations on a given MuOfficeLib
+ * instance.
+ *
+ * The function is called with an fz_context value that can
+ * be safely used (i.e. the context is cloned/dropped
+ * appropriately around the call). The function should signal
+ * errors by fz_throw-ing.
+ *
+ * @param mu the MuOfficeLib instance.
+ * @param fn the function to call to run the operations.
+ * @param arg Opaque data pointer.
+ *
+ * @return error indication - 0 for success
+ */
+MuError MuOfficeLib_run(MuOfficeLib *mu, void (*fn)(fz_context *ctx, void *arg), void *arg);
+
+
+/**
* Provide the password for a document
*
* This function should be called to provide a password with a document
@@ -430,6 +450,33 @@ MuError MuOfficeDoc_getPage( MuOfficeDoc *doc,
/**
+ * Perform MuPDF native operations on a given document.
+ *
+ * The function is called with fz_context and fz_document
+ * values that can be safely used (i.e. the context is
+ * cloned/dropped appropriately around the function, and
+ * locking is used to ensure that no other threads are
+ * simultaneously using the document). Functions can
+ * signal errors by fz_throw-ing.
+ *
+ * Due to the locking, it is best to ensure that as little
+ * time is taken here as possible (i.e. if you fetch some
+ * data and then spend a long time processing it, it is
+ * probably best to fetch the data using MuOfficeDoc_run
+ * and then process it outside). This avoids potentially
+ * blocking the UI.
+ *
+ * @param doc the document object.
+ * @param fn the function to call with fz_context/fz_document
+ * values.
+ * @param arg Opaque data pointer.
+ *
+ * @return error indication - 0 for success
+ */
+MuError MuOfficeDoc_run(MuOfficeDoc *doc, void (*fn)(fz_context *ctx, fz_document *doc, void *arg), void *arg);
+
+
+/**
* Destroy a page object
*
* Note this does not delete or remove the page from the document.
@@ -501,6 +548,33 @@ MuError MuOfficePage_getSizeForZoom( MuOfficePage *page,
/**
+ * Perform MuPDF native operations on a given page.
+ *
+ * The function is called with fz_context and fz_page
+ * values that can be safely used (i.e. the context is
+ * cloned/dropped appropriately around the function, and
+ * locking is used to ensure that no other threads are
+ * simultaneously using the document). Functions can
+ * signal errors by fz_throw-ing.
+ *
+ * Due to the locking, it is best to ensure that as little
+ * time is taken here as possible (i.e. if you fetch some
+ * data and then spend a long time processing it, it is
+ * probably best to fetch the data using MuOfficePage_run
+ * and then process it outside). This avoids potentially
+ * blocking the UI.
+ *
+ * @param page the page object.
+ * @param fn the function to call with fz_context/fz_document
+ * values.
+ * @param arg Opaque data pointer.
+ *
+ * @return error indication - 0 for success
+ */
+MuError MuOfficePage_run(MuOfficePage *page, void (*fn)(fz_context *ctx, fz_page *page, void *arg), void *arg);
+
+
+/**
* Schedule the rendering of an area of document page to
* an area of a bitmap.
*
diff --git a/source/helpers/mu-office-lib/mu-office-lib.c b/source/helpers/mu-office-lib/mu-office-lib.c
index 8c253e92..e2e5f6f5 100644
--- a/source/helpers/mu-office-lib/mu-office-lib.c
+++ b/source/helpers/mu-office-lib/mu-office-lib.c
@@ -107,10 +107,21 @@ static void muoffice_unlock(void *user, int lock);
struct MuOfficeLib_s
{
fz_context *ctx;
- mu_mutex mutexes[FZ_LOCK_MAX];
+ mu_mutex mutexes[FZ_LOCK_MAX+1];
fz_locks_context locks;
};
+/*
+ We add 1 extra lock which we use in this helper to protect
+ against accessing the fz_document from multiple threads
+ inadvertently when the caller is calling 'run' or
+ 'runBackground'.
+*/
+enum
+{
+ DOCLOCK = FZ_LOCK_MAX
+};
+
static void muoffice_lock(void *user, int lock)
{
MuOfficeLib *mu = (MuOfficeLib *)user;
@@ -129,7 +140,7 @@ static void fin_muoffice_locks(MuOfficeLib *mu)
{
int i;
- for (i = 0; i < FZ_LOCK_MAX; i++)
+ for (i = 0; i < FZ_LOCK_MAX+1; i++)
mu_destroy_mutex(&mu->mutexes[i]);
}
@@ -138,7 +149,7 @@ static fz_locks_context *init_muoffice_locks(MuOfficeLib *mu)
int i;
int failed = 0;
- for (i = 0; i < FZ_LOCK_MAX; i++)
+ for (i = 0; i < FZ_LOCK_MAX+1; i++)
failed |= mu_create_mutex(&mu->mutexes[i]);
if (failed)
@@ -209,6 +220,46 @@ void MuOfficeLib_destroy(MuOfficeLib *mu)
Pal_Mem_free(mu);
}
+
+/**
+ * Perform MuPDF native operations on a given MuOfficeLib
+ * instance.
+ *
+ * The function is called with an fz_context value that can
+ * be safely used (i.e. the context is cloned/dropped
+ * appropriately around the call). The function should signal
+ * errors by fz_throw-ing.
+ *
+ * @param mu the MuOfficeLib instance.
+ * @param fn the function to call to run the operations.
+ * @param arg Opaque data pointer.
+ *
+ * @return error indication - 0 for success
+ */
+MuError MuOfficeLib_run(MuOfficeLib *mu, void (*fn)(fz_context *ctx, void *arg), void *arg)
+{
+ fz_context *ctx;
+ MuError err = MuError_OK;
+
+ if (mu == NULL)
+ return MuError_BadNull;
+ if (fn == NULL)
+ return err;
+
+ ctx = fz_clone_context(mu->ctx);
+ if (ctx == NULL)
+ return MuError_OOM;
+
+ fz_try(ctx)
+ fn(ctx, arg);
+ fz_catch(ctx)
+ err = MuError_Generic;
+
+ fz_drop_context(ctx);
+
+ return err;
+}
+
/**
* Find the type of a file given it's filename extension.
*
@@ -288,6 +339,8 @@ static void load_worker(void *arg)
return;
}
+ fz_lock(ctx, DOCLOCK);
+
fz_try(ctx)
{
doc->doc = fz_open_document(ctx, doc->path);
@@ -324,6 +377,8 @@ static void load_worker(void *arg)
err = MuOfficeDocErrorType_UnableToLoadDocument;
fail:
+ fz_unlock(ctx, DOCLOCK);
+
if (err)
doc->error(doc->cookie, err);
@@ -608,6 +663,7 @@ MuError MuOfficeDoc_getPage( MuOfficeDoc *doc,
{
MuOfficePage *page;
MuError err = MuError_OK;
+ fz_context *ctx;
if (!doc)
return MuError_BadNull;
@@ -617,12 +673,15 @@ MuError MuOfficeDoc_getPage( MuOfficeDoc *doc,
*pPage = NULL;
ensure_doc_loaded(doc);
+ ctx = doc->ctx;
page = Pal_Mem_calloc(1, sizeof(*page));
if (page == NULL)
return MuError_OOM;
- fz_try(doc->ctx)
+ fz_lock(ctx, DOCLOCK);
+
+ fz_try(ctx)
{
page->doc = doc;
page->pageNum = pageNumber;
@@ -633,15 +692,73 @@ MuError MuOfficeDoc_getPage( MuOfficeDoc *doc,
doc->pages = page;
*pPage = page;
}
- fz_catch(doc->ctx)
+ fz_catch(ctx)
{
Pal_Mem_free(page);
err = MuError_Generic;
}
+ fz_unlock(ctx, DOCLOCK);
+
+ return err;
+}
+
+
+/**
+ * Perform MuPDF native operations on a given document.
+ *
+ * The function is called with fz_context and fz_document
+ * values that can be safely used (i.e. the context is
+ * cloned/dropped appropriately around the function, and
+ * locking is used to ensure that no other threads are
+ * simultaneously using the document). Functions can
+ * signal errors by fz_throw-ing.
+ *
+ * Due to the locking, it is best to ensure that as little
+ * time is taken here as possible (i.e. if you fetch some
+ * data and then spend a long time processing it, it is
+ * probably best to fetch the data using MuOfficeDoc_run
+ * and then process it outside). This avoids potentially
+ * blocking the UI.
+ *
+ * @param doc the document object.
+ * @param fn the function to call with fz_context/fz_document
+ * values.
+ * @param arg Opaque data pointer.
+ *
+ * @return error indication - 0 for success
+ */
+MuError MuOfficeDoc_run(MuOfficeDoc *doc, void (*fn)(fz_context *ctx, fz_document *doc, void *arg), void *arg)
+{
+ fz_context *ctx;
+ MuError err = MuError_OK;
+
+ if (doc == NULL)
+ return MuError_BadNull;
+ if (fn == NULL)
+ return err;
+
+ ensure_doc_loaded(doc);
+
+ ctx = fz_clone_context(doc->mu->ctx);
+ if (ctx == NULL)
+ return MuError_OOM;
+
+ fz_lock(ctx, DOCLOCK);
+
+ fz_try(ctx)
+ fn(ctx, doc->doc, arg);
+ fz_catch(ctx)
+ err = MuError_Generic;
+
+ fz_unlock(ctx, DOCLOCK);
+
+ fz_drop_context(ctx);
+
return err;
}
+
/**
* Destroy a page object
*
@@ -803,6 +920,58 @@ MuError MuOfficePage_getSizeForZoom( MuOfficePage *page,
return MuError_OK;
}
+/**
+ * Perform MuPDF native operations on a given page.
+ *
+ * The function is called with fz_context and fz_page
+ * values that can be safely used (i.e. the context is
+ * cloned/dropped appropriately around the function, and
+ * locking is used to ensure that no other threads are
+ * simultaneously using the document). Functions can
+ * signal errors by fz_throw-ing.
+ *
+ * Due to the locking, it is best to ensure that as little
+ * time is taken here as possible (i.e. if you fetch some
+ * data and then spend a long time processing it, it is
+ * probably best to fetch the data using MuOfficePage_run
+ * and then process it outside). This avoids potentially
+ * blocking the UI.
+ *
+ * @param page the page object.
+ * @param fn the function to call with fz_context/fz_document
+ * values.
+ * @param arg Opaque data pointer.
+ *
+ * @return error indication - 0 for success
+ */
+MuError MuOfficePage_run(MuOfficePage *page, void (*fn)(fz_context *ctx, fz_page *page, void *arg), void *arg)
+{
+ fz_context *ctx;
+ MuError err = MuError_OK;
+
+ if (page == NULL)
+ return MuError_BadNull;
+ if (fn == NULL)
+ return err;
+
+ ctx = fz_clone_context(page->doc->mu->ctx);
+ if (ctx == NULL)
+ return MuError_OOM;
+
+ fz_lock(ctx, DOCLOCK);
+
+ fz_try(ctx)
+ fn(ctx, page->page, arg);
+ fz_catch(ctx)
+ err = MuError_Generic;
+
+ fz_unlock(ctx, DOCLOCK);
+
+ fz_drop_context(ctx);
+
+ return err;
+}
+
static void render_worker(void *arg)
{
MuOfficeRender *render = (MuOfficeRender *)arg;
@@ -815,17 +984,25 @@ static void render_worker(void *arg)
float scaley;
fz_matrix matrix;
fz_rect page_bounds;
+ int locked = 0;
if (ctx == NULL)
return;
fz_var(pixmap);
fz_var(dev);
+ fz_var(locked);
fz_try(ctx)
{
if (page->list == NULL)
+ {
+ fz_lock(ctx, DOCLOCK);
+ locked = 1;
page->list = fz_new_display_list_from_page(ctx, page->page);
+ locked = 0;
+ fz_unlock(ctx, DOCLOCK);
+ }
/* Make a pixmap from the bitmap */
if (!render->area_valid)
{
@@ -865,6 +1042,8 @@ static void render_worker(void *arg)
}
fz_catch(ctx)
{
+ if (locked)
+ fz_unlock(ctx, DOCLOCK);
err = MuError_Generic;
goto fail;
}