milanpwc has asked for the wisdom of the Perl Monks concerning the following question:

At certain stage of my Perl script, I want to burn the processed data onto a DVD. I have written a 'xorriso' wrapper to do the burning. While the disc is being burned, I want my script to keep processing new incoming requests and being informed of the burning status. What is the simplest way to do this? Fork? Multithreading? Do I need to use named pipe to receive the status in burning?

Replies are listed 'Best First'.
Re: Disc burning
by bliako (Abbot) on Jun 05, 2019 at 13:13 UTC

    If you rely entirely on the command's STDOUT to get its status then Capture::Tiny can give you a filehandle to xorisso's STDOUT which you can read while xorisso runs on the background (see xdg's answer here: https://stackoverflow.com/questions/634508/how-can-i-send-perl-output-to-a-both-stdout-and-a-variable). Expanded xdg's solution (assumes a *sh shell, *may not work in windows*):

    use Capture::Tiny 'tee'; my $output = tee { system('t=1;while [ 1 -eq 1 ]; do echo hello $t; if + [ $t -eq 10 ]; then exit 0; fi; t=$((t+1)); sleep 1; done') }; print $_ while( <$output> );

    Now, that's a first step. But think big:

    The following script I whipped up creates a wrapper for 1) spawning xorisso, 2) non-blocking select its STDOUT, 3) setup a listening socket so that clients can connect and enquire about xorisso's output, 4) non-blocking select the socket, 5) check if command has finished.

    use strict; use warnings; # Script by bliako for https://perlmonks.org/?node_id=11100979 # spawns a system command and while running it sends its output # to a client connecting via a socket. # when the command terminates, the program waits for a client # to send last pending output and exits too. # 05/06/2019 use IO::Socket::INET; use IO::Select; use Scalar::Util qw/openhandle/; use POSIX ":sys_wait_h"; my $EXEC = undef; my $keep_on = 1; $SIG{CHLD} = sub { waitpid(-1,WNOHANG); $keep_on = 0; }; my $port = 20000; my $cmd = 't=1;while [ 1 -eq 1 ]; do echo hello $t; if [ $t -eq 10 ]; +then exit 0; fi; t=$((t+1)); sleep 1; done'; my $execpid = open($EXEC, '-|', $cmd) or die "failed to spawn command +$cmd : $!"; die "spawning system command, $!" unless defined $execpid; print "PID is $execpid\n"; # Listen to port my $sock = IO::Socket::INET->new(LocalPort => 20000, Type => SOCK_STREAM, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1, Timeout => 1, Blocking => 0 ) or die "Can't create socket, $@"; my @cmdoutput; my $aselect = IO::Select->new(); $aselect->add($sock); $aselect->add($EXEC); #local $/ = undef; WHITER: while(){ my @clients = $aselect->can_read(1); foreach my $aclient (@clients){ if( $aclient == $EXEC && $keep_on){ print "$0 : select the EXEC ...\n"; if( openhandle($EXEC) ){ # checking print "spawn command lives\n"; push @cmdoutput, <$EXEC>; } else { print "spawn command ended\n"; push @cmdoutput, '<close>'; $keep_on = 0; } } elsif( $aclient == $sock ){ print "$0 : select a client via socket ...\n"; if( ! $aclient->connected() ){ $aselect->remove($aclient) +} $aclient = $sock->accept(); $aselect->add($aclient); # send them a message, close connection $aclient->print(join("",@cmdoutput)); print "$0 : got a request and send the cmd output so far.\ +n"; @cmdoutput = (); if( ! $keep_on ){ # command has ended but wait for a client to send all +pending output $aclient->print("<closed>\n"); print "$0 : terminating after send client pending outp +ut.\n"; $aclient->close(); $aselect->remove($aclient); last WHITER } $aclient->close(); $aselect->remove($aclient); } } } print "$0 : done.\n";
    # example use: perl a.pl & telnet localhost 20000

    bw, bliako

Re: Disc burning
by choroba (Cardinal) on Jun 05, 2019 at 08:08 UTC
    Fork seems OK. You can waitpid for the process at the end of the script to check the status.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      I want the forked process to return the status or some other messages during burning. How do I make this possible?
        How do I make this possible?

        Example:

        #!/usr/bin/perl use strict; use warnings; use IO::Select; my ($rh,$wh); pipe($rh,$wh); $wh->autoflush; if ((my $pid = fork) == 0) { # child here close $rh; sleep 2; print $wh "child here\n"; sleep 4; print $wh "child again\n"; close $wh; exit; } else { # parent here close $wh; my $s = IO::Select->new(); $s->add($rh); $SIG{ALRM} = sub { if ($s->can_read(0) and ! eof($rh) ) { my $line = <$rh>; print "child sent: $line"; } alarm 1; }; alarm 1; for(;;) { # body of your parent code print "waiting...\n"; sleep; print "again "; last if eof $rh; } print "child is done\n"; }

        For something more elaborate, check out an event module, e.g. EV and use EV::io with $rh. If you already use some library using an event loop (e.g. Tk), use that.

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Disc burning
by haukex (Archbishop) on Jun 05, 2019 at 08:31 UTC
    I want my script to keep processing new incoming requests and being informed of the burning status.

    What does this mean exactly - what is your script doing in the meantime? Is it a GUI, some kind of server, some kind of event loop, ...? What purpose would informing the script of the progress serve (e.g. information for the user, or something else)? The reason I ask is that some event loops provide mechanisms to run external processes in parallel.

      The Perl script implements a server event loop. In case it receives a "burn" command from the client, it have to call an external program called 'xorriso' to burn the processed data onto a disc. While the burning takes quite some time, I want the server loop to keep processing other client requests in the meantime. I have written my own event loop already and I am not going to replace it with some other public domain event loop modules.

        In that case you might be interested in IPC::Run's start/pump/finish interface (or in your case pump_nb), which will let you run a separate process and communicate with it while it is running.

Re: Disc burning
by trippledubs (Deacon) on Jun 07, 2019 at 19:40 UTC

    fork and flock and a lock that blocks. The child pid (fork) locks a file using flock, once that lock is achieved (correctly), you can use the CD-ROM. Inside that locked region of code, you can log to a file to record the status. You could also wait on the child to get the exit status.