diff --git a/include/gds-render/output-renderers/gds-output-renderer.h b/include/gds-render/output-renderers/gds-output-renderer.h index 0256707..56be520 100644 --- a/include/gds-render/output-renderers/gds-output-renderer.h +++ b/include/gds-render/output-renderers/gds-output-renderer.h @@ -106,12 +106,14 @@ const char *gds_output_renderer_get_output_file(GdsOutputRenderer *renderer); * @brief Get layer settings * * This is a convenience function for getting the - * "layer-settings" property + * "layer-settings" property. This also references it. + * This is to prevent race conditions with another thread that might + * alter the layer settings before they are read out. * * @param renderer Renderer * @return Layer settings object */ -LayerSettings *gds_output_renderer_get_layer_settings(GdsOutputRenderer *renderer); +LayerSettings *gds_output_renderer_get_and_ref_layer_settings(GdsOutputRenderer *renderer); /** * @brief Set layer settings @@ -127,6 +129,21 @@ LayerSettings *gds_output_renderer_get_layer_settings(GdsOutputRenderer *rendere */ void gds_output_renderer_set_layer_settings(GdsOutputRenderer *renderer, LayerSettings *settings); +/** + * @brief Render output asynchronously + * + * This function will render in a separate thread. + * To wait for the completion of the rendering process. + * + * @note A second async thread cannot be spawned. + * + * @param renderer Output renderer + * @param cell Cell to render + * @param scale Scale + * @return 0 if successful. In case no thread can be spawned < 0 + */ +int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale); + G_END_DECLS #endif /* _GDS_OUTPUT_RENDERER_H_ */ diff --git a/output-renderers/cairo-renderer.c b/output-renderers/cairo-renderer.c index dc4e737..8755538 100644 --- a/output-renderers/cairo-renderer.c +++ b/output-renderers/cairo-renderer.c @@ -74,7 +74,7 @@ static void revert_inherited_transform(struct cairo_layer *layers) * @param origin Origin translation * @param magnification Scaling * @param flipping Mirror image on x-axis before rotating - * @param rotation Rotattion in degrees + * @param rotation Rotation in degrees * @param scale Scale the image down by. Only used for sclaing origin coordinates. Not applied to layer. */ static void apply_inherited_transform_to_all_layers(struct cairo_layer *layers, @@ -140,7 +140,9 @@ static void render_cell(struct gds_cell *cell, struct cairo_layer *layers, doubl /* Get layer renderer */ if (gfx->layer >= MAX_LAYERS) continue; - if ((cr = layers[gfx->layer].cr) == NULL) + + cr = layers[gfx->layer].cr; + if (cr == NULL) continue; /* Apply settings */ @@ -167,7 +169,6 @@ static void render_cell(struct gds_cell *cell, struct cairo_layer *layers, doubl cairo_move_to(cr, vertex->x/scale, vertex->y/scale); else cairo_line_to(cr, vertex->x/scale, vertex->y/scale); - } /* Create graphics object */ @@ -183,9 +184,7 @@ static void render_cell(struct gds_cell *cell, struct cairo_layer *layers, doubl cairo_fill(cr); break; } - - } - + } /* for gfx list */ } /** @@ -377,12 +376,13 @@ static int cairo_renderer_render_output(GdsOutputRenderer *renderer, LayerSettings *settings; GList *layer_infos = NULL; const char *output_file; + int ret; if (!c_renderer) return -2000; output_file = gds_output_renderer_get_output_file(renderer); - settings = gds_output_renderer_get_layer_settings(renderer); + settings = gds_output_renderer_get_and_ref_layer_settings(renderer); /* Set layer info list. In case of failure it remains NULL */ if (settings) @@ -393,7 +393,12 @@ static int cairo_renderer_render_output(GdsOutputRenderer *renderer, else pdf_file = output_file; - return cairo_renderer_render_cell_to_vector_file(cell, layer_infos, pdf_file, svg_file, scale); + ret = cairo_renderer_render_cell_to_vector_file(cell, layer_infos, pdf_file, svg_file, scale); + + if (settings) + g_object_unref(settings); + + return ret; } static void cairo_renderer_class_init(CairoRendererClass *klass) diff --git a/output-renderers/external-renderer.c b/output-renderers/external-renderer.c index ab752db..ba0dd0c 100644 --- a/output-renderers/external-renderer.c +++ b/output-renderers/external-renderer.c @@ -105,15 +105,20 @@ static int external_renderer_render_output(GdsOutputRenderer *renderer, LayerSettings *settings; GList *layer_infos = NULL; const char *output_file; + int ret; output_file = gds_output_renderer_get_output_file(renderer); - settings = gds_output_renderer_get_layer_settings(renderer); + settings = gds_output_renderer_get_and_ref_layer_settings(renderer); /* Set layer info list. In case of failure it remains NULL */ if (settings) layer_infos = layer_settings_get_layer_info_list(settings); - return external_renderer_render_cell(cell, layer_infos, output_file, scale, ext_renderer->shared_object_path); + ret = external_renderer_render_cell(cell, layer_infos, output_file, scale, ext_renderer->shared_object_path); + if (settings) + g_object_unref(settings); + + return ret; } static void external_renderer_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec) diff --git a/output-renderers/gds-output-renderer.c b/output-renderers/gds-output-renderer.c index 0ccf99b..75de530 100644 --- a/output-renderers/gds-output-renderer.c +++ b/output-renderers/gds-output-renderer.c @@ -33,9 +33,18 @@ #include #include +struct renderer_params { + struct gds_cell *cell; + double scale; +}; + typedef struct { gchar *output_file; LayerSettings *layer_settings; + GMutex settings_lock; + gboolean mutex_init_status; + GThread *thread; + struct renderer_params async_params; gpointer padding[11]; } GdsOutputRendererPrivate; @@ -47,6 +56,9 @@ enum { G_DEFINE_TYPE_WITH_PRIVATE(GdsOutputRenderer, gds_output_renderer, G_TYPE_OBJECT) +enum gds_output_renderer_signal_ids {THREAD_JOINED = 0, GDS_OUTPUT_RENDERER_SIGNAL_COUNT}; +static guint gds_output_renderer_signals[GDS_OUTPUT_RENDERER_SIGNAL_COUNT]; + static int gds_output_renderer_render_dummy(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale) @@ -65,6 +77,15 @@ static void gds_output_renderer_dispose(GObject *self_obj) GdsOutputRendererPrivate *priv; priv = gds_output_renderer_get_instance_private(renderer); + + if (priv->mutex_init_status) { + /* Try locking the mutex, to test if it's free */ + g_mutex_lock(&priv->settings_lock); + g_mutex_unlock(&priv->settings_lock); + g_mutex_clear(&priv->settings_lock); + priv->mutex_init_status = FALSE; + } + if (priv->output_file) g_free(priv->output_file); @@ -103,14 +124,18 @@ static void gds_output_renderer_set_property(GObject *obj, guint property_id, co switch (property_id) { case PROP_OUTPUT_FILE: + g_mutex_lock(&priv->settings_lock); if (priv->output_file) g_free(priv->output_file); priv->output_file = g_strdup(g_value_get_string(value)); + g_mutex_unlock(&priv->settings_lock); break; case PROP_LAYER_SETTINGS: + g_mutex_lock(&priv->settings_lock); g_clear_object(&priv->layer_settings); priv->layer_settings = g_value_get_object(value); g_object_ref(priv->layer_settings); + g_mutex_unlock(&priv->settings_lock); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec); @@ -139,6 +164,18 @@ static void gds_output_renderer_class_init(GdsOutputRendererClass *klass) "Object containing the layer rendering information", GDS_RENDER_TYPE_LAYER_SETTINGS, G_PARAM_READWRITE); g_object_class_install_properties(oclass, N_PROPERTIES, gds_output_renderer_properties); + + /* Setup output signals */ + gds_output_renderer_signals[THREAD_JOINED] = + g_signal_newv("thread-joined", GDS_RENDER_TYPE_OUTPUT_RENDERER, + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + NULL, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0, + NULL); } void gds_output_renderer_init(GdsOutputRenderer *self) @@ -147,7 +184,12 @@ void gds_output_renderer_init(GdsOutputRenderer *self) priv = gds_output_renderer_get_instance_private(self); + priv->layer_settings = NULL; priv->output_file = NULL; + priv->thread = NULL; + priv->mutex_init_status = TRUE; + g_mutex_init(&priv->settings_lock); + return; } @@ -182,11 +224,23 @@ const char *gds_output_renderer_get_output_file(GdsOutputRenderer *renderer) return file; } -LayerSettings *gds_output_renderer_get_layer_settings(GdsOutputRenderer *renderer) +LayerSettings *gds_output_renderer_get_and_ref_layer_settings(GdsOutputRenderer *renderer) { LayerSettings *ret = NULL; + GdsOutputRendererPrivate *priv; + + priv = gds_output_renderer_get_instance_private(renderer); + + /* Acquire settings lock */ + g_mutex_lock(&priv->settings_lock); g_object_get(renderer, "layer-settings", &ret, NULL); + /* Reference it, so it is not cleared by another thread overwriting the property */ + g_object_ref(ret); + + /* It is now safe to clear the lock */ + g_mutex_unlock(&priv->settings_lock); + return ret; } @@ -199,6 +253,7 @@ void gds_output_renderer_set_layer_settings(GdsOutputRenderer *renderer, LayerSe int gds_output_renderer_render_output(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale) { + int ret; GdsOutputRendererClass *klass; GdsOutputRendererPrivate *priv = gds_output_renderer_get_instance_private(renderer); @@ -228,8 +283,40 @@ int gds_output_renderer_render_output(GdsOutputRenderer *renderer, struct gds_ce return GDS_OUTPUT_RENDERER_GEN_ERR; } - return klass->render_output(renderer, cell, scale); + ret = klass->render_output(renderer, cell, scale); + + return ret; } +static int gds_output_renderer_async_wrapper(gpointer data) +{ + GdsOutputRenderer *renderer; + GdsOutputRendererPrivate *priv; + int ret; + + renderer = GDS_RENDER_OUTPUT_RENDERER(data); + priv = gds_output_renderer_get_instance_private(renderer); + if (!priv) + return -1000; + + if(!priv->mutex_init_status) + return -1001; + + g_mutex_lock(&priv->settings_lock); + ret = gds_output_renderer_render_output(renderer, priv->async_params.cell, priv->async_params.scale); + g_mutex_unlock(&priv->settings_lock); + + return ret; +} + +int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale) +{ + GdsOutputRendererPrivate *priv; + int ret = -1; + + priv = gds_output_renderer_get_instance_private(renderer); + + return ret; +} /** @} */ diff --git a/output-renderers/latex-renderer.c b/output-renderers/latex-renderer.c index 44198ec..de1113d 100644 --- a/output-renderers/latex-renderer.c +++ b/output-renderers/latex-renderer.c @@ -335,7 +335,7 @@ static int latex_renderer_render_output(GdsOutputRenderer *renderer, const char *output_file; output_file = gds_output_renderer_get_output_file(renderer); - settings = gds_output_renderer_get_layer_settings(renderer); + settings = gds_output_renderer_get_and_ref_layer_settings(renderer); /* Set layer info list. In case of failure it remains NULL */ if (settings) @@ -350,6 +350,10 @@ static int latex_renderer_render_output(GdsOutputRenderer *renderer, g_error("Could not open LaTeX output file"); } + if (settings) { + g_object_unref(settings); + } + return ret; }