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.9 by root, Sun Jun 28 18:43:17 2015 UTC vs.
Revision 1.15 by root, Tue Mar 5 12:10:31 2019 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines