--- Coro-Multicore/Multicore.pm 2015/07/27 12:05:09 1.9 +++ Coro-Multicore/Multicore.pm 2015/07/27 14:32:33 1.10 @@ -9,9 +9,10 @@ use Coro::Multicore; # enable by default Coro::Multicore::scoped_disable; - EV::loop; # or AnyEvent::Loop::run, Event::loop, AE::cv->recv, ... + AE::cv->recv; # or EV::loop, AnyEvent::Loop::run, Event::loop, ... # when you DO NOT control the event loop, e.g. in a module on CPAN + # do nothing (see HOW TO USE IT) or something like this: use Coro::Multicore (); # disable by default @@ -38,7 +39,11 @@ prepared to allow this. This means that, when you call an XS function of a module prepared for it, -this XS function can execute in parallel to any other Coro threads. +this XS function can execute in parallel to any other Coro threads. This +is useful for both CPU bound tasks (such as cryptography) as well as I/O +bound tasks (such as loading an image from disk). It can also be used +to do stuff in parallel via APIs that were not meant for this, such as +database accesses via DBI. The mechanism to support this is easily added to existing modules and is independent of L or L, and therefore @@ -53,14 +58,100 @@ =head1 HOW TO USE IT -It could hardly be simpler - if you use coro threads, and before you call -a supported lengthy operation implemented in XS, use this module and other -coro threads can run in parallel: +Quick explanation: decide whether you control the main program/the event +loop and choose one of the two styles from the SYNOPSIS. +Longer explanation: There are two major modes this module can used in - +supported operations run asynchronously either by default, or only when +requested. The reason you might not want to enable this module for all +operations by default is compatibility with existing code: + +Since this module integrates into an event loop and you must not normally +block and wait for something in an event loop callbacks. Now imagine +somebody patches your favourite module (e.g. Digest::MD5) to take +advantage of of the Perl Multicore API. + +Then code that runs in an event loop callback and executes +Digest::MD5::md5 would work fine without C - it would +simply calculate the MD5 digest and block execution of anything else. But +with C enabled, the same operation would try to run other +threads. And when those wait for events, there is no event loop anymore, +as the event loop thread is busy doing the MD5 calculation, leading to a +deadlock. + +=head2 USE IT IN THE MAIN PROGRAM + +One way to avoid this is to not run perlmulticore enabled functions +in any callbacks. A simpler way to snure it works is to disable +C thread switching in event loop callbacks, and enable it +everywhere else. + +Therefore, if you control the event loop, as is usually the case when +you write I and not a I, then you can enable C +by default, and disable it in your event loop thread: + + # example 1, separate thread for event loop + + use EV; + use Coro; use Coro::Multicore; -This module has no important API functions to learn or remember. All you -need to do is I it before you can take advantage of it. + async { + Coro::Multicore::scoped_disable; + EV::loop; + }; + + # do something else + + # example 2, run event loop as main program + + use EV; + use Coro; + use Coro::Multicore; + + Coro::Multicore::scoped_disable; + + ... initialisation + + EV::loop; + +The latter form is usually better and more idiomatic - the main thread is +the best place to run the event loop. + +=head2 USE IT IN A MODULE + +When you I control the event loop, for example, because you want +to use this from a module you published on CPAN, then the previous method +doesn't work. + +However, this is not normally a problem in practise - most modules only +do work at request of the caller. In that case, you might not care +whether it does block other threads or not, as this would be the callers +responsibility (or decision), and by extension, a decision for the main +program. + +So unless you use XS and want your XS functions to run asynchronously, +you don't have to worry about C at all - if you +happen to call XS functions that are multicore-enabled and your +caller has configured things correctly, they will automatically run +asynchronously. Or in other words: nothing needs to be done at all, which +also means that this method works fine for existing pure-perl modules, +without having to change them at all. + +Only if your module runs it's own L threads could it be an +issue - maybe your module implements some kind of job pool and relies +on certain operations to run asynchronously. Then you can still use +C by not enabling it be default and only enabling it in +your own threads: + + use Coro; + use Coro::Multicore (); # note the () to disable by default + + async { + Coro::Multicore::scoped_enable; + + # do things asynchronously by calling perlmulticore-enabled functions + }; =head2 EXPORTS @@ -95,7 +186,7 @@ This can be used to effectively disable this module's functionality by default, and enable it only for selected threads or scopes, by calling -C. +C. The function returns the previous value of the enable flag. @@ -122,7 +213,7 @@ use AnyEvent (); BEGIN { - our $VERSION = 0.02; + our $VERSION = 0.03; use XSLoader; XSLoader::load __PACKAGE__, $VERSION; @@ -140,6 +231,73 @@ our $WATCHER = AE::io fd, 0, \&poll; +=head1 THREAD SAFETY OF SUPPORTING XS MODULES + +Just because an XS module supports perlmulticore might not immediately +make it reentrant. For example, while you can (try to) call C +on the same database handle for the patched C (see the +L), this will almost +certainly not work, despite C and C being +thread safe and reentrant - just not on the same database handle. + +Many modules have limitations such as these - some can only be called +concurrently from a single thread as they use global variables, some +can only be called concurrently on different I (e.g. database +connections for DBD modules, or digest objects for Digest modules), +and some can be called at any time (such as the C function in +C). + +Generally, you only have to be careful with the very few modules that use +global variables or rely on C libraries that aren't thread-safe, which +should be documented clearly in the module documentation. + +Most modules are either perfectly reentrant, or at least reentrant as long +as you give every thread it's own I object. + +=head1 EXCEPTIONS AND THREAD CANCELLATION + +L allows you to cancel threads even when they execute within an XS +function (C vs. C methods). Similarly, L allows you +to send exceptions (e.g. via the C method) to threads executing +inside an XS function. + +While doing this is questionable and dangerous with normal Coro threads +already, they are both supported in this module, although with potentially +unwanted effects. The following describes the current implementation and +is subject to change. It is described primarily so you can understand what +went wrong, if things go wrong. + +=over 4 + +=item EXCEPTIONS + +When a thread that has currently released the perl interpreter (e.g. +because it is executing a perlmulticore enabled XS function) receives an exception, it will +at first continue normally. + +After acquiring the perl interpreter again, it will throw the +exception it previously received. More specifically, when a thread +calls C and has received an exception, then +C will not return but instead C. + +Most code that has been updated for perlmulticore support will not expect +this, and might leave internal state corrupted to some extent. + +=item CANCELLATION + +Unsafe cancellation on a thread that has released the perl interpreter +frees its resources, but let's the XS code continue at first. This should +not lead to corruption on the perl level, as the code isn't allowed to +touch perl data structures until it reacquires the interpreter. + +The call to C will then block indefinitely, leaking +the (OS level) thread. + +Safe cancellation will simply fail in this case, so is still "safe" to +call. + +=back + =head1 INTERACTION WITH OTHER SOFTWARE This module is very similar to other environments where perl interpreters