diff --git a/ChangeLog b/ChangeLog index ffb5abdb..3c317bcf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 25/4/15 started 8.0.1 - fix some compiler warnings - work around a glib bug that can cause segv under load +- add some notes on threading to the docs 11/2/15 started 8.0 - remove old doc stuff, lots of doc improvements diff --git a/doc/Makefile.am b/doc/Makefile.am index 9e8da204..14e76f0b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -133,6 +133,7 @@ HTML_IMAGES = \ content_files = \ using-command-line.xml \ using-C.xml \ + using-threads.xml \ using-python.xml \ using-cpp.xml \ extending.xml \ @@ -146,6 +147,7 @@ content_files = \ expand_content_files = \ using-command-line.xml \ using-C.xml \ + using-threads.xml \ using-python.xml \ using-cpp.xml \ extending.xml \ diff --git a/doc/libvips-docs.xml b/doc/libvips-docs.xml index e09aa138..314dd0e5 100644 --- a/doc/libvips-docs.xml +++ b/doc/libvips-docs.xml @@ -9,7 +9,7 @@ VIPS Reference Manual - For VIPS 8.0.0. + For VIPS 8.0.1. The latest version of this documentation can be found on the VIPS website. @@ -38,6 +38,7 @@ + diff --git a/doc/libvips-docs.xml.in b/doc/libvips-docs.xml.in index 3a83ea41..5c25f04b 100644 --- a/doc/libvips-docs.xml.in +++ b/doc/libvips-docs.xml.in @@ -38,6 +38,7 @@ + diff --git a/doc/using-threads.xml b/doc/using-threads.xml new file mode 100644 index 00000000..7ffe3880 --- /dev/null +++ b/doc/using-threads.xml @@ -0,0 +1,196 @@ + + + + + + VIPS and threads + 3 + VIPS Library + + + + Using VIPS + VIPS and threading + + + + Introduction + + This section tries to summarise the rules for threaded programs using + libvips. Generally, libvips is threaded and thread-safe, with a few + exceptions. + + + + + Images + + On startup, you need to call VIPS_INIT() single-threaded. After that, + you can freely create images in any thread and read them in any other + thread. See the example at the end of this chapter. + Note that results can also be shared between threads for you by the vips + operation cache. + + + + When libvips calculates an image, by default it will use as many + threads as you have CPU cores. Use vips_concurrency_set() to change this. + + + + The exception is the drawing operators, such as vips_draw_circle(). + These operations modify their image argument, so you can't call them on + the same image from more than one thread. Reading from an iamge while + another thread is writing to it with one of the draw operations will + obviously also fail. + + + + + + Error handling + + + + + + + Using #VipsRegion between threads + + + + + + Example + + +VIPS and threads example + +/* Read from many threads. + * + * Compile with: + * + * gcc -g -Wall soak.c `pkg-config vips --cflags --libs` + * + * Run with: + * + * rm -rf x + * mkdir x + * for i in {0..10}; do ./a.out ~/pics/k2.jpg; done + * + */ + +#include <stdio.h> +#include <glib.h> + +#include <vips/vips.h> + +/* How many pings we run at once. + */ +#define NUM_IN_PARALLEL (50) + +/* Number of tests we do in total. + */ +#define TOTAL_TESTS (NUM_IN_PARALLEL * 20) + +/* Workers queue up on this. + */ +GMutex allocation_lock; + +/* Our set of threads. + */ +GThread *workers[NUM_IN_PARALLEL]; + +/* Number of calls so far. + */ +int n_calls = 0; + +/* Our test function. This is called by NUM_IN_PARALLEL threads a total of + * TOTAL_TESTS times. + */ +static int +test (const char *filename) +{ + VipsImage *im, *x; + char output_file[256]; + + snprintf (output_file, 256, "x/tmp-%p.jpg", g_thread_self ()); + + if (!(im = vips_image_new_from_file (filename, + "access", + VIPS_ACCESS_SEQUENTIAL_UNBUFFERED, + NULL))) + return (-1); + + if (vips_resize (im, &x, 0.1, NULL)) + { + g_object_unref (im); + return (-1); + } + g_object_unref (im); + im = x; + + if (vips_image_write_to_file (im, output_file, NULL)) + { + g_object_unref (im); + return (-1); + } + g_object_unref (im); + + return (0); +} + +/* What we run as a thread. + */ +static void * +worker (void *data) +{ + const char *filename = (const char *) data; + + for (;;) + { + gboolean done; + + done = FALSE; + g_mutex_lock (&allocation_lock); + n_calls += 1; + if (n_calls > TOTAL_TESTS) + done = TRUE; + g_mutex_unlock (&allocation_lock); + + if (done) + break; + + if (test (filename)) + vips_error_exit (NULL); + } + + return (NULL); +} + +int +main (int argc, char **argv) +{ + int i; + + if (VIPS_INIT (argv[0])) + vips_error_exit (NULL); + + g_mutex_init (&allocation_lock); + + for (i = 0; i < NUM_IN_PARALLEL; i++) + workers[i] = g_thread_new (NULL, (GThreadFunc) worker, argv[1]); + + for (i = 0; i < NUM_IN_PARALLEL; i++) + g_thread_join (workers[i]); + + return (0); +} + + + + + + diff --git a/libvips/iofuncs/threadpool.c b/libvips/iofuncs/threadpool.c index 4cdc9f89..cc999200 100644 --- a/libvips/iofuncs/threadpool.c +++ b/libvips/iofuncs/threadpool.c @@ -216,8 +216,8 @@ vips_g_thread_new( const char *domain, GThreadFunc func, gpointer data ) * #VipsThreadPool. * * The special value 0 means "default". In this case, the number of threads is - * set by the environmnt variable VIPS_CONCURRENCY, or if that is not set, the - * number of threads availble on the hist machine. + * set by the environment variable VIPS_CONCURRENCY, or if that is not set, the + * number of threads availble on the host machine. * * See also: vips_concurrency_get(). */