Coro-Multicore
view release on metacpan or search on metacpan
Coro::Multicore - make coro threads on multiple cores with specially
supported modules
SYNOPSIS
# when you DO control the main event loop, e.g. in the main program
use Coro::Multicore; # enable by default
Coro::Multicore::scoped_disable;
AE::cv->recv; # or EV::run, 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
async {
Coro::Multicore::scoped_enable;
# blocking is safe in your own threads
...
};
DESCRIPTION
While Coro threads (unlike ithreads) provide real threads similar to
pthreads, python threads and so on, they do not run in parallel to each
other even on machines with multiple CPUs or multiple CPU cores.
This module lifts this restriction under two very specific but useful
conditions: firstly, the coro thread executes in XS code and does not
touch any perl data structures, and secondly, the XS code is specially
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 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 Coro or Coro::Multicore, and therefore could be used,
without changes, with other, similar, modules, or even the perl core,
should it gain real thread support anytime soon. See
<http://perlmulticore.schmorp.de/> for more info on how to prepare a
module to allow parallel execution. Preparing an existing module is
easy, doesn't add much overhead and no dependencies.
This module is an AnyEvent user (and also, if not obvious, uses Coro).
HOW TO USE IT
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 "Coro::Multicore" - it would
simply calculate the MD5 digest and block execution of anything else.
But with "Coro::Multicore" 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.
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 ensure it works is to disable
"Coro::Multicore" 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 *program* and not a *module*, then you can enable
"Coro::Multicore" by default, and disable it in your event loop thread:
# example 1, separate thread for event loop
use EV;
use Coro;
use Coro::Multicore;
async {
Coro::Multicore::scoped_disable;
EV::run;
};
# do something else
# example 2, run event loop as main program
use EV;
use Coro;
use Coro::Multicore;
Coro::Multicore::scoped_disable;
... initialisation
EV::run;
The latter form is usually better and more idiomatic - the main thread
is the best place to run the event loop.
Often you want to do some initialisation before running the event loop.
The most efficient way to do that is to put your intialisation code (and
main program) into its own thread and run the event loop in your main
program:
use AnyEvent::Loop;
use Coro::Multicore; # enable by default
async {
load_data;
do_other_init;
bind_socket;
...
};
Coro::Multicore::scoped_disable;
AnyEvent::Loop::run;
This has the effect of running the event loop first, so the
initialisation code can block if it wants to.
If this is too cumbersome but you still want to make sure you can call
blocking functions before entering the event loop, you can keep
"Coro::Multicore" disabled till you cna run the event loop:
use AnyEvent::Loop;
( run in 0.987 second using v1.01-cache-2.11-cpan-39bf76dae61 )