ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro-Multicore/perlmulticore.h
(Generate patch)

Comparing Coro-Multicore/perlmulticore.h (file contents):
Revision 1.2 by root, Sat Jun 27 19:36:39 2015 UTC vs.
Revision 1.15 by root, Tue Mar 5 12:10:31 2019 UTC

1TODO: if () acquire example
2/* 1/*
3 * Author: Marc A. Lehmann <xsthreadpool@schmorp.de> 2 * Author: Marc A. Lehmann <xsthreadpool@schmorp.de>
4 * License: public domain, or where this is not possible/at your option, 3 * License: public domain, or where this is not possible/at your option,
5 * CC0 (https://creativecommons.org/publicdomain/zero/1.0/) 4 * CC0 (https://creativecommons.org/publicdomain/zero/1.0/)
5 *
6 * Full documentation can be found at http://perlmulticore.schmorp.de/
7 * The newest version of this header can be downloaded from
8 * http://perlmulticore.schmorp.de/perlmulticore.h
6 */ 9 */
7 10
8#ifndef PERL_MULTICORE_H 11#ifndef PERL_MULTICORE_H
9#define PERL_MULTICORE_H 12#define PERL_MULTICORE_H
10 13
11#if 0 14/*
12 15
13=head1 NAME 16=head1 NAME
14 17
15perlmulticore.h - release the perl interpreter for other uses while doing hard work 18perlmulticore.h - implements the Perl Multicore Specification
16 19
17=head1 SYNOPSIS 20=head1 SYNOPSIS
18 21
19 #include "perlmultiore.h" 22 #include "perlmulticore.h"
20 23
21 // in your XS function: 24 // in your XS function:
22 25
23 perlinterp_release (); 26 perlinterp_release ();
24 do_the_C_thing (); 27 do_the_C_thing ();
25 perlinterp_acquire (); 28 perlinterp_acquire ();
26 29
30 // optional, in BOOT section:
31
32 perlmulticore_support ();
33
27=head1 DESCRIPTION 34=head1 DESCRIPTION
28 35
29This header file implements a simple mechanism for XS modules to allow 36This documentation is the abridged version of the full documention at
30re-use of the perl interpreter for other threads while doing some lengthy 37L<http://perlmulticore.schmorp.de/>. It's recommended to go there instead
31operation, such as cryptography, SQL queries, disk I/O and so on. 38of reading this document.
32 39
33The design goals for this mechanism were to be simple to use, very 40This header file implements a very low overhead (both in code and runtime)
34efficient when not needed, low code and data size overhead and broad 41mechanism for XS modules to allow re-use of the perl interpreter for other
35applicability. 42threads while doing some lengthy operation, such as cryptography, SQL
43queries, disk I/O and so on.
36 44
37The newest version of this document can be found at
38L<http://pod.tst.eu/http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>.
39
40The nwest version of the header fgile itself, which 45The newest version of the header file itself, can be downloaded from
41includes this documentation, can be downloaded from 46L<http://perlmulticore.schmorp.de/perlmulticore.h>.
42L<http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>.
43 47
44=head1 HOW DO I USE THIS IN MY MODULES? 48=head1 HOW DO I USE THIS IN MY MODULES?
45 49
46The usage is very simple - you include this header file in your XS module. Then, before you 50The usage is very simple - you include this header file in your XS module. Then, before you
47do your lengthy operation, you release the perl interpreter: 51do your lengthy operation, you release the perl interpreter:
53 perlinterp_acquire (); 57 perlinterp_acquire ();
54 58
55And that's it. This doesn't load any modules and consists of only a few 59And that's it. This doesn't load any modules and consists of only a few
56machine instructions when no module to take advantage of it is loaded. 60machine instructions when no module to take advantage of it is loaded.
57 61
58Here is a simple example, an C<flock> wrapper implemented in XS. Unlike 62More documentation and examples can be found at the perl multicore site at
59perl's built-in C<flock>, it allows other threads (for example, those 63L<http://perlmulticore.schmorp.de>.
60provided by L<Coro>) to execute, instead of blocking the whole perl
61interpreter. For the sake of this example, it requires a file descriptor
62instead of a handle.
63
64 #include "perlmulticore.h" /* this header file */
65
66 // and in the XS portion
67 int flock (int fd, int operation)
68 CODE:
69 perlinterp_release ();
70 RETVAL = flock (fd, operation);
71 perlinterp_acquire ();
72 OUTPUT:
73 RETVAL
74
75Another example would be to modify L<DBD::mysql> to allow other
76threads to execute while executing SQL queries. One way to do this
77is find all C<mysql_st_internal_execute> and similar calls (such as
78C<mysql_st_internal_execute41>), and adorn them with release/acquire
79calls:
80
81 {
82 perlinterp_release ();
83 imp_sth->row_num= mysql_st_internal_execute(sth, ...);
84 perlinterp_acquire ();
85 }
86
87=head2 HOW ABOUT NOT-SO LONG WORK?
88
89Sometimes you don't know how long your code will take - in a compression
90library for example, compressing a few hundred Kilobyte of data can take
91a while, while 50 Bytes will compress so fast that even attempting to do
92something else could be more costly than just doing it.
93
94This is a very hard problem to solve. The best you can do at the moment is
95to release the perl interpreter only when you think the work to be done
96justifies the expense.
97
98As a rule of thumb, if you expect to need more than a few thousand cycles,
99you should release the interpreter, else you shouldn't. When in doubt,
100release.
101
102For example, in a compression library, you might want to do this:
103
104 if (bytes_to_be_compressed > 2000) perlinterp_release ();
105 do_compress (...);
106 if (bytes_to_be_compressed > 2000) perlinterp_acquire ();
107
108Make sure the if conditions are exactly the same and don't change, so you
109always call acquire when you release, and vice versa.
110
111When you don't have a handy indicator, you might still do something
112useful. For example, if you do some file locking with C<fcntl> and you
113expect the lock to be available immediately in most cases, you could try
114with C<F_SETLK> (which doesn't wait), and only release/wait/acquire when
115the lock couldn't be set:
116
117 int res = fcntl (fd, F_SETLK, &flock);
118
119 if (res)
120 {
121 // error, assume lock is held by another process and do it the slow way
122 perlinterp_release ();
123 res = fcntl (fd, F_SETLKW, &flock);
124 perlinterp_release ();
125 }
126 64
127=head1 THE HARD AND FAST RULES 65=head1 THE HARD AND FAST RULES
128 66
129As with everything, there are a number of rules to follow. 67As with everything, there are a number of rules to follow.
130 68
131=over 4 69=over 4
132 70
133=item I<Never> touch any perl data structures after calling C<perlinterp_release>. 71=item I<Never> touch any perl data structures after calling C<perlinterp_release>.
134 72
135Possibly the most important rule of them all, anything perl is
136completely off-limits after C<perlinterp_release>, until you call 73Anything perl is completely off-limits after C<perlinterp_release>, until
137C<perlinterp_acquire>, after which you can access perl stuff again. 74you call C<perlinterp_acquire>, after which you can access perl stuff
75again.
138 76
139That includes anything in the perl interpreter that you didn't prove to be 77That includes anything in the perl interpreter that you didn't prove to be
140safe, and didn't prove to be safe in older and future versions of perl: 78safe, and didn't prove to be safe in older and future versions of perl:
141global variables, local perl scalars, even if you are sure nobody accesses 79global variables, local perl scalars, even if you are sure nobody accesses
142them and you only try to "read" their value, and so on. 80them and you only try to "read" their value.
143
144If you need to access perl things, do it before releasing the
145interpreter with C<perlinterp_release>, or after acquiring it again with
146C<perlinterp_acquire>.
147 81
148=item I<Always> call C<perlinterp_release> and C<perlinterp_acquire> in pairs. 82=item I<Always> call C<perlinterp_release> and C<perlinterp_acquire> in pairs.
149 83
150For each C<perlinterp_release> call there must be a C<perlinterp_acquire> 84For each C<perlinterp_release> call there must be a C<perlinterp_acquire>
151call. They don't have to be in the same function, and you can have 85call. They don't have to be in the same function, and you can have
152multiple calls to them, as long as every C<perlinterp_release> call is 86multiple calls to them, as long as every C<perlinterp_release> call is
153followed by exactly one C<perlinterp_acquire> call. 87followed by exactly one C<perlinterp_acquire> call at runtime.
154
155For example., this would be fine:
156
157 perlinterp_release ();
158
159 if (!function_that_fails_with_0_return_value ())
160 {
161 perlinterp_acquire ();
162 croak ("error");
163 // croak doesn't return
164 }
165
166 perlinterp_acquire ();
167 // do other stuff
168 88
169=item I<Never> nest calls to C<perlinterp_release> and C<perlinterp_acquire>. 89=item I<Never> nest calls to C<perlinterp_release> and C<perlinterp_acquire>.
170 90
171That simply means that after calling C<perlinterp_release>, you must 91That simply means that after calling C<perlinterp_release>, you must
172call C<perlinterp_acquire> before calling C<perlinterp_release> 92call C<perlinterp_acquire> before calling C<perlinterp_release>
173again. Likewise, after C<perlinterp_acquire>, you can call 93again. Likewise, after C<perlinterp_acquire>, you can call
174C<perlinterp_release> but not another C<perlinterp_acquire>. 94C<perlinterp_release> but not another C<perlinterp_acquire>.
175 95
176=item I<Always> call C<perlinterp_release> first. 96=item I<Always> call C<perlinterp_release> first.
177 97
178Also simple: you I<must not> call C<perlinterp_acquire> without having 98You I<must not> call C<perlinterp_acquire> without having called
179called C<perlinterp_release> before. 99C<perlinterp_release> before.
180 100
181=item I<Never> underestimate threads. 101=item I<Never> underestimate threads.
182 102
183While it's easy to add parallel execution ability to your XS module, it 103While it's easy to add parallel execution ability to your XS module, it
184doesn't mean it is safe. After you release the perl interpreter, it's 104doesn't mean it is safe. After you release the perl interpreter, it's
187code must be thread safe, and if you use any library, that library must be 107code must be thread safe, and if you use any library, that library must be
188thread-safe, too. 108thread-safe, too.
189 109
190Always assume that the code between C<perlinterp_release> and 110Always assume that the code between C<perlinterp_release> and
191C<perlinterp_acquire> is executed in parallel on multiple CPUs at the same 111C<perlinterp_acquire> is executed in parallel on multiple CPUs at the same
192time. If your code can't cope with that, you could consider using a mutex 112time.
193to only allow one such execution, which is still better than blocking
194everybody else from doing anything:
195
196 static pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
197
198 perlinterp_release ();
199 pthread_mutex_lock (&my_mutex);
200 do_your_non_thread_safe_thing ();
201 pthread_mutex_unlock (&my_mutex);
202 perlinterp_acquire ();
203
204This isn't as trivial as it looks though, as you need to find out which
205threading system is in use (with L<Coro::Multicore>, it currently is
206always pthreads).
207
208=item I<Don't> get confused by having to release first.
209
210In many real world scenarios, you acquire a resource, do something, then
211release it again. Don't let this confuse you, with this, you already own
212the resource (the perl interpreter) so you have to I<release> first, and
213I<acquire> it again later, not the other way around.
214 113
215=back 114=back
216 115
217 116
218=head1 DESIGN PRINCIPLES 117=head1 DISABLING PERL MULTICORE AT COMPILE TIME
219 118
220This section discusses how the design goals were reached (you be the 119You can disable the complete perl multicore API by defining the
221judge), how it is implemented, and what overheads this implies. 120symbol C<PERL_MULTICORE_DISABLE> to C<1> (e.g. by specifying
121F<-DPERL_MULTICORE_DISABLE> as compiler argument).
222 122
223=over 4 123This could be added to perl's C<CPPFLAGS> when configuring perl on
124platforms that do not support threading at all for example.
224 125
225=item Simple to Use
226 126
227All you have to do is identify the place in your existing code where you 127=head1 ADVERTISING MULTICORE API SUPPORT
228stop touching perl stuff, do your actual work, and start touching perl
229stuff again.
230 128
231Then slap C<perlinterp_release ()> and C<perlinterp_acquire ()> around the 129To help users find out whether a particular build of your module is, in
232actual work code. 130fact, multicore enabled, you can invoke the C<perlmulticore_support>
131macro in your C<BOOT:> section, e.g.:
233 132
234You have to include F<perlmulticore.h> and distribute it with your XS
235code, but all these things border on the trivial.
236 133
237=item Very Efficient 134 MODULE = My::Mod PACKAGE = My::Mod::Pkg
238 135
239The definition for C<perlinterp_release> and C<perlinterp_release> is very 136 BOOT:
240short: 137 perlmulticore_support ();
241 138
242 #define perlinterp_release() perl_multicore_api->pmapi_release () 139What this does is set the C<$My::Mod::PERLMULTICORE_SUPPORT> variable to
243 #define perlinterp_acquire() perl_multicore_api->pmapi_acquire () 140the major API version * 1000 + minor version, for example, version C<1002>
141introduced this feature.
244 142
245Both are macros that read a pointer from memory (perl_multicore_api), 143For this to work, the C<cv> parameter passed to C<BOOT:> must still be
246dereference a function pointer stored at that place, and call the 144in scope. To ensure this, either invoke the macro early in your C<BOOT:>
247function, which takes no arguments and returns nothing. 145section, or don't declare a local variable called C<cv>, either of which
146should be easy to do.
248 147
249The first call to C<perlinterp_release> will check for the presence 148Note that this is I<optional>, so you don't have to do that.
250of any supporting module, and if none is loaded, will create a dummy
251implementation where both C<pmapi_release> and C<pmapi_acquire> execute
252this function:
253 149
254 static void perl_multicore_nop (void) { }
255
256So in the case of no magical module being loaded, all calls except the
257first are two memory accesses and a predictable function call of an empty
258function.
259
260Of course, the overhead is much higher when these functions actually
261implement anything useful, but you always get what you pay for.
262
263=item Low Code and Data Size Overhead
264
265On a 64 bit system, F<perlmulticore.h> uses exactly C<8> octets (one
266pointer) of your data segment, to store the C<perl_multicore_api>
267pointer. In addition it creates a C<16> octet perl string to store the
268function pointers in, and stores it in a hash provided by perl for this
269purpose.
270
271This is pretty much the equivalent of executing this code:
272
273 $existing_hash{perl_multicore_api} = "123456781234567812345678";
274
275And that's it, which is, as I think, indeed very little.
276
277As for code size, on my amd64 system, every call to C<perlinterp_release>
278or C<perlinterp_acquire> results in a variation of the following 9-10
279octet sequence:
280
281 150> mov 0x200f23(%rip),%rax # <perl_multicore_api>
282 157> callq *0x8(%rax)
283
284The biggest part if the initialisation code, which consists of 11 lines of
285typical XS code. On my system, all the code in F<perlmulticore.h> compiles
286to less than 160 octets of read-only data.
287
288=item Broad Applicability
289
290While there are alternative ways to achieve the goal of parallel execution
291with threads that might be more efficient, this mechanism was chosen
292because it is very simple to retrofit existing modules with it, and it
293
294The design goals for this mechanism were to be simple to use, very
295efficient when not needed, low code and data size overhead and broad
296applicability.
297
298=back
299 150
300=head1 AUTHOR 151=head1 AUTHOR
301 152
302 Marc A. Lehmann <perlmulticore@schmorp.de> 153 Marc A. Lehmann <perlmulticore@schmorp.de>
154 http://perlmulticore.schmorp.de/
303 155
304=head1 LICENSE 156=head1 LICENSE
305 157
306The F<perlmulticore.h> is put into the public domain. Where this is legally 158The F<perlmulticore.h> header file is put into the public
159domain. Where this is legally not possible, or at your
307not possible, or at your option, it can be licensed under creativecommons 160option, it can be licensed under creativecommons CC0
308CC0 license: L<https://creativecommons.org/publicdomain/zero/1.0/>. 161license: L<https://creativecommons.org/publicdomain/zero/1.0/>.
309 162
310=cut 163=cut
311 164
312#endif 165*/
313 166
167/* version history
168 * 1.1 (1001) 2015-07-03: initial release.
169 * 1.2 (1002) 2019-03-03: introduce optional perlmulticore_support macro.
170 */
171#define PERL_MULTICORE_MAJOR 1 /* bumped on incompatible changes */
172#define PERL_MULTICORE_MINOR 2 /* bumped on every change */
173
174#if PERL_MULTICORE_DISABLE
175
176#define perlinterp_release() do { } while (0)
177#define perlinterp_acquire() do { } while (0)
178#define perlmulticore_support() do { } while (0)
179
180#else
181
182START_EXTERN_C
183
184/* this struct is shared between all modules, and currently */
185/* contain only the two function pointers for release/acquire */
314struct perl_multicore_api 186struct perl_multicore_api
315{ 187{
316 void (*pmapi_release)(void); 188 void (*pmapi_release)(void);
317 void (*pmapi_acquire)(void); 189 void (*pmapi_acquire)(void);
318}; 190};
319 191
320static void perl_multicore_init (void); 192static void perl_multicore_init (void);
321 193
322const struct perl_multicore_api perl_multicore_api_init = { perl_multicore_init, abort }; 194static const struct perl_multicore_api perl_multicore_api_init
195 = { perl_multicore_init, 0 };
323 196
324static struct perl_multicore_api *perl_multicore_api 197static struct perl_multicore_api *perl_multicore_api
325 = (struct perl_multicore_api *)&perl_multicore_api_init; 198 = (struct perl_multicore_api *)&perl_multicore_api_init;
326 199
327#define perlinterp_release() perl_multicore_api->pmapi_release () 200#define perlinterp_release() perl_multicore_api->pmapi_release ()
328#define perlinterp_acquire() perl_multicore_api->pmapi_acquire () 201#define perlinterp_acquire() perl_multicore_api->pmapi_acquire ()
329 202
203/* this is the release/acquire implementation used as fallback */
330static void 204static void
331perl_multicore_nop (void) 205perl_multicore_nop (void)
332{ 206{
333} 207}
334 208
209static const char perl_multicore_api_key[] = "perl_multicore_api";
210
211/* this is the initial implementation of "release" - it initialises */
212/* the api and then calls the real release function */
335static void 213static void
336perl_multicore_init (void) 214perl_multicore_init (void)
337{ 215{
338 dTHX; 216 dTHX;
339 217
340 /* check for existing API struct in PL_modglobal */ 218 /* check for existing API struct in PL_modglobal */
341 SV **api_svp = hv_fetch (PL_modglobal, "perl_multicore_api", sizeof ("perl_multicore_api") - 1, 1); 219 SV **api_svp = hv_fetch (PL_modglobal, perl_multicore_api_key,
220 sizeof (perl_multicore_api_key) - 1, 1);
342 221
343 if (SvPOKp (*api_svp)) 222 if (SvPOKp (*api_svp))
344 perl_multicore_api = (struct perl_multicore_api *)SvPVX (*api_svp); /* we have one, use the existing one */ 223 perl_multicore_api = (struct perl_multicore_api *)SvPVX (*api_svp); /* we have one, use the existing one */
345 else 224 else
346 { 225 {
347 /* create a new one with a dummy nop implementation */ 226 /* create a new one with a dummy nop implementation */
227 #ifdef NEWSV
348 SV *api_sv = NEWSV (0, sizeof (*perl_multicore_api)); 228 SV *api_sv = NEWSV (0, sizeof (*perl_multicore_api));
229 #else
230 SV *api_sv = newSV ( sizeof (*perl_multicore_api));
231 #endif
349 SvCUR_set (api_sv, sizeof (*perl_multicore_api)); 232 SvCUR_set (api_sv, sizeof (*perl_multicore_api));
350 SvPOK_only (api_sv); 233 SvPOK_only (api_sv);
351 perl_multicore_api = (struct perl_multicore_api *)SvPVX (api_sv); 234 perl_multicore_api = (struct perl_multicore_api *)SvPVX (api_sv);
352 perl_multicore_api->pmapi_release = 235 perl_multicore_api->pmapi_release =
353 perl_multicore_api->pmapi_acquire = perl_multicore_nop; 236 perl_multicore_api->pmapi_acquire = perl_multicore_nop;
356 239
357 /* call the real (or dummy) implementation now */ 240 /* call the real (or dummy) implementation now */
358 perlinterp_release (); 241 perlinterp_release ();
359} 242}
360 243
244#define perlmulticore_support() \
245 sv_setiv (get_sv ( \
246 form ("%s::PERLMULTICORE_SUPPORT", HvNAME (GvSTASH (CvGV (cv)))), \
247 GV_ADD | GV_ADDMULTI), \
248 PERL_MULTICORE_MAJOR * 1000 + PERL_MULTICORE_MINOR); \
249
250END_EXTERN_C
251
361#endif 252#endif
253
254#endif
255

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines