[babl] palette: make palette conversion thread safe
- From: N/A <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [babl] palette: make palette conversion thread safe
- Date: Wed, 21 Jun 2017 18:47:12 +0000 (UTC)
commit aab1ef832fc61c5d21773eb23b17f2b906e0060e
Author: Ell <ell_se yahoo com>
Date: Wed Jun 21 13:53:08 2017 -0400
palette: make palette conversion thread safe
Conversion from RGBA u8 to an 8-bit palette format caches conversion
results in a hash table, belonging to the palette model. Currently,
manipulation of the hash table is not thread safe -- when multiple
threads convert to the same palette format concurrently, the result
may be wrong. In particular, there is a race condition when two
different colors that share the same hash are converted concurrently.
Fix this by changing the hash table layout, so that it can be
modified atomically. We assume that aligned 32-bit writes are
atomic.
Note that the new layout is only suitable for palettes with up to
256 colors, but this is all we use the hash table for ATM anyway.
Add a regression test.
babl/babl-palette.c | 26 ++++---
tests/Makefile.am | 6 +-
tests/palette-concurrency-stress-test.c | 133 +++++++++++++++++++++++++++++++
3 files changed, 152 insertions(+), 13 deletions(-)
---
diff --git a/babl/babl-palette.c b/babl/babl-palette.c
index 2f9bf8d..3c32601 100644
--- a/babl/babl-palette.c
+++ b/babl/babl-palette.c
@@ -60,8 +60,7 @@ typedef struct BablPalette
unsigned char *data; /* one linear segment of all the pixels representing the palette, in order */
double *data_double;
unsigned char *data_u8;
- int hash[HASH_TABLE_SIZE];
- unsigned int hashpx[HASH_TABLE_SIZE];
+ unsigned int hash[HASH_TABLE_SIZE];
} BablPalette;
static void
@@ -70,8 +69,7 @@ babl_palette_reset_hash (BablPalette *pal)
int i;
for (i = 0; i < HASH_TABLE_SIZE; i++)
{
- pal->hashpx[i] = ((255 << 16) | (255 << 8) | 255) + 11; /* non existant pixel */
- pal->hash[i] = -1;
+ pal->hash[i] = i + 1; /* always a miss */
}
}
@@ -80,10 +78,18 @@ babl_palette_lookup (BablPalette *pal, int r, int g, int b, int a)
{
unsigned int pixel = (r << 16) | (g << 8) | b;
int hash_index = pixel % HASH_TABLE_SIZE;
- int idx = pal->hash[hash_index];
-
- if (idx >= 0 &&
- pal->hashpx[hash_index] == pixel)
+ unsigned int hash_value = pal->hash[hash_index];
+ unsigned int hash_pixel = hash_value & 0x00ffffffu;
+ int idx = hash_value >> 24;
+
+ /* note: we're assuming the palette has no more than 256 colors, otherwise
+ * the index doesn't fit in the top 8 bits of the hash-table value. since
+ * we're only using this functions with u8 palette formats, there's no need
+ * to actually verify this, but if we add wider formats in the future, it's
+ * something to be aware of.
+ */
+
+ if (pixel == hash_pixel)
{
return idx;
}
@@ -108,11 +114,9 @@ babl_palette_lookup (BablPalette *pal, int r, int g, int b, int a)
best_idx = idx;
}
}
- pal->hash[hash_index] = best_idx;
- pal->hashpx[hash_index] = pixel;
+ pal->hash[hash_index] = ((unsigned int) best_idx << 24) | pixel;
return best_idx;
}
- return 0;
}
static BablPalette *make_pal (const Babl *format, const void *data, int count)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 61c926a..0db0ccd 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,5 +1,7 @@
if OS_UNIX
-CONCURRENCY_STRESS_TEST = concurrency-stress-test
+CONCURRENCY_STRESS_TESTS = \
+ concurrency-stress-test \
+ palette-concurrency-stress-test
endif
C_TESTS = \
@@ -23,7 +25,7 @@ C_TESTS = \
models \
cairo-RGB24 \
transparent \
- $(CONCURRENCY_STRESS_TEST)
+ $(CONCURRENCY_STRESS_TESTS)
TESTS = \
$(C_TESTS)
diff --git a/tests/palette-concurrency-stress-test.c b/tests/palette-concurrency-stress-test.c
new file mode 100644
index 0000000..806c8ce
--- /dev/null
+++ b/tests/palette-concurrency-stress-test.c
@@ -0,0 +1,133 @@
+/* babl - dynamically extendable universal pixel conversion library.
+ * Copyright (C) 2009 Martin Nordholts
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <pthread.h>
+
+#include "babl.h"
+
+
+#define N_THREADS 10
+#define N_PIXELS 1000000 /* (per thread) */
+
+
+/* should be the same as HASH_TABLE_SIZE in babl/babl-palette.c */
+#define BABL_PALETTE_HASH_TABLE_SIZE 1111
+
+
+typedef struct
+{
+ const Babl *fish;
+ unsigned char src[4 * N_PIXELS];
+ unsigned char dest[N_PIXELS];
+} ThreadContext;
+
+
+static void *
+thread_proc (void *data)
+{
+ ThreadContext *ctx = data;
+
+ babl_process (ctx->fish, ctx->src, ctx->dest, N_PIXELS);
+
+ return NULL;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ const Babl *pal;
+ const Babl *pal_format;
+ unsigned char colors[4 * N_THREADS];
+ pthread_t threads[N_THREADS];
+ ThreadContext *ctx[N_THREADS];
+ int i, j;
+ int OK = 1;
+
+ babl_init ();
+
+ /* create a palette of N_THREADS different colors, all of which have the same
+ * hash
+ */
+ pal = babl_new_palette (NULL, &pal_format, NULL);
+
+ for (i = 0; i < N_THREADS; i++)
+ {
+ unsigned char *p = &colors[4 * i];
+ unsigned int v;
+
+ v = i * BABL_PALETTE_HASH_TABLE_SIZE;
+
+ p[0] = (v >> 16) & 0xff;
+ p[1] = (v >> 8) & 0xff;
+ p[2] = (v >> 0) & 0xff;
+ p[3] = 0xff;
+ }
+
+ babl_palette_set_palette (pal, babl_format ("RGBA u8"), colors, N_THREADS);
+
+ /* initialize the thread contexts such that each thread processes a buffer
+ * containing a single, distinct color
+ */
+ for (i = 0; i < N_THREADS; i++)
+ {
+ ctx[i] = malloc (sizeof (ThreadContext));
+
+ ctx[i]->fish = babl_fish (babl_format ("RGBA u8"), pal_format);
+
+ for (j = 0; j < 4 * N_PIXELS; j++)
+ {
+ ctx[i]->src[j] = colors[4 * i + j % 4];
+ }
+ }
+
+ /* run all threads at the same time */
+ for (i = 0; i < N_THREADS; i++)
+ {
+ pthread_create (&threads[i],
+ NULL, /* attr */
+ thread_proc,
+ ctx[i]);
+ }
+
+ /* wait for them to finish */
+ for (i = 0; i < N_THREADS; i++)
+ {
+ pthread_join (threads[i],
+ NULL /* thread_return */);
+ }
+
+ /* verify the results */
+ for (i = 0; i < N_THREADS; i++)
+ {
+ for (j = 0; OK && j < N_PIXELS; j++)
+ {
+ OK = (ctx[i]->dest[j] == i);
+ }
+
+ free (ctx[i]);
+ }
+
+ babl_exit ();
+
+ return ! OK;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]