How can I ensure that a function never gets executed concurrently in perl?

Viewed 76

I use Mojo::IOLoop to talk to a port to send commands to a digital receiver.

Because the receiver can only open the port once, but the code talking to it sits behind a web interface and a web server, there is a risk that commands get sent concurrently - so the digital receiver loses them, and things go wrong.

So - how can I ensure that some piece of code never gets run concurrently?

I was trying to use file locks like this:

sub enqueue {
    my $self = shift;
    my ($cmd, $promise) = @_;

    while (!(flock DATA, LOCK_EX|LOCK_NB)) { sleep(0.25) }

    my (@r);

    my $id = Mojo::IOLoop->client({ address => '192.168.1.204', 
                                    port => 23, timeout => 13 }
              => sub ($loop, $err, $stream) {
                  return $promise->reject('could not open stream') unless $stream;

                  $stream->on(error => sub ($stream, $err) {
                                  flock(DATA, LOCK_UN);
                                  $stream->close;
                                  $promise->reject($err);
                              });

                  $stream->on(read => sub ($stream, $bytes) {
                                  $bytes =~ s/\r\n$//;
                                  push @r, split /\r\n/, $bytes;
                                  # stream done is a function that checks that 
                                  # the appropriate number of lines got sent back
                                  if (stream_done(@r)) {
                                      flock(DATA, LOCK_UN);
                                      $promise->resolve(\@r);
                                      $stream->close;
                                  };
                              });

                  $stream->on(timeout => sub ($stream) {
                                  flock(DATA, LOCK_UN);
                                  $stream->close;
                                  $promise->reject('timeout') if ($err);
                              });
                  $stream->write($cmd);
              });
}
1 Answers

I'm not sure if I understand correctly what you want to do as I am not familiar with Mojo. But if you just don't want to execute a function two times create a lock file and delete if when it is finished:

use File::Slurp;
sub my_method_to_execute_only_once {
    my $tmp = 'temp.txt';
    return if (is_method_running($tmp));
    # DO YOUR STUFF
    unlink $tmp;
}
sub is_method_running{
    
    my ($filename) = @_;
    
    if (-e $tmp){
        return 1; 
    }
    
    write_file($tmp, "running");
    return 0;
}
Related