Async-Interrupt

 view release on metacpan or  search on metacpan

Interrupt.pm  view on Meta::CPAN

Finally, the I/O callback for the event pipe handles the signals:

   sub _signal_check {
      # drain the pipe first
      $SIGPIPE->drain;

      # two loops, just to be sure
      while (%SIGNAL_RECEIVED) {
         for (keys %SIGNAL_RECEIVED) {
            delete $SIGNAL_RECEIVED{$_};
            warn "signal $_ received\n";
         }
      }
   }

=head2 Interrupt perl from another thread

This example interrupts the Perl interpreter from another thread, via the
XS API. This is used by e.g. the L<EV::Loop::Async> module.

On the Perl level, a new loop object (which contains the thread)
is created, by first calling some XS constructor, querying the
C-level callback function and feeding that as the C<c_cb> into the
Async::Interrupt constructor:

   my $self = XS_thread_constructor;
   my ($c_func, $c_arg) = _c_func $self; # return the c callback
   my $asy = new Async::Interrupt c_cb => [$c_func, $c_arg];

Then the newly created Interrupt object is queried for the signaling
function that the newly created thread should call, and this is in turn
told to the thread object:

   _attach $self, $asy->signal_func;

So to repeat: first the XS object is created, then it is queried for the
callback that should be called when the Interrupt object gets signalled.

Then the interrupt object is queried for the callback function that the
thread should call to signal the Interrupt object, and this callback is
then attached to the thread.

You have to be careful that your new thread is not signalling before the
signal function was configured, for example by starting the background
thread only within C<_attach>.

That concludes the Perl part.

The XS part consists of the actual constructor which creates a thread,
which is not relevant for this example, and two functions, C<_c_func>,
which returns the Perl-side callback, and C<_attach>, which configures
the signalling functioon that is safe toc all from another thread. For
simplicity, we will use global variables to store the functions, normally
you would somehow attach them to C<$self>.

The C<c_func> simply returns the address of a static function and arranges
for the object pointed to by C<$self> to be passed to it, as an integer:

   void
   _c_func (SV *loop)
           PPCODE:
           EXTEND (SP, 2);
           PUSHs (sv_2mortal (newSViv (PTR2IV (c_func))));
           PUSHs (sv_2mortal (newSViv (SvRV (loop))));

This would be the callback (since it runs in a normal Perl context, it is
permissible to manipulate Perl values):

   static void
   c_func (pTHX_ void *loop_, int value)
   {
     SV *loop_object = (SV *)loop_;
     ...
   }

And this attaches the signalling callback:

   static void (*my_sig_func) (void *signal_arg, int value);
   static void *my_sig_arg;

   void
   _attach (SV *loop_, IV sig_func, void *sig_arg)
           CODE:
   {
           my_sig_func = sig_func;
           my_sig_arg  = sig_arg;

           /* now run the thread */
           thread_create (&u->tid, l_run, 0);
   }

And C<l_run> (the background thread) would eventually call the signaling
function:

   my_sig_func (my_sig_arg, 0);

You can have a look at L<EV::Loop::Async> for an actual example using
intra-thread communication, locking and so on.


=head1 THE Async::Interrupt CLASS

=over 4

=cut

package Async::Interrupt;

use common::sense;

BEGIN {
   # the next line forces initialisation of internal
   # signal handling variables, otherwise, PL_sig_pending
   # etc. might be null pointers.
   $SIG{KILL} = sub { };

   our $VERSION = 1.26;

   require XSLoader;
   XSLoader::load ("Async::Interrupt", $VERSION);
}



( run in 0.842 second using v1.01-cache-2.11-cpan-71847e10f99 )