Bug 790810 - Nested layer groups lead to a deadlock with multithreading

In gimp_operation_buffer_source_validate_process(), align the ROI
to the tile grid *before* intersecting it with the validate-
handler's dirty region.  This is necessary since, even though
subsequent operations will only read data within the ROI, the
entire tiles containing the ROI will be fetched, resulting in an
area potentially greater than the ROI.  We need to validate this
area in advance, or else it will be validated as part of the
subsequent operations, which can lead into the same deadlock we're
trying to prevent.
This commit is contained in:
Ell 2018-03-26 18:51:24 -04:00
parent e4d2de7dde
commit caa3a98f04

View file

@ -64,10 +64,6 @@ static void gimp_operation_buffer_source_validate_buffer_changed (Ge
const GeglRectangle *rect,
gpointer data);
static void gimp_operation_buffer_source_validate_buffer_validate (GimpOperationBufferSourceValidate *buffer_source_validate,
const cairo_rectangle_int_t *rect,
gint level);
G_DEFINE_TYPE (GimpOperationBufferSourceValidate, gimp_operation_buffer_source_validate,
GEGL_TYPE_OPERATION_SOURCE)
@ -255,43 +251,74 @@ gimp_operation_buffer_source_validate_process (GeglOperation *operation,
*/
if (n_threads > 1)
{
gint shift_x;
gint shift_y;
gint tile_width;
gint tile_height;
cairo_rectangle_int_t rect;
cairo_region_overlap_t overlap;
rect.x = result->x;
rect.y = result->y;
rect.width = result->width;
rect.height = result->height;
g_object_get (buffer_source_validate->buffer,
"shift-x", &shift_x,
"shift-y", &shift_y,
"tile-width", &tile_width,
"tile-height", &tile_height,
NULL);
/* align the rectangle to the tile grid */
rect.x = (gint) floor ((gdouble) (result->x + shift_x) / tile_width) * tile_width;
rect.y = (gint) floor ((gdouble) (result->y + shift_y) / tile_height) * tile_height;
rect.width = (gint) ceil ((gdouble) (result->x + result->width + shift_x) / tile_width) * tile_width - rect.x;
rect.height = (gint) ceil ((gdouble) (result->y + result->height + shift_y) / tile_height) * tile_height - rect.y;
/* check if the rectangle interescts with the dirty region */
overlap = cairo_region_contains_rectangle (validate_handler->dirty_region,
&rect);
if (overlap == CAIRO_REGION_OVERLAP_IN)
if (overlap != CAIRO_REGION_OVERLAP_OUT)
{
gimp_operation_buffer_source_validate_buffer_validate (
buffer_source_validate, &rect, level);
}
else if (overlap == CAIRO_REGION_OVERLAP_PART)
{
cairo_region_t *region;
gint n_rectangles;
gint i;
GeglBufferIterator *iter;
region = cairo_region_copy (validate_handler->dirty_region);
cairo_region_intersect_rectangle (region, &rect);
n_rectangles = cairo_region_num_rectangles (region);
for (i = 0; i < n_rectangles; i++)
/* if the rectangle is not entirely within the dirty
* region ...
*/
if (overlap == CAIRO_REGION_OVERLAP_PART)
{
cairo_region_get_rectangle (region, i, &rect);
cairo_region_t *region;
gimp_operation_buffer_source_validate_buffer_validate (
buffer_source_validate, &rect, level);
/* ... intersect it with region and use the result's
* bounds
*/
region = cairo_region_copy (validate_handler->dirty_region);
cairo_region_intersect_rectangle (region, &rect);
cairo_region_get_extents (region, &rect);
cairo_region_destroy (region);
/* realign the rectangle to the tile grid */
rect.x = (gint) floor ((gdouble) (result->x + shift_x) / tile_width) * tile_width;
rect.y = (gint) floor ((gdouble) (result->y + shift_y) / tile_height) * tile_height;
rect.width = (gint) ceil ((gdouble) (result->x + result->width + shift_x) / tile_width) * tile_width - rect.x;
rect.height = (gint) ceil ((gdouble) (result->y + result->height + shift_y) / tile_height) * tile_height - rect.y;
}
cairo_region_destroy (region);
rect.x -= shift_x;
rect.y -= shift_y;
/* iterate over the rectangle -- this implicitly causes
* validation
*/
iter = gegl_buffer_iterator_new (buffer,
GEGL_RECTANGLE (rect.x,
rect.y,
rect.width,
rect.height),
level, NULL,
GEGL_BUFFER_READ,
GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (iter));
}
}
}
@ -314,46 +341,3 @@ gimp_operation_buffer_source_validate_buffer_changed (GeglBuffer *buffe
gegl_operation_invalidate (GEGL_OPERATION (buffer_source_validate),
rect, FALSE);
}
static void
gimp_operation_buffer_source_validate_buffer_validate (GimpOperationBufferSourceValidate *buffer_source_validate,
const cairo_rectangle_int_t *rect,
gint level)
{
gint shift_x;
gint shift_y;
gint tile_width;
gint tile_height;
GeglRectangle roi;
GeglBufferIterator *iter;
g_object_get (buffer_source_validate->buffer,
"shift-x", &shift_x,
"shift-y", &shift_y,
"tile-width", &tile_width,
"tile-height", &tile_height,
NULL);
/* align rectangle to tile grid */
roi.x = (gint) floor ((gdouble) (rect->x + shift_x) / tile_width) * tile_width;
roi.y = (gint) floor ((gdouble) (rect->y + shift_y) / tile_height) * tile_height;
roi.width = (gint) ceil ((gdouble) (rect->x + rect->width + shift_x) / tile_width) * tile_width - roi.x;
roi.height = (gint) ceil ((gdouble) (rect->y + rect->height + shift_y) / tile_height) * tile_height - roi.y;
roi.x -= shift_x;
roi.y -= shift_y;
/* intersect rectangle with abyss */
gegl_rectangle_intersect (&roi, &roi,
gegl_buffer_get_abyss (buffer_source_validate->buffer));
/* iterate over rectangle -- this implicitly causes validation */
iter = gegl_buffer_iterator_new (buffer_source_validate->buffer,
&roi, level, NULL,
GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (iter));
}