Multicore(3)
πΈ NAME πΈ
Multicore(3) is the manual page for Coro::Multicore β it's giving "make coro threads on multiple cores with specially supported modules". No cap. π«Ά
β¨ 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 π
Okay so Coro threads (unlike ithreads) are real threads like pthreads, python threads and so on. But they do not run in parallel to each other even on machines with multiple CPUs or multiple CPU cores. π
This module lifts that 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. β¨
That means when you call an XS function of a module prepared for it, this XS function can execute in parallel to any other Coro threads. It's useful for CPU bound tasks (like cryptography) and I/O bound tasks (like 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. So it 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 be 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, 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 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 a 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 initialisation 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 until you can run the event loop:
use AnyEvent::Loop;
use Coro::Multicore (); # disable by default
load_data;
do_other_init;
bind_socket;
...
Coro::Multicore::scoped_disable; # disable for event loop
Coro::Multicore::enable 1; # enable for the rest of the program
AnyEvent::Loop::run;
π§ USE IT IN A MODULE π§
When you do not 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 caller's 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 Coro::Multicore 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 its own Coro 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 Coro::Multicore by not enabling it by 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
};
π EXPORTS π
This module does not (at the moment) export any symbols. It does, however, export "behaviour" β if you use the default import, then Coro::Multicore will be enabled for all threads and all callers in the whole program:
use Coro::Multicore;
In a module where you don't control what else might be loaded and run, you might want to be more conservative, and not import anything. This has the effect of not enabling the functionality by default, so you have to enable it per scope:
use Coro::Multicore ();
sub myfunc {
Coro::Multicore::scoped_enable;
# from here to the end of this function, and in any functions
# called from this function, tasks will be executed asynchronously.
}
π API FUNCTIONS π
$previous = Coro::Multicore::enable [$enable]β This function enables (if$enableis true) or disables (if$enableis false) the multicore functionality globally. By default, it is enabled. π
This can be used to effectively disable this module's functionality by default, and enable it only for selected threads or scopes, by calling Coro::Multicore::scoped_enable. β¨
Note that this setting only affects the global default β it will not reflect whether multicore functionality is enabled for the current thread. π
The function returns the previous value of the enable flag. π«Ά
Coro::Multicore::scoped_enableβ This function instructs Coro::Multicore to handle all requests executed in the current coro thread, from the call to the end of the current scope. πΈ
Calls to scoped_enable and scoped_disable don't nest very well at the moment, so don't nest them. π
Coro::Multicore::scoped_disableβ The opposite ofCoro::Multicore::scope_disable: instructs Coro::Multicore to not handle the next multicore-enabled request. π
π₯ 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 execute on the same database handle for the patched DBD::mysql (see the registry <http://perlmulticore.schmorp.de/registry>), this will almost certainly not work, despite DBD::mysql and libmysqlclient 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 handles (e.g. database connections for DBD modules, or digest objects for Digest modules), and some can be called at any time (such as the md5 function in Digest::MD5). π§
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 its own handle object. π
π EXCEPTIONS AND THREAD CANCELLATION π
okay so this is the part where we get spicy πΆοΈ
Coro lets you cancel threads even when they're running inside an XS function (yes, the cancel vs. cancel methods). And same energy: Coro lets you throw exceptions at threads β like with the throw method β while they're chilling inside an XS function.
Doing this is questionable and dangerous even with normal Coro threads, bestie. Both things are supported in this module, but with potentially unwanted effects. What follows describes the current implementation and is subject to change. It's written down mainly so that when stuff goes wrong, you can at least understand what went wrong. π
π«§ EXCEPTIONS
When a thread that has currently released the perl interpreter (say, because it's executing a perlmulticore enabled XS function) receives an exception, it will at first just... keep going. Normally. As if nothing happened. π
Then, after it acquires the perl interpreter again, it will throw the exception it previously received. To be specific: when a thread calls perlinterp_acquire () and it has received an exception, then perlinterp_acquire () will not return β it will die instead. π
Most code that's been updated for perlmulticore support will not expect this, and might leave internal state corrupted to some extent. so like. heads up.
π¦ 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, since the code isn't allowed to touch perl data structures until it reacquires the interpreter.
Then the call to perlinterp_acquire () will block indefinitely, leaking the (OS level) thread. π³οΈ
Safe cancellation will simply fail in this case, so it is still "safe" to call. periodt. β¨
π INTERACTION WITH OTHER SOFTWARE π
This module is very similar to other environments where perl interpreters get moved between threads, like mod_perl2, and the same caveats apply. ngl it's giving "same vibes, same problems."
I want to spell out the most important ones:
π§΅ pthreads usage
Any creation of pthreads makes it impossible to fork portably from a perl program, because forking from within a threaded program will leave the program in a state similar to a signal handler. While it might work on some platforms (as an extension), this might also result in silent data corruption. It also seems to work most of the time, so it's hard to test for this. π¬
I recommend using something like AnyEvent::Fork, which can create subprocesses safely (via Proc::FastSpawn).
Similar issues exist for signal handlers, although this module works hard to keep safe perl signals safe. π
π¦ module support
This module moves the same perl interpreter between different threads. Some modules might get confused by that (although this can usually be considered a bug). This is a rare case though. no cap.
β° event loop reliance
To be able to wake up programs waiting for results, this module relies on an active event loop (via AnyEvent). This is used to notify the perl interpreter when the asynchronous task is done.
Since event loops typically fail to work properly after a fork, this means that some operations that were formerly working will now hang after fork. π
A workaround is to call Coro::Multicore::enable 0 after a fork to disable the module.
Future versions of this module might do this automatically. π
π BUGS & LIMITATIONS π
π (OS-) threads are never released
At the moment, threads that were created once will never be freed. They will be reused for asynchronous requests, though, so as long as you limit the maximum number of concurrent asynchronous tasks, this will also limit the maximum number of threads created.
The idle threads are not necessarily using a lot of resources: on GNU/Linux + glibc, each thread takes about 8KiB of userspace memory + whatever the kernel needs (probably less than 8KiB).
Future versions will likely lift this limitation. π«Ά
π The enable_times feature of Coro is messed up
The enable_times feature uses the per-thread timer to measure per-thread execution time, but since Coro::Multicore runs threads on different pthreads it will get the wrong times. Real times are not affected. so like, only the fake ones are lying to you π
π΄ Fork support
Due to the nature of threads, you are not allowed to use this module in a forked child normally, with one exception: if you don't create any threads in the parent, then it is safe to start using it in a forked child. π§
π AUTHOR π
Marc Lehmann <schmorp@schmorp.de>
http://software.schmorp.de/pkg/AnyEvent-XSThreadPool.html
Additional thanks to ZsbΓ‘n Ambrus, who gave considerable desing input for this module and the perl multicore specification. π (that's a typo in the source, not me, I'm just being faithful bestie β¨)
and that's the whole page, mwah π
this one came from perl v5.42.0 and was dated 2025-07-25, and it's filed under Multicore(3) β go forth and multicore responsibly, ily π«ΆπΈ
π¦ where this page comes from
- fedora 43: perl-Coro-Multicore 1.07-17.fc43 Β· (GPL-1.0-or-later OR Artistic-1.0-Perl) AND (LicenseRef-Fedora-Public-Domain OR CC0-1.0)
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms