Threaded Transforms
- From: Shaun McCance <shaunm gnome org>
- To: gnome-doc-devel-list gnome org
- Subject: Threaded Transforms
- Date: Tue, 27 Jun 2006 08:38:32 -0500
I'm attaching a rough first pass at a threaded
wrapper for XSLT transformations. I'm going to
be offline for a couple of days, and I know at
least Brent wanted to see this.
Basically, you make a YelpTransform struct with
some callback functions for chunks, errors, and
finalization. The YelpTransform forks off a new
thread with the libxslt stuff. Internally, it
manages a GAsyncQueue and adds idle handlers to
call your callbacks in the main thread (or, more
pedantically, in whatever thread your main loop
is running in).
Outside this module, you never see the thread or
the queue or any of that. It encapsulates the
threading completely.
This implementation is not production-quality.
Notably, freeing a running YelpTransform will
result in Very Bad Things happening. Ideally,
we'd find a way to set some sort of stop bit
in the xsltTransformContext on free and wait
for the thread to exit cleanly. The thread
itself could then clean everything up in its
dying breath if the stop bit was set. We'd
probably rename free to release, I guess.
It is not a GObject, because it's designed to
be managed from only one place. In the brave
new YelpDocument world, a YelpDocument would
create a YelpTransform to do its grunt work
(if it uses XSLT, which a document doesn't
necessarily have to). Thus, we don't really
need ref/unref, and we don't need to hook up
multiple callback functions to events.
We can probably save hideous small mallocs
with a memory pool. GLib has routines for
this. Same with thread pools.
The YelpTransformChunk bit is a stopgap so
that I could get something testable running.
The YelpDocument API calls for a universal
YelpPage object, and we'd use that. (There
are some lifecylce issues we'll have to work
through, but they aren't insurmountable.)
You want me to write more, but I really need
to get going. Compile test-transform and run
it with the full path of a DocBook file. The
output isn't very exciting (wow, yay, it can
run XSLT, whoop-de-frikkin-doo), but what's
happening underneath is new and good.
Share and enjoy,
Shaun
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/yelp/src/Makefile.am,v
retrieving revision 1.95
diff -u -r1.95 Makefile.am
--- src/Makefile.am 12 Jun 2006 05:18:34 -0000 1.95
+++ src/Makefile.am 27 Jun 2006 13:17:42 -0000
@@ -101,7 +101,7 @@
yelp_LDFLAGS = -R$(MOZILLA_HOME) $(AM_LDFLAGS)
-check_PROGRAMS = test-man-parser test-pager test-uri
+check_PROGRAMS = test-man-parser test-pager test-uri test-transform
test_man_parser_SOURCES = \
yelp-error.c yelp-error.h \
@@ -163,6 +163,16 @@
test_uri_LDADD = $(YELP_LIBS)
test_uri_LDFLAGS = $(AM_LDFLAGS)
+
+test_transform_SOURCES = \
+ yelp-debug.c yelp-debug.h \
+ yelp-error.c yelp-transform.h \
+ yelp-transform.c yelp-transform.h \
+ test-transform.c
+test_transform_CFLAGS = $(YELP_CFLAGS) $(AM_CFLAGS) $(YELP_DEFINES)
+test_transform_LDADD = $(YELP_LIBS)
+test_transform_LDFLAGS = $(AM_LDFLAGS)
+
@INTLTOOL_SERVER_RULE@
Index: src/yelp-error.c
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-error.c,v
retrieving revision 1.7
diff -u -r1.7 yelp-error.c
--- src/yelp-error.c 22 Feb 2005 23:44:27 -0000 1.7
+++ src/yelp-error.c 27 Jun 2006 13:17:42 -0000
@@ -67,3 +67,27 @@
else
return error->message;
}
+
+YelpError *
+yelp_error_new (gchar *title, gchar *format, ...)
+{
+ YelpError *error;
+ va_list args;
+
+ error = g_new0 (YelpError, 1);
+ error->title = g_strdup (title);
+
+ va_start (args, format);
+ error->text = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ return error;
+}
+
+void
+yelp_error_free (YelpError *error)
+{
+ g_free (error->title);
+ g_free (error->text);
+ g_free (error);
+}
Index: src/yelp-error.h
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-error.h,v
retrieving revision 1.11
diff -u -r1.11 yelp-error.h
--- src/yelp-error.h 2 Jan 2005 06:06:26 -0000 1.11
+++ src/yelp-error.h 27 Jun 2006 13:17:42 -0000
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* Copyright (C) 2002 Mikael Hallendal <micke imendio com>
*
@@ -32,11 +32,22 @@
YELP_ERROR_FORMAT, /* Format is not supported */
YELP_ERROR_IO, /* Error in IO */
YELP_ERROR_PROC /* Error processing the document */
-} YelpError;
+} YelpErrorCode;
GQuark yelp_error_quark (void) G_GNUC_CONST;
const gchar * yelp_error_get_primary (GError *error);
const gchar * yelp_error_get_secondary (GError *error);
+
+typedef struct _YelpError YelpError;
+struct _YelpError {
+ gchar *title;
+ gchar *text;
+};
+
+YelpError * yelp_error_new (gchar *title,
+ gchar *format,
+ ...);
+void yelp_error_free (YelpError *error);
#endif /* __YELP_ERROR_H__ */
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* Copyright (C) 2002 Shaun McCance
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
*
* Author: Shaun McCance <shaunm gnome org>
*/
#include <glib.h>
#include "yelp-error.h"
#include "yelp-transform.h"
static void chunk_func (YelpTransform *transform,
YelpTransformChunk *chunk,
gpointer user_data);
static void error_func (YelpTransform *transform,
YelpError *error,
gpointer user_data);
static void final_func (YelpTransform *transform,
gpointer user_data);
GMainLoop *loop;
static void
chunk_func (YelpTransform *transform,
YelpTransformChunk *chunk,
gpointer user_data)
{
printf ("CHUNK: %s\n", chunk->id);
printf (" %s\n", chunk->title);
}
static void
error_func (YelpTransform *transform,
YelpError *error,
gpointer user_data)
{
printf ("ERROR: %s\n", error->title);
}
static void
final_func (YelpTransform *transform,
gpointer user_data)
{
printf ("FINAL\n");
g_main_loop_quit (loop);
}
gint
main (gint argc, gchar **argv)
{
xmlParserCtxtPtr parser;
xmlDocPtr doc;
YelpTransform *transform;
gchar **params;
g_thread_init (NULL);
if (argc < 2) {
g_error ("Usage: test-transform file\n");
return 1;
}
params = g_new0 (gchar *, 7);
params[0] = "db.chunk.extension";
params[1] = "\"\"";
params[2] = "db.chunk.info_basename";
params[3] = "\"x-yelp-titlepage\"";
params[4] = "db.chunk.max_depth";
params[5] = "2";
params[6] = NULL;
transform = yelp_transform_new (DATADIR"/yelp/xslt/db2html.xsl",
chunk_func,
error_func,
final_func,
NULL);
parser = xmlNewParserCtxt ();
doc = xmlCtxtReadFile (parser,
argv[1],
NULL,
XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
XML_PARSE_NOENT | XML_PARSE_NONET );
xmlXIncludeProcessFlags (doc,
XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
XML_PARSE_NOENT | XML_PARSE_NONET );
yelp_transform_start (transform, doc, params);
loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run (loop);
return 0;
}
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* Copyright (C) 2003-2006 Shaun McCance <shaunm gnome org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Shaun McCance <shaunm gnome org>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <glib/gi18n.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/xinclude.h>
#include <libxslt/xslt.h>
#include <libxslt/templates.h>
#include <libxslt/transform.h>
#include <libxslt/extensions.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/xsltutils.h>
#include "yelp-debug.h"
#include "yelp-error.h"
#include "yelp-transform.h"
#define YELP_NAMESPACE "http://www.gnome.org/yelp/ns"
static void transform_run (YelpTransform *transform);
static void transform_set_error (YelpTransform *transform,
YelpError *error);
static gboolean transform_chunk (YelpTransform *transform);
static gboolean transform_error (YelpTransform *transform);
static gboolean transform_final (YelpTransform *transform);
static void xslt_yelp_document (xsltTransformContextPtr ctxt,
xmlNodePtr node,
xmlNodePtr inst,
xsltStylePreCompPtr comp);
static void xslt_yelp_cache (xsltTransformContextPtr ctxt,
xmlNodePtr node,
xmlNodePtr inst,
xsltStylePreCompPtr comp);
/******************************************************************************/
YelpTransform
*yelp_transform_new (gchar *stylesheet,
YelpTransformChunkFunc chunk_func,
YelpTransformErrorFunc error_func,
YelpTransformFinalFunc final_func,
gpointer user_data)
{
YelpTransform *transform;
transform = g_new0 (YelpTransform, 1);
transform->stylesheet = xsltParseStylesheetFile (BAD_CAST stylesheet);
if (!transform->stylesheet) {
transform->error =
yelp_error_new (_("Invalid Stylesheet"),
_("The XSLT stylesheet ‘%s’ is either missing, or it is "
"not valid."),
stylesheet);
transform_error (transform);
return NULL;
}
transform->chunk_func = chunk_func;
transform->error_func = error_func;
transform->final_func = final_func;
transform->queue = g_async_queue_new ();
transform->user_data = user_data;
return transform;
}
void
yelp_transform_free (YelpTransform *transform)
{
if (transform->outputDoc)
xmlFreeDoc (transform->outputDoc);
if (transform->stylesheet)
xsltFreeStylesheet (transform->stylesheet);
if (transform->context)
xsltFreeTransformContext (transform->context);
g_strfreev (transform->params);
if (transform->error)
yelp_error_free (transform->error);
g_async_queue_unref (transform->queue);
g_free (transform);
}
void
yelp_transform_start (YelpTransform *transform,
xmlDocPtr document,
gchar **params)
{
transform->inputDoc = document;
transform->context = xsltNewTransformContext (transform->stylesheet,
transform->inputDoc);
if (!transform->context) {
YelpError *error =
yelp_error_new (_("Broken Transformation"),
_("An unknown error occurred while attempting to "
"transform the document."));
transform_set_error (transform, error);
return;
}
transform->params = g_strdupv (params);
transform->context->_private = transform;
xsltRegisterExtElement (transform->context,
BAD_CAST "document",
BAD_CAST YELP_NAMESPACE,
(xsltTransformFunction) xslt_yelp_document);
xsltRegisterExtElement (transform->context,
BAD_CAST "cache",
BAD_CAST YELP_NAMESPACE,
(xsltTransformFunction) xslt_yelp_cache);
transform->thread = g_thread_create ((GThreadFunc) transform_run,
transform, FALSE, NULL);
}
void
yelp_transform_chunk_free (YelpTransformChunk *chunk)
{
g_free (chunk->id);
g_free (chunk->title);
g_free (chunk->contents);
g_free (chunk);
}
/******************************************************************************/
static void
transform_run (YelpTransform *transform)
{
transform->outputDoc = xsltApplyStylesheetUser (transform->stylesheet,
transform->inputDoc,
(const char **) transform->params,
NULL, NULL,
transform->context);
// FIXME: do something with outputDoc?
// FIXME: check for anything remaining on the queue
g_idle_add ((GSourceFunc) transform_final, transform);
}
static void
transform_set_error (YelpTransform *transform,
YelpError *error)
{
if (transform->error)
yelp_error_free (transform->error);
transform->error = error;
g_idle_add ((GSourceFunc) transform_error, transform);
}
static gboolean
transform_chunk (YelpTransform *transform)
{
YelpTransformChunk *chunk;
chunk = (YelpTransformChunk *) g_async_queue_try_pop (transform->queue);
if (chunk) {
if (transform->chunk_func)
transform->chunk_func (transform, chunk, transform->user_data);
else
yelp_transform_chunk_free (chunk);
}
return FALSE;
}
static gboolean
transform_error (YelpTransform *transform)
{
YelpError *error;
error = transform->error;
transform->error = NULL;
if (transform->error_func)
transform->error_func (transform, error, transform->user_data);
else
yelp_error_free (error);
return FALSE;
}
static gboolean
transform_final (YelpTransform *transform)
{
if (transform->final_func)
transform->final_func (transform, transform->user_data);
return FALSE;
}
/******************************************************************************/
static void
xslt_yelp_document (xsltTransformContextPtr ctxt,
xmlNodePtr node,
xmlNodePtr inst,
xsltStylePreCompPtr comp)
{
YelpTransform *transform;
YelpTransformChunk *chunk;
xmlChar *page_id = NULL;
xmlChar *page_title = NULL;
xmlChar *page_buf;
gint buf_size;
xsltStylesheetPtr style = NULL;
const char *old_outfile;
xmlDocPtr new_doc = NULL;
xmlDocPtr old_doc;
xmlNodePtr old_insert;
xmlNodePtr cur;
if (!ctxt || !node || !inst || !comp)
return;
transform = (YelpTransform *) ctxt->_private;
debug_print (DB_FUNCTION, "entering\n");
page_id = xsltEvalAttrValueTemplate (ctxt, inst,
(const xmlChar *) "href",
NULL);
if (page_id == NULL) {
xsltTransformError (ctxt, NULL, inst,
_("No href attribute found on yelp:document"));
/* FIXME: put a real error here */
goto done;
}
debug_print (DB_FUNCTION, " page_id = \"%s\"\n", page_id);
old_outfile = ctxt->outputFile;
old_doc = ctxt->output;
old_insert = ctxt->insert;
ctxt->outputFile = (const char *) page_id;
style = xsltNewStylesheet ();
if (style == NULL) {
xsltTransformError (ctxt, NULL, inst,
_("Out of memory"));
goto done;
}
style->omitXmlDeclaration = TRUE;
new_doc = xmlNewDoc (BAD_CAST "1.0");
new_doc->charset = XML_CHAR_ENCODING_UTF8;
new_doc->dict = ctxt->dict;
xmlDictReference (new_doc->dict);
ctxt->output = new_doc;
ctxt->insert = (xmlNodePtr) new_doc;
xsltApplyOneTemplate (ctxt, node, inst->children, NULL, NULL);
xsltSaveResultToString (&page_buf, &buf_size, new_doc, style);
ctxt->outputFile = old_outfile;
ctxt->output = old_doc;
ctxt->insert = old_insert;
if (!page_title)
page_title = BAD_CAST g_strdup (_("Unknown Page"));
chunk = g_new0 (YelpTransformChunk, 1);
chunk->id = (gchar *) page_id;
chunk->title = (gchar *) page_title;
chunk->contents = (gchar *) page_buf;
g_async_queue_push (transform->queue, chunk);
g_idle_add ((GSourceFunc) transform_chunk, transform);
done:
if (new_doc)
xmlFreeDoc (new_doc);
if (style)
xsltFreeStylesheet (style);
}
static void
xslt_yelp_cache (xsltTransformContextPtr ctxt,
xmlNodePtr node,
xmlNodePtr inst,
xsltStylePreCompPtr comp)
{
}
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* Copyright (C) 2003-2006 Shaun McCance <shaunm gnome org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Shaun McCance <shaunm gnome org>
*/
#ifndef __YELP_TRANSFORM_H__
#define __YELP_TRANSFORM_H__
#include <glib.h>
#include <libxml/tree.h>
#include <libxslt/xslt.h>
#include <libxslt/transform.h>
#include "yelp-error.h"
typedef struct _YelpTransform YelpTransform;
typedef struct _YelpTransformChunk YelpTransformChunk;
typedef void (*YelpTransformChunkFunc) (YelpTransform *transform,
YelpTransformChunk *chunk,
gpointer user_data);
typedef void (*YelpTransformErrorFunc) (YelpTransform *transform,
YelpError *error,
gpointer user_data);
typedef void (*YelpTransformFinalFunc) (YelpTransform *transform,
gpointer user_data);
struct _YelpTransform {
xmlDocPtr inputDoc;
xmlDocPtr outputDoc;
xsltStylesheetPtr stylesheet;
xsltTransformContextPtr context;
YelpTransformChunkFunc chunk_func;
YelpTransformErrorFunc error_func;
YelpTransformFinalFunc final_func;
gchar **params;
GThread *thread;
GAsyncQueue *queue;
gpointer user_data;
YelpError *error;
};
struct _YelpTransformChunk {
gchar *id;
gchar *title;
gchar *contents;
};
YelpTransform *yelp_transform_new (gchar *stylesheet,
YelpTransformChunkFunc chunk_func,
YelpTransformErrorFunc error_func,
YelpTransformFinalFunc final_func,
gpointer user_data);
void yelp_transform_free (YelpTransform *transform);
void yelp_transform_start (YelpTransform *transform,
xmlDocPtr document,
gchar **params);
void yelp_transform_chunk_free (YelpTransformChunk *chunk);
#endif /* __YELP_TRANSFORM_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]