Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

AnyEvents - mainly

by anita2R (Scribe)
on Apr 25, 2020 at 22:51 UTC ( #11116068=perlquestion: print w/replies, xml ) Need Help??

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

Hello Monks,

I have written a short script as a pretentiously named 'proof of concept' for a project I am working on.

The project's aims are to create a daemon that accepts data and commands through a tcp socket, processes/prioritizes them and drives the data and formatting to an LCD screen over an i2c connection. In addition the daemon will respond to signals such as HUP to cause a re-initialization of the lcd device and USR1 to load custom characters.

The test script shown here does not include any of the lcd communication or command processing as I have this working already and it is not relevant to the test script.

As I have never written a Perl script to create a daemon, or use sockets or that is event driven, I would appreciate comments on my approach and programming details

The test script, so far works as I want ... The program is daemonized, the pid is stored in a file and error output is directed to an error file. The script responds to HUP and USR1 signals and data sent from another machine on the network is received and processed by the io socket.

For the test, the outputs are sent to a log file.

The three events: i/o, HUP & USR1 are handled in anonymous subroutines, but the data received by the i/o socket is then passed to a main loop for further processing using a condition variable with ->send.

The ->recv is in the main loop, and according to the documentation for AnyEvent, this blocks the loop from running until a ->send occurs. Can I just keep the loop running so that I can do other things?.

I am not sure if the way I have structured this is appropriate and of course my 'y' solution to the 'x' problem may not be the best way to solve my issue.

The test script responds to the following series of external commands:

TestEvent.pl sudo kill -USR1 $(cat events.pid) SktClnt.pl Enter data to send: A.B.C sudo kill -HUP $(cat events.pid)

... with this in the events.log file

25-04-2020 18:03:46, Port: 55129 looping 0 25-04-2020 18:04:02, User 1 signal received 25-04-2020 18:04:11, Connection received from 192.168.0.153 Data: A.B.C loop data: A.B.C 25-04-2020 18:04:20, Hangup

Here is the script

#!/usr/bin/perl # EventsTest.pl # Version 1.0, 25 April 2020 use Sys::HostIP; use Daemon::Daemonize qw( :all ); use IO::Socket::INET; use Event; use AnyEvent; use Date::Calc qw( Today_and_Now ); use strict; use warnings; $|++; # ********************************************************** # # ************** Variables to be set by user *************** # # ********************************************************** # base path/name for log files (e.g. /home/anita2r/events.log) my $base = "/home/anita2R/events"; # port for socket connection my $port = 55129; # ********************************************************** # # ****************** Create log files etc. ***************** # # ********************************************************** # # Setup log, error and pid files my $error_log = "$base.err"; my $pid_file = "$base.pid"; my $msg_log = "$base.log"; # clear error and log files truncate $error_log, 0; truncate $msg_log, 0; # ********************************************************** # # ************* Create a communications socket ************* # # ********************************************************** # # create a socket and log port number my $hostip = Sys::HostIP->new; my $ip = $hostip->ip; # save port to log file my $DtTm = DtTmStmp(); open( my $fh, '>>', $msg_log ); print $fh "$DtTm, Port: $port\n"; close $fh; # setup socket my $skt = IO::Socket::INET->new( Listen => 1, LocalAddr => $ip, LocalPort => $port, Proto => 'tcp', Reuse => 1, ) or die "$! ... Can't open socket on port $port\n"; # ********************************************************** # # ******************** Signal handlers ********************* # # ********************************************************** # # ********************************************************** # # HUP signal handler # for this test HUP will log and close the script my $wtforhup = AnyEvent->signal( signal => "HUP", cb => sub { my $DtTm = DtTmStmp(); open( my $fh, '>>', $msg_log ); print $fh "$DtTm, Hangup\n"; close $fh; # clear the pid file and stop the daemon delete_pidfile( $pid_file ); exit 1; } ); # ********************************************************** # # USR1 signal handler # for this test USR1 will just add an entry to the log my $wtforusr1 = AnyEvent->signal( signal => "USR1", cb => sub{ my $DtTm = DtTmStmp(); open( my $fh, '>>', $msg_log ); print $fh "$DtTm, User 1 signal received\n"; close $fh; } ); # ********************************************************** # # IO socket handler # read data, log receive # and pass it on for additional processing my $data; my $iocv = AnyEvent->condvar; my $wtforio = AnyEvent->io( fh => \*$skt, poll => 'r', cb => sub{ my $lskt = $skt->accept(); while( <$lskt> ) { $data = $_; chomp $data; my $hostip = $lskt->peerhost(); # log received data my $DtTm = DtTmStmp(); open( my $fh, '>>', $msg_log ); print $fh "$DtTm, Connection received from $hostip\n"; print $fh "Data: $data\n"; close $fh; } close( $lskt ); # send data for further processing # $iocv->recv is in main loop $iocv->send( $data ); } ); # ********************************************************** # # *********************** Daemonize ************************ # # ********************************************************** # # daemonize, redirect errors & save new pid daemonize( close => 'std', stderr => $error_log, ); write_pidfile( $pid_file ); # ********************************************************** # # ********************* Initialization ********************* # # ********************************************************** # # In the real program, various initialization routines # are carried out before entering the main program loop # ********************************************************** # # ******************* Main Program Loop ******************** # # ********************************************************** # my $loop = 1; my $a = 0; while( $loop ) { # just to show whether the loop is running # one entry every 5 seconds if( $a % 5 == 0) { open( $fh, '>>', $msg_log ); print $fh "looping $a\n"; close $fh; } $a++; sleep 1; # $iocv-recv blocks until an $iorecv->send my $data = $iocv->recv; open( $fh, '>>', $msg_log ); print $fh "loop data: $data\n"; close $fh; # done with this cv undef $iocv; # setup a fresh cv - loop will be blocked until next send $iocv = AnyEvent->condvar; } exit 0; # ********************************************************** # # ****************** General subroutines ******************* # # ********************************************************** # # ********************************************************** # # date/time stamp sub DtTmStmp { my( $yr, $mo, $dy, $hr, $mi, $se ) = Today_and_Now(); my $DtTm = sprintf( "%02d-%02d-%04d %02d:%02d:%02d", $dy, $mo, $yr, $hr, $mi, $se ); return $DtTm; } # ********************************************************** #

Regards

anita2R

Replies are listed 'Best First'.
Re: AnyEvents - mainly
by Corion (Pope) on Apr 26, 2020 at 06:17 UTC

    You don't even need your own mainloop, as AnyEvent brings its own main loop with it.

    I would rewrite your main loop as follows:

    my $loop = AnyEvent->condvar; my $a = 0; sub heartbeat { open( $fh, '>>', $msg_log ); print $fh "looping $a\n"; close $fh; }; AnyEvent->timer( interval => 5, cb => \&heartbeat ); $loop->recv; exit 0;

    I would move reacting to IO out of the main loop as well:

    $iocv->cb( \&on_io ); sub on_io { open( $fh, '>>', $msg_log ); print $fh "loop data: $data\n"; close $fh; undef( $iocv ); $iocv = AnyEvent->condvar; $iocv->cb( \&on_io ); };

    Ideally, you don't have your own main loop and only have callbacks and let AnyEvent decide, which callback to run, depending on what kind of event has arrived.

      Corion,

      Thanks for that. I didn't appreciate that AnyEvents provides its own main loop.

      I will rewrite it as suggested

      anita2R

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11116068]
Approved by johngg
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (7)
As of 2020-11-23 16:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?