--- Async-Interrupt/Interrupt.pm 2009/07/02 15:13:03 1.2 +++ Async-Interrupt/Interrupt.pm 2009/07/12 16:33:26 1.9 @@ -9,28 +9,75 @@ =head1 DESCRIPTION This module implements a single feature only of interest to advanced perl -modules, namely asynchronous interruptions (think "unix signals", which +modules, namely asynchronous interruptions (think "UNIX signals", which are very similar). -Sometimes, modules wish to run code asynchronously (in another thread), -and then signal the perl interpreter on certain events. One common way is -to write some data to a pipe and use an event handling toolkit to watch -for I/O events. Another way is to send a signal. Those methods are slow, -and in the case of a pipe, also not asynchronous - it won't interrupt a -running perl interpreter. +Sometimes, modules wish to run code asynchronously (in another thread, +or from a signal handler), and then signal the perl interpreter on +certain events. One common way is to write some data to a pipe and use an +event handling toolkit to watch for I/O events. Another way is to send +a signal. Those methods are slow, and in the case of a pipe, also not +asynchronous - it won't interrupt a running perl interpreter. This module implements asynchronous notifications that enable you to -signal running perl code form another thread, asynchronously, without -issuing syscalls. +signal running perl code from another thread, asynchronously, and +sometimes even without using a single syscall. -It works by creating an C object for each such use. This -object stores a perl and/or a C-level callback that is invoked when the -C object gets signalled. It is executed at the next time -the perl interpreter is running (i.e. it will interrupt a computation, but -not an XS function or a syscall). +=head2 USAGE SCENARIOS + +=over 4 + +=item Race-free signal handling + +There seems to be no way to do race-free signal handling in perl: to +catch a signal, you have to execute Perl code, and between entering the +interpreter C on, fixing the race +completely. + +This can be used to implement the signal hadling in event loops, +e.g. L, L, L and so on. + +=item Background threads want speedy reporting + +Assume you want very exact timing, and you can spare an extra cpu core +for that. Then you can run an extra thread that signals your perl +interpreter. This means you can get a very exact timing source while your +perl code is number crunching, without even using a syscall to communicate +between your threads. + +For example the deliantra game server uses a variant of this technique +to interrupt background processes regularly to send map updates to game +clients. + +L and L could also use this to speed up result reporting. + +=item Speedy event loop invocation + +One could use this module e.g. in L to interrupt a running coro-thread +and cause it to enter the event loop. + +Or one could bind to C and tell some important sockets to send this +signal, causing the event loop to be entered to reduce network latency. + +=back + +=head2 HOW TO USE + +You can use this module by creating an C object for each +such event source. This object stores a perl and/or a C-level callback +that is invoked when the C object gets signalled. It is +executed at the next time the perl interpreter is running (i.e. it will +interrupt a computation, but not an XS function or a syscall). You can signal the C object either by calling it's C<< -->signal >> method, or, more commonly, by calling a C function. +->signal >> method, or, more commonly, by calling a C function. There is +also the built-in (POSIX) signal source. The C<< ->signal_func >> returns the address of the C function that is to be called (plus an argument to be used during the call). The signalling @@ -38,8 +85,10 @@ SIG_ATOMIC_MAX (guaranteed to allow at least 0..127). Since this kind of interruption is fast, but can only interrupt a -I interpreter, there is optional support for also signalling a -pipe - that means you can also wait for the pipe to become readable while +I interpreter, there is optional support for signalling a pipe +- that means you can also wait for the pipe to become readable (e.g. via +L or L). This, of course, incurs the overhead of a C +and C syscall. =over 4 @@ -50,7 +99,7 @@ no warnings; BEGIN { - $VERSION = '0.02'; + $VERSION = '0.041'; require XSLoader; XSLoader::load Async::Interrupt::, $VERSION; @@ -101,10 +150,19 @@ Note that, because the callback can be invoked at almost any time, you have to be careful at saving and restoring global variables that Perl -might use (the excetpion is C, which is aved and restored by +might use (the exception is C, which is saved and restored by Async::Interrupt). The callback itself runs as part of the perl context, so you can call any perl functions and modify any perl data structures (in -which case the requireemnts set out for C apply as well). +which case the requirements set out for C apply as well). + +=item signal => $signame_or_value + +When this parameter is specified, then the Async::Interrupt will hook the +given signal, that is, it will effectively call C<< ->signal (0) >> each time +the given signal is caught by the process. + +Only one async can hook a given signal, and the signal will be restored to +defaults when the Async::Interrupt object gets destroyed. =item pipe => [$fileno_or_fh_for_reading, $fileno_or_fh_for_writing] @@ -115,8 +173,11 @@ are written. It is required that the file handles are both in nonblocking mode. -(You can get a portable pipe and set non-blocking mode portably by using -e.g. L from the L distro). +You can get a portable pipe and set non-blocking mode portably by using +e.g. L from the L distribution. + +It is also possible to pass in a linux eventfd as both read and write +handle (which is faster than a pipe). The object will keep a reference to the file handles. @@ -130,7 +191,7 @@ sub new { my ($class, %arg) = @_; - bless \(_alloc $arg{cb}, @{$arg{c_cb}}[0,1], @{$arg{pipe}}[0,1]), $class + bless \(_alloc $arg{cb}, @{$arg{c_cb}}[0,1], @{$arg{pipe}}[0,1], $arg{signal}), $class } =item ($signal_func, $signal_arg) = $async->signal_func @@ -167,10 +228,41 @@ =item $async->block -Sometimes you need a "critical section" of code where - =item $async->unblock +Sometimes you need a "critical section" of code that will not be +interrupted by an Async::Interrupt. This can be implemented by calling C<< +$async->block >> before the critical section, and C<< $async->unblock >> +afterwards. + +Note that there must be exactly one call of C for every previous +call to C (i.e. calls can nest). + +Since ensuring this in the presence of exceptions and threads is +usually more difficult than you imagine, I recommend using C<< +$async->scoped_block >> instead. + +=item $async->scope_block + +This call C<< $async->block >> and installs a handler that is called when +the current scope is exited (via an exception, by canceling the Coro +thread, by calling last/goto etc.). + +This is the recommended (and fastest) way to implement critical sections. + +=item $async->pipe_enable + +=item $async->pipe_disable + +Enable/disable signalling the pipe when the interrupt occurs (default is +enabled). Writing to a pipe is relatively expensive, so it can be disabled +when you know you are not waiting for it (for example, with L you +could disable the pipe in a check watcher, and enable it in a prepare +watcher). + +Note that when C is in effect, no attempt to read from the +pipe will be done. + =cut 1; @@ -179,22 +271,23 @@ =head1 EXAMPLE -#TODO +There really should be a complete C/XS example. Bug me about it. Better +yet, create one. =head1 IMPLEMENTATION DETAILS AND LIMITATIONS -This module works by "hijacking" SIGKILL, which is guarenteed to be always -available in perl, but also cannot be caught, so is always available. +This module works by "hijacking" SIGKILL, which is guaranteed to always +exist, but also cannot be caught, so is always available. -Basically, this module fakes the receive of a SIGKILL signal and -then catches it. This makes normal signal handling slower (probably -unmeasurably), but has the advantage of not requiring a special runops nor -slowing down normal perl execution a bit. - -It assumes that C and C are both exception-safe to -modify (C is used by this module, and perl itself uses -C, so we can assume that this is quite portbale, at least w.r.t. -signals). +Basically, this module fakes the occurance of a SIGKILL signal and +then intercepts the interpreter handling it. This makes normal signal +handling slower (probably unmeasurably, though), but has the advantage +of not requiring a special runops function, nor slowing down normal perl +execution a bit. + +It assumes that C and C are both async-safe to modify +(C is used by this module, and perl itself uses C, so we +can assume that this is quite portable, at least w.r.t. signals). =head1 AUTHOR