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

Greetings, O Knowledgeable Monks!

I've written a simple mail-checking daemon, mostly based on the Perl Cookbook recipie 17.15 and the rest of chapter 17. It works passably well, but I'm concerned that using sleep in the while ( $bAlive ) loop in main is inefficient.

Is there a better way to make a daemon periodically check the status of /var/mail/$username that this method?

Thanks in advance!

#!/usr/bin/perl use warnings; use strict; use POSIX; use Proc::ProcessTable; use Tk; our $top; our $bAlive = 1; our $sleep = 5; our $mail_size = 0; $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signal_handler; sub signal_handler { $bAlive = 0; } main (); sub main { setup (); while ( $bAlive ) { popup_and_fork () if not is_mail_size_zero (); sleep $sleep; } } sub setup { # Die if another copy is already running: # my $proc_table = new Proc::ProcessTable; my $nMailMen = grep { /$0/ } map { $_->{cmndline} } @{$proc_table- +>table}; die "$0 already running" if $nMailMen > 1; my $pid = fork (); exit if $pid; die "couldn't fork: $!" if not defined $pid; POSIX::setsid () or die "can't start new session: $!"; $mail_size = get_mail_size (); } sub get_mailfile { chomp ( my $username = `whoami` ); return "/var/mail/$username"; } sub get_mail_size { return -s get_mailfile (); } sub is_mail_size_nonzero { return 0 != get_mail_size (); } sub popup_and_fork { if ( my $pid = fork () ) { # parent waitpid ( $pid, 0 ); } elsif ( defined $pid ) { # child my $mail_size = get_mail_size (); popup_box (); exit; } else { die "fork failure: $!"; } } sub popup_box { $top = new MainWindow; $top->title ( 'Mailman' ); $top->Button ( -text => "You've got mail\n" . get_summary (), -command => \&close_popup_box, )->pack (); $top->repeat ( $sleep * 1000, \&dismiss_if_needed ); MainLoop; } sub get_summary { open ( my $fh, get_mailfile () ) or die "can't open mailfile"; my @froms = grep { /^From:/ } <$fh>; close $fh; return join "", @froms; } sub close_popup_box { $top->destroy; } sub dismiss_if_needed { close_popup_box () if is_mail_size_zero (); }

Replies are listed 'Best First'.
Re: Efficient Daemon Looping?
by halley (Prior) on Mar 19, 2004 at 15:32 UTC
    Unless some external event can signal your process directly, sleep() is about as efficient as it gets. You should tune your sleep durations to be as long as acceptable, but really, the CPU won't waste the effort to give your process a second thought until the sleeping time elapses. (Sleep is NOT equivalent to 1 while (time < wakeup);)

    --
    [ e d @ h a l l e y . c c ]

      You make a very good point. It is a common misconception that sleep is unduly expensive.

      This sounds like a good candidate for a FAQ, even though it's not A'd very F.

      Great, thanks much!
Re: Efficient Daemon Looping?
by ambrus (Abbot) on Mar 19, 2004 at 16:47 UTC

    The sleep solution is quite good.

    Bash has builtin mail notification (customizable with environment variables), it works by checking the mail file before issuing a prompt if a certain time (60 sec) has elapsed since it last checked.

    If, however, you really want a solution without sleep, see Documentation/dnotify.txt in the linux source. That describes a linux-specific way to watch for a changed file. I have not ever tried those fcntls ever, so I don't know the details.

    Update 2006 jul 14: a similar way to dnotify is leases, which is also a couple of linux-specific fcntls, but allows to watch a file for when it's opened, not when it's written to. They are documented in the fcntl man page.