Blob Blame History Raw
From 5b3fd16594515047f5b9bd8f15865d0491e07369 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
Date: Mon, 5 Oct 2015 16:07:17 +0200
Subject: [PATCH] Unbundle perlmulticore
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Petr Písař <ppisar@redhat.com>
---
 LZF.xs          |   2 +-
 MANIFEST        |   1 -
 perlmulticore.h | 399 --------------------------------------------------------
 3 files changed, 1 insertion(+), 401 deletions(-)
 delete mode 100644 perlmulticore.h

diff --git a/LZF.xs b/LZF.xs
index 2f6fd98..5d69122 100644
--- a/LZF.xs
+++ b/LZF.xs
@@ -2,7 +2,7 @@
 #include "perl.h"
 #include "XSUB.h"
 
-#include "perlmulticore.h"
+#include <perlmulticore.h>
 
 #define LZF_STANDALONE 1
 #define LZF_STATE_ARG 1
diff --git a/MANIFEST b/MANIFEST
index 05cb98f..227ee3d 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -5,7 +5,6 @@ COPYING
 COPYING.Artistic
 COPYING.GNU
 Makefile.PL
-perlmulticore.h
 t/00_load.t
 t/01_run.t
 t/02_freeze.t
diff --git a/perlmulticore.h b/perlmulticore.h
deleted file mode 100644
index 5c268f5..0000000
--- a/perlmulticore.h
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Author: Marc A. Lehmann <xsthreadpool@schmorp.de>
- * License: public domain, or where this is not possible/at your option,
- *          CC0 (https://creativecommons.org/publicdomain/zero/1.0/)
- */
-
-#ifndef PERL_MULTICORE_H
-#define PERL_MULTICORE_H
-
-/*
-
-=head1 NAME
-
-perlmulticore.h - the Perl Multicore Specification and Implementation
-
-=head1 SYNOPSIS
-
-  #include "perlmultiore.h"
-
-  // in your XS function:
-
-  perlinterp_release ();
-  do_the_C_thing ();
-  perlinterp_acquire ();
-
-=head1 DESCRIPTION
-
-This header file implements a simple mechanism for XS modules to allow
-re-use of the perl interpreter for other threads while doing some lengthy
-operation, such as cryptography, SQL queries, disk I/O and so on.
-
-The design goals for this mechanism were to be simple to use, very
-efficient when not needed, low code and data size overhead and broad
-applicability.
-
-The newest version of this document can be found at
-L<http://pod.tst.eu/http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>.
-
-The newest version of the header file itself, which
-includes this documentation, can be downloaded from
-L<http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>.
-
-=head1 HOW DO I USE THIS IN MY MODULES?
-
-The usage is very simple - you include this header file in your XS module. Then, before you
-do your lengthy operation, you release the perl interpreter:
-
-   perlinterp_release ();
-
-And when you are done with your computation, you acquire it again:
-
-   perlinterp_acquire ();
-
-And that's it. This doesn't load any modules and consists of only a few
-machine instructions when no module to take advantage of it is loaded.
-
-Here is a simple example, an C<flock> wrapper implemented in XS. Unlike
-perl's built-in C<flock>, it allows other threads (for example, those
-provided by L<Coro>) to execute, instead of blocking the whole perl
-interpreter. For the sake of this example, it requires a file descriptor
-instead of a handle.
-
-   #include "perlmulticore.h" // this header file
-
-   // and in the XS portion
-   int flock (int fd, int operation)
-           CODE:
-           perlinterp_release ();
-           RETVAL = flock (fd, operation);
-           perlinterp_acquire ();
-           OUTPUT:
-           RETVAL
-
-Another example would be to modify L<DBD::mysql> to allow other
-threads to execute while executing SQL queries. One way to do this
-is find all C<mysql_st_internal_execute> and similar calls (such as
-C<mysql_st_internal_execute41>), and adorn them with release/acquire
-calls:
-
-   {
-     perlinterp_release ();
-     imp_sth->row_num= mysql_st_internal_execute(sth, ...);
-     perlinterp_acquire ();
-   }
-
-=head2 HOW ABOUT NOT-SO LONG WORK?
-
-Sometimes you don't know how long your code will take - in a compression
-library for example, compressing a few hundred Kilobyte of data can take
-a while, while 50 Bytes will compress so fast that even attempting to do
-something else could be more costly than just doing it.
-
-This is a very hard problem to solve. The best you can do at the moment is
-to release the perl interpreter only when you think the work to be done
-justifies the expense.
-
-As a rule of thumb, if you expect to need more than a few thousand cycles,
-you should release the interpreter, else you shouldn't. When in doubt,
-release.
-
-For example, in a compression library, you might want to do this:
-
-   if (bytes_to_be_compressed > 2000) perlinterp_release ();
-   do_compress (...);
-   if (bytes_to_be_compressed > 2000) perlinterp_acquire ();
-
-Make sure the if conditions are exactly the same and don't change, so you
-always call acquire when you release, and vice versa.
-
-When you don't have a handy indicator, you might still do something
-useful. For example, if you do some file locking with C<fcntl> and you
-expect the lock to be available immediately in most cases, you could try
-with C<F_SETLK> (which doesn't wait), and only release/wait/acquire when
-the lock couldn't be set:
-
-   int res = fcntl (fd, F_SETLK, &flock);
-
-   if (res)
-     {
-       // error, assume lock is held by another process and do it the slow way
-       perlinterp_release ();
-       res = fcntl (fd, F_SETLKW, &flock);
-       perlinterp_acquire ();
-     }
-
-=head1 THE HARD AND FAST RULES
-
-As with everything, there are a number of rules to follow.
-
-=over 4
-
-=item I<Never> touch any perl data structures after calling C<perlinterp_release>.
-
-Possibly the most important rule of them all, anything perl is
-completely off-limits after C<perlinterp_release>, until you call
-C<perlinterp_acquire>, after which you can access perl stuff again.
-
-That includes anything in the perl interpreter that you didn't prove to be
-safe, and didn't prove to be safe in older and future versions of perl:
-global variables, local perl scalars, even if you are sure nobody accesses
-them and you only try to "read" their value, and so on.
-
-If you need to access perl things, do it before releasing the
-interpreter with C<perlinterp_release>, or after acquiring it again with
-C<perlinterp_acquire>.
-
-=item I<Always> call C<perlinterp_release> and C<perlinterp_acquire> in pairs.
-
-For each C<perlinterp_release> call there must be a C<perlinterp_acquire>
-call. They don't have to be in the same function, and you can have
-multiple calls to them, as long as every C<perlinterp_release> call is
-followed by exactly one C<perlinterp_acquire> call.
-
-For example., this would be fine:
-
-   perlinterp_release ();
-
-   if (!function_that_fails_with_0_return_value ())
-     {
-       perlinterp_acquire ();
-       croak ("error");
-       // croak doesn't return
-     }
-
-   perlinterp_acquire ();
-   // do other stuff
-
-=item I<Never> nest calls to C<perlinterp_release> and C<perlinterp_acquire>.
-
-That simply means that after calling C<perlinterp_release>, you must
-call C<perlinterp_acquire> before calling C<perlinterp_release>
-again. Likewise, after C<perlinterp_acquire>, you can call
-C<perlinterp_release> but not another C<perlinterp_acquire>.
-
-=item I<Always> call C<perlinterp_release> first.
-
-Also simple: you I<must not> call C<perlinterp_acquire> without having
-called C<perlinterp_release> before.
-
-=item I<Never> underestimate threads.
-
-While it's easy to add parallel execution ability to your XS module, it
-doesn't mean it is safe. After you release the perl interpreter, it's
-perfectly possible that it will call your XS function in another thread,
-even while your original function still executes. In other words: your C
-code must be thread safe, and if you use any library, that library must be
-thread-safe, too.
-
-Always assume that the code between C<perlinterp_release> and
-C<perlinterp_acquire> is executed in parallel on multiple CPUs at the same
-time. If your code can't cope with that, you could consider using a mutex
-to only allow one such execution, which is still better than blocking
-everybody else from doing anything:
-
-   static pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-   perlinterp_release ();
-   pthread_mutex_lock (&my_mutex);
-   do_your_non_thread_safe_thing ();
-   pthread_mutex_unlock (&my_mutex);
-   perlinterp_acquire ();
-
-=item I<Don't> get confused by having to release first.
-
-In many real world scenarios, you acquire a resource, do something, then
-release it again. Don't let this confuse you, with this, you already own
-the resource (the perl interpreter) so you have to I<release> first, and
-I<acquire> it again later, not the other way around.
-
-=back
-
-
-=head1 DESIGN PRINCIPLES
-
-This section discusses how the design goals were reached (you be the
-judge), how it is implemented, and what overheads this implies.
-
-=over 4
-
-=item Simple to Use
-
-All you have to do is identify the place in your existing code where you
-stop touching perl stuff, do your actual work, and start touching perl
-stuff again.
-
-Then slap C<perlinterp_release ()> and C<perlinterp_acquire ()> around the
-actual work code.
-
-You have to include F<perlmulticore.h> and distribute it with your XS
-code, but all these things border on the trivial.
-
-=item Very Efficient
-
-The definition for C<perlinterp_release> and C<perlinterp_release> is very
-short:
-
-   #define perlinterp_release() perl_multicore_api->pmapi_release ()
-   #define perlinterp_acquire() perl_multicore_api->pmapi_acquire ()
-
-Both are macros that read a pointer from memory (perl_multicore_api),
-dereference a function pointer stored at that place, and call the
-function, which takes no arguments and returns nothing.
-
-The first call to C<perlinterp_release> will check for the presence
-of any supporting module, and if none is loaded, will create a dummy
-implementation where both C<pmapi_release> and C<pmapi_acquire> execute
-this function:
-
-  static void perl_multicore_nop (void) { }
-
-So in the case of no magical module being loaded, all calls except the
-first are two memory accesses and a predictable function call of an empty
-function.
-
-Of course, the overhead is much higher when these functions actually
-implement anything useful, but you always get what you pay for.
-
-With L<Coro::Multicore>, every release/acquire involves two pthread
-switches, two coro thread switches, a bunch of syscalls, and sometimes
-interacting with the event loop.
-
-A dedicated thread pool such as the one L<IO::AIO> uses could reduce
-these overheads, and would also reduce the dependencies (L<AnyEvent> is a
-smaller and more portable dependency than L<Coro>), but it would require a
-lot more work on the side of the module author wanting to support it than
-this solution.
-
-=item Low Code and Data Size Overhead
-
-On a 64 bit system, F<perlmulticore.h> uses exactly C<8> octets (one
-pointer) of your data segment, to store the C<perl_multicore_api>
-pointer. In addition it creates a C<16> octet perl string to store the
-function pointers in, and stores it in a hash provided by perl for this
-purpose.
-
-This is pretty much the equivalent of executing this code:
-
-   $existing_hash{perl_multicore_api} = "123456781234567812345678";
-
-And that's it, which is, as I think, indeed very little.
-
-As for code size, on my amd64 system, every call to C<perlinterp_release>
-or C<perlinterp_acquire> results in a variation of the following 9-10
-octet sequence:
-
-   150>   mov    0x200f23(%rip),%rax  # <perl_multicore_api>
-   157>   callq  *0x8(%rax)
-
-The biggest part if the initialisation code, which consists of 11 lines of
-typical XS code. On my system, all the code in F<perlmulticore.h> compiles
-to less than 160 octets of read-only data.
-
-=item Broad Applicability
-
-While there are alternative ways to achieve the goal of parallel execution
-with threads that might be more efficient, this mechanism was chosen
-because it is very simple to retrofit existing modules with it, and it
-
-The design goals for this mechanism were to be simple to use, very
-efficient when not needed, low code and data size overhead and broad
-applicability.
-
-=back
-
-
-=head1 DISABLING PERL MULTICORE AT COMPILE TIME
-
-You can disable the complete perl multicore API by defining the
-symbol C<PERL_MULTICORE_DISABLE> to C<1> (e.g. by specifying
-F<-DPERL_MULTICORE_DISABLE> as compiler argument).
-
-This will leave no traces of the API in the compiled code, suitable
-"empty" C<perl_release> and C<perl_acquire> definitions will be provided.
-
-This could be added to perl's C<CPPFLAGS> when configuring perl on
-platforms that do not support threading at all for example.
-
-
-=head1 AUTHOR
-
-   Marc A. Lehmann <perlmulticore@schmorp.de>
-   http://perlmulticore.schmorp.de/
-
-=head1 LICENSE
-
-The F<perlmulticore.h> header file is put into the public
-domain. Where this is legally not possible, or at your
-option, it can be licensed under creativecommons CC0
-license: L<https://creativecommons.org/publicdomain/zero/1.0/>.
-
-=cut
-
-*/
-
-#define PERL_MULTICORE_MAJOR 1 /* bumped on incompatible changes */
-#define PERL_MULTICORE_MINOR 0 /* bumped on every change */
-
-#if PERL_MULTICORE_DISABLE
-
-#define perlinterp_release() do { } while (0)
-#define perlinterp_acquire() do { } while (0)
-
-#else
-
-/* this struct is shared between all modules, and currently */
-/* contain only the two function pointers for release/acquire */
-struct perl_multicore_api
-{
-  void (*pmapi_release)(void);
-  void (*pmapi_acquire)(void);
-};
-
-static void perl_multicore_init (void);
-
-const struct perl_multicore_api perl_multicore_api_init = { perl_multicore_init, abort };
-
-static struct perl_multicore_api *perl_multicore_api
-   = (struct perl_multicore_api *)&perl_multicore_api_init;
-
-#define perlinterp_release() perl_multicore_api->pmapi_release ()
-#define perlinterp_acquire() perl_multicore_api->pmapi_acquire ()
-
-/* this is the release/acquire implementation used as fallback */
-static void
-perl_multicore_nop (void)
-{
-}
-
-/* this is the initial implementation of "release" - it initialises */
-/* the api and then calls the real release function */
-static void
-perl_multicore_init (void)
-{
-  dTHX;
-
-  /* check for existing API struct in PL_modglobal */
-  SV **api_svp = hv_fetch (PL_modglobal, "perl_multicore_api", sizeof ("perl_multicore_api") - 1, 1);
-
-  if (SvPOKp (*api_svp))
-    perl_multicore_api = (struct perl_multicore_api *)SvPVX (*api_svp); /* we have one, use the existing one */
-  else
-    {
-      /* create a new one with a dummy nop implementation */
-      SV *api_sv = NEWSV (0, sizeof (*perl_multicore_api));
-      SvCUR_set (api_sv, sizeof (*perl_multicore_api));
-      SvPOK_only (api_sv);
-      perl_multicore_api = (struct perl_multicore_api *)SvPVX (api_sv);
-      perl_multicore_api->pmapi_release =
-      perl_multicore_api->pmapi_acquire = perl_multicore_nop;
-      *api_svp = api_sv;
-    }
-
-  /* call the real (or dummy) implementation now */
-  perlinterp_release ();
-}
-
-#endif
-
-#endif
-- 
2.4.3