1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
/*
* 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/)
*
* Full documentation can be found at http://perlmulticore.schmorp.de/
* The newest version of this header can be downloaded from
* http://perlmulticore.schmorp.de/perlmulticore.h
*/
#ifndef PERL_MULTICORE_H
#define PERL_MULTICORE_H
/*
=head1 NAME
perlmulticore.h - implements the Perl Multicore Specification
=head1 SYNOPSIS
#include "perlmulticore.h"
// in your XS function:
perlinterp_release ();
do_the_C_thing ();
perlinterp_acquire ();
// optional, in BOOT section:
perlmulticore_support ();
=head1 DESCRIPTION
This documentation is the abridged version of the full documention at
L<http://perlmulticore.schmorp.de/>. It's recommended to go there instead
of reading this document.
This header file implements a very low overhead (both in code and runtime)
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 newest version of the header file itself, can be downloaded from
L<http://perlmulticore.schmorp.de/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.
More documentation and examples can be found at the perl multicore site at
L<http://perlmulticore.schmorp.de>.
=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>.
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.
=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 at runtime.
=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.
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.
=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 could be added to perl's C<CPPFLAGS> when configuring perl on
platforms that do not support threading at all for example.
=head1 ADVERTISING MULTICORE API SUPPORT
To help users find out whether a particular build of your module is, in
fact, multicore enabled, you can invoke the C<perlmulticore_support>
macro in your C<BOOT:> section, e.g.:
MODULE = My::Mod PACKAGE = My::Mod::Pkg
BOOT:
perlmulticore_support ();
What this does is set the C<$My::Mod::PERLMULTICORE_SUPPORT> variable to
the major API version * 1000 + minor version, for example, version C<1002>
introduced this feature.
For this to work, the C<cv> parameter passed to C<BOOT:> must still be
in scope. To ensure this, either invoke the macro early in your C<BOOT:>
section, or don't declare a local variable called C<cv>, either of which
should be easy to do.
Note that this is I<optional>, so you don't have to do that.
=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
*/
/* version history
* 1.1 (1001) 2015-07-03: initial release.
* 1.2 (1002) 2019-03-03: introduce optional perlmulticore_support macro.
*/
#define PERL_MULTICORE_MAJOR 1 /* bumped on incompatible changes */
#define PERL_MULTICORE_MINOR 2 /* bumped on every change */
#if PERL_MULTICORE_DISABLE
#define perlinterp_release() do { } while (0)
#define perlinterp_acquire() do { } while (0)
#define perlmulticore_support() do { } while (0)
#else
START_EXTERN_C
/* 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);
static const struct perl_multicore_api perl_multicore_api_init
= { perl_multicore_init, 0 };
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)
{
}
static const char perl_multicore_api_key[] = "perl_multicore_api";
/* 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_key,
sizeof (perl_multicore_api_key) - 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 */
#ifdef NEWSV
SV *api_sv = NEWSV (0, sizeof (*perl_multicore_api));
#else
SV *api_sv = newSV ( sizeof (*perl_multicore_api));
#endif
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 ();
}
#define perlmulticore_support() \
sv_setiv (get_sv ( \
form ("%s::PERLMULTICORE_SUPPORT", HvNAME (GvSTASH (CvGV (cv)))), \
GV_ADD | GV_ADDMULTI), \
PERL_MULTICORE_MAJOR * 1000 + PERL_MULTICORE_MINOR); \
END_EXTERN_C
#endif
#endif
|