1 | TODO: 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 | |
15 | perlmulticore.h - release the perl interpreter for other uses while doing hard work |
14 | perlmulticore.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 | |
… | |
… | |
35 | applicability. |
34 | applicability. |
36 | |
35 | |
37 | The newest version of this document can be found at |
36 | The newest version of this document can be found at |
38 | L<http://pod.tst.eu/http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>. |
37 | L<http://pod.tst.eu/http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>. |
39 | |
38 | |
40 | The nwest version of the header fgile itself, which |
39 | The newest version of the header file itself, which |
41 | includes this documentation, can be downloaded from |
40 | includes this documentation, can be downloaded from |
42 | L<http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>. |
41 | L<http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>. |
43 | |
42 | |
44 | =head1 HOW DO I USE THIS IN MY MODULES? |
43 | =head1 HOW DO I USE THIS IN MY MODULES? |
45 | |
44 | |
… | |
… | |
59 | perl's built-in C<flock>, it allows other threads (for example, those |
58 | perl's built-in C<flock>, it allows other threads (for example, those |
60 | provided by L<Coro>) to execute, instead of blocking the whole perl |
59 | provided by L<Coro>) to execute, instead of blocking the whole perl |
61 | interpreter. For the sake of this example, it requires a file descriptor |
60 | interpreter. For the sake of this example, it requires a file descriptor |
62 | instead of a handle. |
61 | instead of a handle. |
63 | |
62 | |
64 | #include "perlmulticore.h" /* this header file */ |
63 | #include "perlmulticore.h" // this header file |
65 | |
64 | |
66 | // and in the XS portion |
65 | // and in the XS portion |
67 | int flock (int fd, int operation) |
66 | int flock (int fd, int operation) |
68 | CODE: |
67 | CODE: |
69 | perlinterp_release (); |
68 | perlinterp_release (); |
… | |
… | |
119 | if (res) |
118 | if (res) |
120 | { |
119 | { |
121 | // 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 |
122 | perlinterp_release (); |
121 | perlinterp_release (); |
123 | res = fcntl (fd, F_SETLKW, &flock); |
122 | res = fcntl (fd, F_SETLKW, &flock); |
124 | perlinterp_release (); |
123 | perlinterp_acquire (); |
125 | } |
124 | } |
126 | |
125 | |
127 | =head1 THE HARD AND FAST RULES |
126 | =head1 THE HARD AND FAST RULES |
128 | |
127 | |
129 | As with everything, there are a number of rules to follow. |
128 | As with everything, there are a number of rules to follow. |
… | |
… | |
199 | pthread_mutex_lock (&my_mutex); |
198 | pthread_mutex_lock (&my_mutex); |
200 | do_your_non_thread_safe_thing (); |
199 | do_your_non_thread_safe_thing (); |
201 | pthread_mutex_unlock (&my_mutex); |
200 | pthread_mutex_unlock (&my_mutex); |
202 | perlinterp_acquire (); |
201 | perlinterp_acquire (); |
203 | |
202 | |
204 | This isn't as trivial as it looks though, as you need to find out which |
|
|
205 | threading system is in use (with L<Coro::Multicore>, it currently is |
|
|
206 | always pthreads). |
|
|
207 | |
|
|
208 | =item I<Don't> get confused by having to release first. |
203 | =item I<Don't> get confused by having to release first. |
209 | |
204 | |
210 | In many real world scenarios, you acquire a resource, do something, then |
205 | In many real world scenarios, you acquire a resource, do something, then |
211 | release it again. Don't let this confuse you, with this, you already own |
206 | release it again. Don't let this confuse you, with this, you already own |
212 | the resource (the perl interpreter) so you have to I<release> first, and |
207 | the resource (the perl interpreter) so you have to I<release> first, and |
… | |
… | |
257 | first are two memory accesses and a predictable function call of an empty |
252 | first are two memory accesses and a predictable function call of an empty |
258 | function. |
253 | function. |
259 | |
254 | |
260 | Of course, the overhead is much higher when these functions actually |
255 | Of course, the overhead is much higher when these functions actually |
261 | implement anything useful, but you always get what you pay for. |
256 | implement anything useful, but you always get what you pay for. |
|
|
257 | |
|
|
258 | With L<Coro::Multicore>, every release/acquire involves two pthread |
|
|
259 | switches, two coro thread switches, a bunch of syscalls, and sometimes |
|
|
260 | interacting with the event loop. |
|
|
261 | |
|
|
262 | A dedicated thread pool such as the one L<IO::AIO> uses could reduce |
|
|
263 | these overheads, and would also reduce the dependencies (L<AnyEvent> is a |
|
|
264 | smaller and more portable dependency than L<Coro>), but it would require a |
|
|
265 | lot more work on the side of the module author wanting to support it than |
|
|
266 | this solution. |
262 | |
267 | |
263 | =item Low Code and Data Size Overhead |
268 | =item Low Code and Data Size Overhead |
264 | |
269 | |
265 | On a 64 bit system, F<perlmulticore.h> uses exactly C<8> octets (one |
270 | On a 64 bit system, F<perlmulticore.h> uses exactly C<8> octets (one |
266 | pointer) of your data segment, to store the C<perl_multicore_api> |
271 | pointer) of your data segment, to store the C<perl_multicore_api> |
… | |
… | |
295 | efficient when not needed, low code and data size overhead and broad |
300 | efficient when not needed, low code and data size overhead and broad |
296 | applicability. |
301 | applicability. |
297 | |
302 | |
298 | =back |
303 | =back |
299 | |
304 | |
|
|
305 | |
|
|
306 | =head1 DISABLING PERL MULTICORE AT COMPILE TIME |
|
|
307 | |
|
|
308 | You can disable the complete perl multicore API by defining the |
|
|
309 | symbol C<PERL_MULTICORE_DISABLE> to C<1> (e.g. by specifying |
|
|
310 | F<-DPERL_MULTICORE_DISABLE> as compiler argument). |
|
|
311 | |
|
|
312 | This 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 | |
|
|
315 | This could be added to perl's C<CPPFLAGS> when configuring perl on |
|
|
316 | platforms that do not support threading at all for example. |
|
|
317 | |
|
|
318 | |
300 | =head1 AUTHOR |
319 | =head1 AUTHOR |
301 | |
320 | |
302 | Marc A. Lehmann <perlmulticore@schmorp.de> |
321 | Marc A. Lehmann <perlmulticore@schmorp.de> |
|
|
322 | http://perlmulticore.schmorp.de/ |
303 | |
323 | |
304 | =head1 LICENSE |
324 | =head1 LICENSE |
305 | |
325 | |
306 | The F<perlmulticore.h> is put into the public domain. Where this is legally |
326 | The F<perlmulticore.h> header file is put into the public |
|
|
327 | domain. Where this is legally not possible, or at your |
307 | not possible, or at your option, it can be licensed under creativecommons |
328 | option, it can be licensed under creativecommons CC0 |
308 | CC0 license: L<https://creativecommons.org/publicdomain/zero/1.0/>. |
329 | license: L<https://creativecommons.org/publicdomain/zero/1.0/>. |
309 | |
330 | |
310 | =cut |
331 | =cut |
311 | |
332 | |
312 | #endif |
333 | */ |
313 | |
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 */ |
314 | struct perl_multicore_api |
344 | struct perl_multicore_api |
315 | { |
345 | { |
316 | void (*pmapi_release)(void); |
346 | void (*pmapi_release)(void); |
317 | void (*pmapi_acquire)(void); |
347 | void (*pmapi_acquire)(void); |
318 | }; |
348 | }; |
… | |
… | |
325 | = (struct perl_multicore_api *)&perl_multicore_api_init; |
355 | = (struct perl_multicore_api *)&perl_multicore_api_init; |
326 | |
356 | |
327 | #define perlinterp_release() perl_multicore_api->pmapi_release () |
357 | #define perlinterp_release() perl_multicore_api->pmapi_release () |
328 | #define perlinterp_acquire() perl_multicore_api->pmapi_acquire () |
358 | #define perlinterp_acquire() perl_multicore_api->pmapi_acquire () |
329 | |
359 | |
|
|
360 | /* this is the release/acquire implementation used as fallback */ |
330 | static void |
361 | static void |
331 | perl_multicore_nop (void) |
362 | perl_multicore_nop (void) |
332 | { |
363 | { |
333 | } |
364 | } |
334 | |
365 | |
|
|
366 | /* this is the initial implementation of "release" - it initialises */ |
|
|
367 | /* the api and then calls the real release function */ |
335 | static void |
368 | static void |
336 | perl_multicore_init (void) |
369 | perl_multicore_init (void) |
337 | { |
370 | { |
338 | dTHX; |
371 | dTHX; |
339 | |
372 | |
… | |
… | |
357 | /* call the real (or dummy) implementation now */ |
390 | /* call the real (or dummy) implementation now */ |
358 | perlinterp_release (); |
391 | perlinterp_release (); |
359 | } |
392 | } |
360 | |
393 | |
361 | #endif |
394 | #endif |
|
|
395 | |
|
|
396 | #endif |