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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines