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.8 by root, Sun Jun 28 18:37:52 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/)
6 */ 5 */
7 6
8#ifndef PERL_MULTICORE_H 7#ifndef PERL_MULTICORE_H
9#define PERL_MULTICORE_H 8#define PERL_MULTICORE_H
10 9
11#if 0 10/*
12 11
13=head1 NAME 12=head1 NAME
14 13
15perlmulticore.h - release the perl interpreter for other uses while doing hard work 14perlmulticore.h - the Perl Multicore Specification and Implementation
16 15
17=head1 SYNOPSIS 16=head1 SYNOPSIS
18 17
19 #include "perlmultiore.h" 18 #include "perlmultiore.h"
20 19
32 31
33The design goals for this mechanism were to be simple to use, very 32The design goals for this mechanism were to be simple to use, very
34efficient when not needed, low code and data size overhead and broad 33efficient when not needed, low code and data size overhead and broad
35applicability. 34applicability.
36 35
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
40includes this documentation, can be downloaded from
41L<http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>.
37 42
38=head1 HOW DO I USE THIS IN MY MODULES? 43=head1 HOW DO I USE THIS IN MY MODULES?
39 44
40The suage is very simple - you include this header file in your XS module. Then, before you 45The 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: 46do your lengthy operation, you release the perl interpreter:
42 47
43 perlinterp_release (); 48 perlinterp_release ();
44 49
45And when you are done with your computation, you acquire it again: 50And when you are done with your computation, you acquire it again:
46 51
47 perlinterp_acquire (); 52 perlinterp_acquire ();
48 53
49And that's it. This doesn't load any modules and consists of only a few 54And 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. 55machine instructions when no module to take advantage of it is loaded.
51 56
52Here is a simple example, an C<flock> wrapper implemented in XS. Unlike 57Here is a simple example, an C<flock> wrapper implemented in XS. Unlike
53perl's built-in C<flock>, it allows other threads (for example, those 58perl's built-in C<flock>, it allows other threads (for example, those
54provided by L<Coro>) to execute, instead of blocking the whole perl 59provided by L<Coro>) to execute, instead of blocking the whole perl
55interpreter. For the sake of this example, it requires a file descriptor 60interpreter. For the sake of this example, it requires a file descriptor
56instead of a handle. 61instead of a handle.
57 62
58 #include "perlmulticore.h" /* this header file */ 63 #include "perlmulticore.h" // this header file
59 64
60 // and in the XS portion 65 // and in the XS portion
61 int flock (int fd, int operation) 66 int flock (int fd, int operation)
62 CODE: 67 CODE:
63 perlinterp_release (); 68 perlinterp_release ();
80 85
81=head2 HOW ABOUT NOT-SO LONG WORK? 86=head2 HOW ABOUT NOT-SO LONG WORK?
82 87
83Sometimes you don't know how long your code will take - in a compression 88Sometimes 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 89library 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 90a while, while 50 Bytes will compress so fast that even attempting to do
86something else could be more costly than just doing it. 91something else could be more costly than just doing it.
87 92
88This is a very hard problem to solve. The best you can do at the moment is 93This 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 94to release the perl interpreter only when you think the work to be done
90justifies the expense. 95justifies the expense.
102Make sure the if conditions are exactly the same and don't change, so you 107Make sure the if conditions are exactly the same and don't change, so you
103always call acquire when you release, and vice versa. 108always call acquire when you release, and vice versa.
104 109
105When you don't have a handy indicator, you might still do something 110When 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 111useful. 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 112expect the lock to be available immediately in most cases, you could try
108with C<F_SETLK> (which doesn't wait), and only release/wait/acquire when 113with C<F_SETLK> (which doesn't wait), and only release/wait/acquire when
109the lock couldn't be set: 114the lock couldn't be set:
110 115
111 int res = fcntl (fd, F_SETLK, &flock); 116 int res = fcntl (fd, F_SETLK, &flock);
112 117
113 if (res) 118 if (res)
114 { 119 {
115 // error, assume lock is held by another process and do it the slow way 120 // error, assume lock is held by another process and do it the slow way
116 perlinterp_release (); 121 perlinterp_release ();
117 res = fcntl (fd, F_SETLKW, &flock); 122 res = fcntl (fd, F_SETLKW, &flock);
118 perlinterp_release (); 123 perlinterp_acquire ();
119 } 124 }
120 125
121=head1 THE HARD AND FAST RULES 126=head1 THE HARD AND FAST RULES
122 127
123As with everything, there are a number of rules to follow. 128As with everything, there are a number of rules to follow.
152 157
153 if (!function_that_fails_with_0_return_value ()) 158 if (!function_that_fails_with_0_return_value ())
154 { 159 {
155 perlinterp_acquire (); 160 perlinterp_acquire ();
156 croak ("error"); 161 croak ("error");
162 // croak doesn't return
157 } 163 }
158 164
159 perlinterp_acquire (); 165 perlinterp_acquire ();
160 // do other stuff 166 // do other stuff
161 167
181thread-safe, too. 187thread-safe, too.
182 188
183Always assume that the code between C<perlinterp_release> and 189Always assume that the code between C<perlinterp_release> and
184C<perlinterp_acquire> is executed in parallel on multiple CPUs at the same 190C<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 191time. If your code can't cope with that, you could consider using a mutex
186to only allow one such execution, which is sitll better than blocking 192to only allow one such execution, which is still better than blocking
187everybody else from doing anything: 193everybody else from doing anything:
188 194
189 static pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER; 195 static pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
190 196
191 perlinterp_release (); 197 perlinterp_release ();
192 pthread_mutex_lock (&my_mutex); 198 pthread_mutex_lock (&my_mutex);
193 do_your_non_thread_safe_thing (); 199 do_your_non_thread_safe_thing ();
194 pthread_mutex_unlock (&my_mutex); 200 pthread_mutex_unlock (&my_mutex);
195 perlinterp_acquire (); 201 perlinterp_acquire ();
196 202
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. 203=item I<Don't> get confused by having to release first.
202 204
203In many real world scenarios, you acquire a resource, do something, then 205In 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 206release 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 207the resource (the perl interpreter) so you have to I<release> first, and
216=over 4 218=over 4
217 219
218=item Simple to Use 220=item Simple to Use
219 221
220All you have to do is identify the place in your existing code where you 222All 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 223stop touching perl stuff, do your actual work, and start touching perl
222stuff again. 224stuff again.
223 225
224Then slap C<perlinterp_release ()> and C<perlinterp_acquire ()> around the 226Then slap C<perlinterp_release ()> and C<perlinterp_acquire ()> around the
225actual work code. 227actual work code.
226 228
250first are two memory accesses and a predictable function call of an empty 252first are two memory accesses and a predictable function call of an empty
251function. 253function.
252 254
253Of course, the overhead is much higher when these functions actually 255Of course, the overhead is much higher when these functions actually
254implement anything useful, but you always get what you pay for. 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.
255 267
256=item Low Code and Data Size Overhead 268=item Low Code and Data Size Overhead
257 269
258On a 64 bit system, F<perlmulticore.h> uses exactly C<8> octets (one 270On 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> 271pointer) of your data segment, to store the C<perl_multicore_api>
272octet sequence: 284octet sequence:
273 285
274 150> mov 0x200f23(%rip),%rax # <perl_multicore_api> 286 150> mov 0x200f23(%rip),%rax # <perl_multicore_api>
275 157> callq *0x8(%rax) 287 157> callq *0x8(%rax)
276 288
277amd64 code sure is bloated.
278
279The biggest part if the initialisation code, which consists of 11 lines of 289The 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 290typical XS code. On my system, all the code in F<perlmulticore.h> compiles
281to less than 160 octets of read-only data. 291to less than 160 octets of read-only data.
282 292
283=item Broad Applicability 293=item Broad Applicability
290efficient when not needed, low code and data size overhead and broad 300efficient when not needed, low code and data size overhead and broad
291applicability. 301applicability.
292 302
293=back 303=back
294 304
305
306=head1 DISABLING PERL MULTICORE AT COMPILE TIME
307
308You can disable the complete perl multicore API by defining the
309symbol C<PERL_MULTICORE_DISABLE> to C<1> (e.g. by specifying
310F<-DPERL_MULTICORE_DISABLE> as compiler argument).
311
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
316platforms that do not support threading at all for example.
317
318
295=head1 AUTHOR 319=head1 AUTHOR
296 320
297 Marc A. Lehmann <perlmulticore@schmorp.de> 321 Marc A. Lehmann <perlmulticore@schmorp.de>
322 http://perlmulticore.schmorp.de/
298 323
299=head1 LICENSE 324=head1 LICENSE
300 325
301The F<perlmulticore.h> is put into the public domain. Where this is legally 326The F<perlmulticore.h> header file is put into the public
327domain. Where this is legally not possible, or at your
302not possible, or at your option, it can be licensed under creativecommons 328option, it can be licensed under creativecommons CC0
303CC0 license: L<https://creativecommons.org/publicdomain/zero/1.0/>. 329license: L<https://creativecommons.org/publicdomain/zero/1.0/>.
304 330
305=cut 331=cut
306 332
307#endif 333*/
308 334
335#if PERL_MULTICORE_DISABLE
336
337#define perlinterp_release() do { } while (0)
338#define perlinterp_acquire() do { } while (0)
339
340#else
341
342/* this struct is shared between all modules, and currently */
343/* contain only the two function pointers for release/acquire */
309struct perl_multicore_api 344struct perl_multicore_api
310{ 345{
311 void (*pmapi_release)(void); 346 void (*pmapi_release)(void);
312 void (*pmapi_acquire)(void); 347 void (*pmapi_acquire)(void);
313}; 348};
320 = (struct perl_multicore_api *)&perl_multicore_api_init; 355 = (struct perl_multicore_api *)&perl_multicore_api_init;
321 356
322#define perlinterp_release() perl_multicore_api->pmapi_release () 357#define perlinterp_release() perl_multicore_api->pmapi_release ()
323#define perlinterp_acquire() perl_multicore_api->pmapi_acquire () 358#define perlinterp_acquire() perl_multicore_api->pmapi_acquire ()
324 359
360/* this is the release/acquire implementation used as fallback */
325static void 361static void
326perl_multicore_nop (void) 362perl_multicore_nop (void)
327{ 363{
328} 364}
329 365
366/* this is the initial implementation of "release" - it initialises */
367/* the api and then calls the real release function */
330static void 368static void
331perl_multicore_init (void) 369perl_multicore_init (void)
332{ 370{
333 dTHX; 371 dTHX;
334 372
352 /* call the real (or dummy) implementation now */ 390 /* call the real (or dummy) implementation now */
353 perlinterp_release (); 391 perlinterp_release ();
354} 392}
355 393
356#endif 394#endif
395
396#endif

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines