add mutex functions to the API
authorTony Cook <tony@develop-help.com>
Mon, 22 Oct 2012 10:01:41 +0000 (21:01 +1100)
committerTony Cook <tony@develop-help.com>
Sat, 24 Nov 2012 03:59:25 +0000 (14:59 +1100)
14 files changed:
MANIFEST
Makefile.PL
imager.h
imdatatypes.h
imext.c
imext.h
imexttypes.h
lib/Imager/API.pod
lib/Imager/APIRef.pod
mutexnull.c [new file with mode: 0644]
mutexpthr.c [new file with mode: 0644]
mutexwin.c [new file with mode: 0644]
t/t82inline.t
t/x20spell.t

index b06d94a..878d357 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -217,6 +217,9 @@ MANIFEST
 MANIFEST.SKIP
 map.c
 maskimg.c
+mutexnull.c
+mutexpthr.c
+mutexwin.c
 palimg.c
 paste.im
 plug.h
index a3ab1d4..8513bf3 100644 (file)
@@ -169,6 +169,25 @@ my @objs = qw(Imager.o context.o draw.o polygon.o image.o io.o iolayer.o
               bmp.o tga.o color.o fills.o imgdouble.o limits.o hlines.o
               imext.o scale.o rubthru.o render.o paste.o compose.o flip.o);
 
+if ($Config{useithreads}) {
+  if ($Config{i_pthread}) {
+    print "POSIX threads\n";
+    push @objs, "mutexpthr.o";
+  }
+  elsif ($^O eq 'MSWin32') {
+    print "Win32 threads\n";
+    push @objs, "mutexwin.o";
+  }
+  else {
+    print "Unsupported threading model\n";
+    push @objs, "mutexnull.o";
+  }
+}
+else {
+  print "No threads\n";
+  push @objs, "mutexnull.o";
+}
+
 my @typemaps = qw(typemap.local typemap);
 if ($] < 5.008) {
     unshift @typemaps, "typemap.oldperl";
index 0d6523f..6d003bb 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -389,6 +389,12 @@ extern im_context_t im_context_clone(im_context_t ctx, const char *where);
 
 extern im_context_t (*im_get_context)(void);
 
+/* mutex API */
+extern i_mutex_t i_mutex_new(void);
+extern void i_mutex_destroy(i_mutex_t m);
+extern void i_mutex_lock(i_mutex_t m);
+extern void i_mutex_unlock(i_mutex_t m);
+
 #include "imio.h"
 
 #endif
index 32c92d1..af55f90 100644 (file)
@@ -553,6 +553,18 @@ typedef enum {
   ic_color
 } i_combine_t;
 
+/*
+=item i_mutex_t
+X<i_mutex>
+=category mutex
+=synopsis i_mutex_t mutex;
+
+Opaque type for Imager's mutex API.
+
+=cut
+ */
+typedef struct i_mutex_tag *i_mutex_t;
+
 /*
    describes an axis of a MM font.
    Modelled on FT2's FT_MM_Axis.
diff --git a/imext.c b/imext.c
index 24aad64..4c23d91 100644 (file)
--- a/imext.c
+++ b/imext.c
@@ -161,7 +161,11 @@ im_ext_funcs imager_function_table =
     im_loog,
     im_context_refinc,
     im_context_refdec,
-    im_errors
+    im_errors,
+    i_mutex_new,
+    i_mutex_destroy,
+    i_mutex_lock,
+    i_mutex_unlock
   };
 
 /* in general these functions aren't called by Imager internally, but
diff --git a/imext.h b/imext.h
index 3f08bd9..131dd1f 100644 (file)
--- a/imext.h
+++ b/imext.h
@@ -227,6 +227,11 @@ extern im_ext_funcs *imager_function_ext_table;
 #define io_slurp(ig, datap) ((im_extt->f_io_slurp)((ig), (datap)))
 #define io_glue_destroy(ig) ((im_extt->f_io_glue_destroy)(ig))
 
+#define i_mutex_new() ((im_extt->f_i_mutex_new)())
+#define i_mutex_destroy(m) ((im_extt->f_i_mutex_destroy)(m))
+#define i_mutex_lock(m) ((im_extt->f_i_mutex_lock)(m))
+#define i_mutex_unlock(m) ((im_extt->f_i_mutex_unlock)(m))
+
 #define im_push_errorf (im_extt->f_im_push_errorf)
 
 #ifdef IMAGER_LOG
index 6bdc35e..673aded 100644 (file)
@@ -228,6 +228,13 @@ typedef struct {
   void (*f_im_context_refinc)(im_context_t, const char *where);
   void (*f_im_context_refdec)(im_context_t, const char *where);
   i_errmsg *(*f_im_errors)(im_context_t);
+  i_mutex_t (*f_i_mutex_new)(void);
+  void (*f_i_mutex_destroy)(i_mutex_t m);
+  void (*f_i_mutex_lock)(i_mutex_t m);
+  void (*f_i_mutex_unlock)(i_mutex_t m);
+  im_slot_t (*f_im_context_slot_new)(im_slot_destroy_t);
+  int (*f_im_context_slot_set)(im_context_t, im_slot_t, void *);
+  void *(*f_im_context_slot_get)(im_context_t, im_slot_t);
 } im_ext_funcs;
 
 #define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table"
index 703eb12..9ab8e2c 100644 (file)
@@ -412,6 +412,31 @@ This can be used to define your own local context macro:
     ...
   }
 
+=head1 Mutex Functions
+
+Since some libraries are not thread safe, Imager's API includes some
+simple mutex functions.
+
+To create a mutex:
+
+  i_mutex_t m = i_mutex_new();
+
+To control or lock the mutex:
+
+  i_mutex_lock(m);
+
+To release or unlock the mutex:
+
+  i_mutex_unlock(m);
+
+To free any resources used by the mutex:
+
+  i_mutex_destroy(m);
+
+I most cases where you'd use these functions, your code would create
+the mutex in your BOOT section, then lock and unlock the mutex as
+needed to control access to the library.
+
 =head1 AUTHOR
 
 Tony Cook <tonyc@cpan.org>
index 8949d46..1f61bb1 100644 (file)
@@ -109,6 +109,12 @@ Imager::APIRef - Imager's C API - reference.
 
   # Logging
 
+  # Mutex functions
+  i_mutex_t m = i_mutex_new();
+  i_mutex_destroy(m);
+  i_mutex_lock(m);
+  i_mutex_unlock(m);
+
   # Paletted images
 
   # Tags
@@ -1876,6 +1882,58 @@ This is an internal function called by the mm_log() macro.
 From: File log.c
 
 
+=back
+
+=head2 Mutex functions
+
+=over
+
+=item i_mutex_new()
+
+  i_mutex_t m = i_mutex_new();
+
+Create a mutex.
+
+If a critical section cannot be created for whatever reason, Imager
+will abort.
+
+
+=for comment
+From: File mutexwin.c
+
+=item i_mutex_destroy(m)
+
+  i_mutex_destroy(m);
+
+Destroy a mutex.
+
+
+=for comment
+From: File mutexwin.c
+
+=item i_mutex_lock(m)
+
+  i_mutex_lock(m);
+
+Lock the mutex, waiting if another thread has the mutex locked.
+
+
+=for comment
+From: File mutexwin.c
+
+=item i_mutex_unlock(m)
+
+  i_mutex_unlock(m);
+
+Release the mutex.
+
+The behavior of releasing a mutex you don't hold is unspecified.
+
+
+=for comment
+From: File mutexwin.c
+
+
 =back
 
 =head2 Paletted images
@@ -2210,6 +2268,31 @@ Returns ~0UL on failure.
 =for comment
 From: File io.c
 
+=item im_context_refdec(ctx, where)
+X<im_context_refdec API>
+=section Context objects
+
+  im_context_refdec(aIMCTX, "a description");
+
+Remove a reference to the context, releasing it if all references have
+been removed.
+
+
+=for comment
+From: File context.c
+
+=item im_context_refinc(ctx, where)
+X<im_context_refinc API>
+=section Context objects
+
+  im_context_refinc(aIMCTX, "a description");
+
+Add a new reference to the context.
+
+
+=for comment
+From: File context.c
+
 =item im_errors(ctx)
 
   i_errmsg *errors = im_errors(aIMCTX);
@@ -2251,14 +2334,6 @@ will change:
 
 =item *
 
-B<im_context_refdec>
-
-=item *
-
-B<im_context_refinc>
-
-=item *
-
 B<im_lhead>
 
 =item *
diff --git a/mutexnull.c b/mutexnull.c
new file mode 100644 (file)
index 0000000..ef39ca5
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+  dummy mutexes, for non-threaded builds
+*/
+
+#include "imageri.h"
+
+#include <pthread.h>
+
+/* documented in mutexwin.c */
+
+struct i_mutex_tag {
+  int dummy;
+};
+
+i_mutex_t
+i_mutex_new(void) {
+  i_mutex_t m;
+
+  m = mymalloc(sizeof(*m));
+
+  return m;
+}
+
+void
+i_mutex_destroy(i_mutex_t m) {
+  myfree(m);
+}
+
+void
+i_mutex_lock(i_mutex_t m) {
+  (void)m;
+}
+
+void
+i_mutex_unlock(i_mutex_t m) {
+  (void)m;
+}
diff --git a/mutexpthr.c b/mutexpthr.c
new file mode 100644 (file)
index 0000000..709f02f
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+  pthreads mutexes
+*/
+
+#include "imageri.h"
+
+#include <pthread.h>
+#include <errno.h>
+
+/* documented in mutexwin.c */
+
+struct i_mutex_tag {
+  pthread_mutex_t mutex;
+};
+
+i_mutex_t
+i_mutex_new(void) {
+  i_mutex_t m;
+
+  m = mymalloc(sizeof(*m));
+  if (pthread_mutex_init(&m->mutex, NULL) != 0) {
+    i_fatal(3, "Error initializing mutex %d", errno);
+  }
+
+  return m;
+}
+
+void
+i_mutex_destroy(i_mutex_t m) {
+  pthread_mutex_destroy(&(m->mutex));
+  myfree(m);
+}
+
+void
+i_mutex_lock(i_mutex_t m) {
+  pthread_mutex_lock(&(m->mutex));
+}
+
+void
+i_mutex_unlock(i_mutex_t m) {
+  pthread_mutex_unlock(&m->mutex);
+}
diff --git a/mutexwin.c b/mutexwin.c
new file mode 100644 (file)
index 0000000..d4726e2
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+=head1 NAME
+
+mutex.c - Imager's mutex API.
+
+=head1 FUNCTIONS
+
+=over
+
+=cut
+*/
+
+#include "imageri.h"
+
+#include <windows.h>
+
+struct i_mutex_tag {
+  CRITICAL_SECTION section;
+};
+
+/*
+=item i_mutex_new()
+=category Mutex functions
+=synopsis i_mutex_t m = i_mutex_new();
+=order 10
+
+Create a mutex.
+
+If a critical section cannot be created for whatever reason, Imager
+will abort.
+
+=cut
+*/
+
+i_mutex_t
+i_mutex_new(void) {
+  i_mutex_t m;
+
+  m = mymalloc(sizeof(*m));
+  InitializeCriticalSection(&(m->section));
+
+  return m;
+}
+
+/*
+=item i_mutex_destroy(m)
+=category Mutex functions
+=synopsis i_mutex_destroy(m);
+
+Destroy a mutex.
+
+=cut
+*/
+
+void
+i_mutex_destroy(i_mutex_t m) {
+  DeleteCriticalSection(&(m->section));
+  myfree(m);
+}
+
+/*
+=item i_mutex_lock(m)
+=category Mutex functions
+=synopsis i_mutex_lock(m);
+
+Lock the mutex, waiting if another thread has the mutex locked.
+
+=cut
+*/
+
+void
+i_mutex_lock(i_mutex_t m) {
+  EnterCriticalSection(&(m->section));
+}
+
+/*
+=item i_mutex_unlock(m)
+=category Mutex functions
+=synopsis i_mutex_unlock(m);
+
+Release the mutex.
+
+The behavior of releasing a mutex you don't hold is unspecified.
+
+=cut
+*/
+
+void
+i_mutex_unlock(i_mutex_t m) {
+  LeaveCriticalSection(&(m->section));
+}
+
index 92e995d..82f3c80 100644 (file)
@@ -19,7 +19,7 @@ plan skip_all => "perl 5.005_04, 5.005_05 too buggy"
 
 -d "testout" or mkdir "testout";
 
-plan tests => 115;
+plan tests => 116;
 require Inline;
 Inline->import(with => 'Imager');
 Inline->import("FORCE"); # force rebuild
@@ -410,6 +410,18 @@ raw_psampf(Imager im, int chan_count) {
   return i_psampf(im, 0, 1, 0, samps, NULL, chan_count);
 }
 
+int
+test_mutex() {
+  i_mutex_t m;
+
+  m = i_mutex_new();
+  i_mutex_lock(m);
+  i_mutex_unlock(m);
+  i_mutex_destroy(m);
+
+  return 1;
+}
+
 EOS
 
 my $im = Imager->new(xsize=>50, ysize=>50);
@@ -626,6 +638,8 @@ for my $bits (8, 16) {
   is($im->type, "paletted", "make sure we kept the image type");
 }
 
+ok(test_mutex(), "call mutex APIs");
+
 sub _get_error {
   my @errors = Imager::i_errors();
   return join(": ", map $_->[0], @errors);
index 9c9928a..d7368b9 100644 (file)
@@ -47,6 +47,7 @@ infix
 invocant
 metadata
 multi-threaded
+mutex
 paletted
 postfix
 preload